blob: 9ad9edf4aaf9b08fba2f54a1a514899b029ef1af [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Protocol buffers for user queries parsed into abstract syntax trees.
6
7A user issue query can look like [Type=Defect owner:jrobbins "memory leak"].
8In that simple form, all the individual search conditions are simply ANDed
9together. In the code, a list of conditions to be ANDed is called a
10conjunction.
11
12Monorail also supports a quick-or feature: [Type=Defect,Enhancement]. That
13will match any issue that has labels Type-Defect or Type-Enhancement, or both.
14
15Monorail supports a top-level "OR" keyword that can
16be used to logically OR a series of conjunctions. For example:
17[Type=Defect stars>10 OR Type=Enhancement stars>50].
18
19Parentheses groups and "OR" statements are preprocessed before the final
20QueryAST is constructed.
21
22So, QueryAST is always exactly two levels: the overall tree
23consists of a list of conjunctions, and each conjunction consists of a list
24of conditions.
25
26A condition can look like [stars>10] or [summary:memory] or
27[Type=Defect,Enhancement]. Each condition has a single comparison operator.
28Most conditions refer to a single field definition, but in the case of
29cross-project search a single condition can have a list of field definitions
30from the different projects being searched. Each condition can have a list
31of constant values to compare against. The values may be all strings or all
32integers.
33
34Some conditions are procesed by the SQL database and others by the GAE
35search API. All conditions are passed to each module and it is up to
36the module to decide which conditions to handle and which to ignore.
37"""
38
39from __future__ import print_function
40from __future__ import division
41from __future__ import absolute_import
42
43from protorpc import messages
44
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010045from mrproto import tracker_pb2
Copybara854996b2021-09-07 19:36:02 +000046
47
48# This is a special field_name for a FieldDef that means to do a fulltext
49# search for words that occur in any part of the issue.
50ANY_FIELD = 'any_field'
51
52
53class QueryOp(messages.Enum):
54 """Enumeration of possible query condition operators."""
55 EQ = 1
56 NE = 2
57 LT = 3
58 GT = 4
59 LE = 5
60 GE = 6
61 TEXT_HAS = 7
62 NOT_TEXT_HAS = 8
63 IS_DEFINED = 11
64 IS_NOT_DEFINED = 12
65 KEY_HAS = 13
66
67
68class TokenType(messages.Enum):
69 """Enumeration of query tokens used for parentheses parsing."""
70 SUBQUERY = 1
71 LEFT_PAREN = 2
72 RIGHT_PAREN = 3
73 OR = 4
74
75
76class QueryToken(messages.Message):
77 """Data structure to represent a single token for parentheses parsing."""
78 token_type = messages.EnumField(TokenType, 1, required=True)
79 value = messages.StringField(2)
80
81
82class Condition(messages.Message):
83 """Representation of one query condition. E.g., [Type=Defect,Task]."""
84 op = messages.EnumField(QueryOp, 1, required=True)
85 field_defs = messages.MessageField(tracker_pb2.FieldDef, 2, repeated=True)
86 str_values = messages.StringField(3, repeated=True)
87 int_values = messages.IntegerField(4, repeated=True)
88 # The suffix of a search field
89 # eg. the 'approver' in 'UXReview-approver:user@mail.com'
90 key_suffix = messages.StringField(5)
91 # The name of the phase this field value should belong to.
92 phase_name = messages.StringField(6)
93
94
95class Conjunction(messages.Message):
96 """A list of conditions that are implicitly ANDed together."""
97 conds = messages.MessageField(Condition, 1, repeated=True)
98
99
100class QueryAST(messages.Message):
101 """Abstract syntax tree for the user's query."""
102 conjunctions = messages.MessageField(Conjunction, 1, repeated=True)
103
104
105def MakeCond(op, field_defs, str_values, int_values,
106 key_suffix=None, phase_name=None):
107 """Shorthand function to construct a Condition PB."""
108 return Condition(
109 op=op, field_defs=field_defs, str_values=str_values,
110 int_values=int_values, key_suffix=key_suffix, phase_name=phase_name)