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