Skip to content

Commit b4f00aa

Browse files
committed
Enhanced 'search' operator to allow complex criteria matching on payload items
1 parent 0e27ccc commit b4f00aa

File tree

3 files changed

+30
-525
lines changed

3 files changed

+30
-525
lines changed

st2common/st2common/operators.py

+18-105
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ class UnrecognizedConditionError(Exception):
5252

5353
def search(value, criteria_pattern, criteria_condition, check_function):
5454
"""
55-
Search a list of values that match all child criteria. If condition is 'any', return a
56-
successful match if any items match all child criteria. If condition is 'all', return a
57-
successful match if ALL items match all child criteria.
55+
Search a list of values that match all child criteria.
56+
Allow comparison of payload items to multiple criteria using different logicial conditions.
57+
If condition is 'any', return a successful match if any items match all child criteria.
58+
If condition is 'all', return a successful match if ALL items match all child criteria.
59+
Other allowed conditions: all2all, all2any, any2any and any2all.
5860
5961
value: the payload list to search
6062
condition: one of:
61-
* any - return true if any items of the list match and false if none of them match
62-
* all - return true if all items of the list match and false if any of them do not match
63+
* all2all - true if all payload items match all criteria items
64+
* all2any - true if all payload items match any criteria items
65+
* any2any - true if any payload items match any criteria items
66+
* any2all - true if any payload items match all criteria items
67+
* any - same as any2all (useful to maintain backward compatibility)
68+
* all - same as all2all (useful to maintain backward compatibility)
6369
pattern: a dictionary of criteria to apply to each item of the list
6470
6571
This operator has O(n) algorithmic complexity in terms of number of child patterns.
@@ -86,14 +92,14 @@ def search(value, criteria_pattern, criteria_condition, check_function):
8692
]
8793
}
8894
89-
And an example usage in criteria:
95+
Example #1
9096
9197
---
9298
criteria:
9399
trigger.fields:
94100
type: search
95101
# Controls whether this criteria has to match any or all items of the list
96-
condition: any # or all
102+
condition: all
97103
pattern:
98104
# Here our context is each item of the list
99105
# All of these patterns have to match the item for the item to match
@@ -105,105 +111,14 @@ def search(value, criteria_pattern, criteria_condition, check_function):
105111
item.to_value:
106112
type: "equals"
107113
pattern: "Approved"
108-
"""
109-
if criteria_condition == "any":
110-
# Any item of the list can match all patterns
111-
rtn = any(
112-
[
113-
# Any payload item can match
114-
all(
115-
[
116-
# Match all patterns
117-
check_function(
118-
child_criterion_k,
119-
child_criterion_v,
120-
PayloadLookup(
121-
child_payload, prefix=TRIGGER_ITEM_PAYLOAD_PREFIX
122-
),
123-
)
124-
for child_criterion_k, child_criterion_v in six.iteritems(
125-
criteria_pattern
126-
)
127-
]
128-
)
129-
for child_payload in value
130-
]
131-
)
132-
elif criteria_condition == "all":
133-
# Every item of the list must match all patterns
134-
rtn = all(
135-
[
136-
# All payload items must match
137-
all(
138-
[
139-
# Match all patterns
140-
check_function(
141-
child_criterion_k,
142-
child_criterion_v,
143-
PayloadLookup(
144-
child_payload, prefix=TRIGGER_ITEM_PAYLOAD_PREFIX
145-
),
146-
)
147-
for child_criterion_k, child_criterion_v in six.iteritems(
148-
criteria_pattern
149-
)
150-
]
151-
)
152-
for child_payload in value
153-
]
154-
)
155-
else:
156-
raise UnrecognizedConditionError(
157-
"The '%s' search condition is not recognized, only 'any' "
158-
"and 'all' are allowed" % criteria_condition
159-
)
160-
161-
return rtn
162-
163-
164-
def multiple(value, criteria_pattern, criteria_condition, check_function):
165-
"""
166-
Allow comparison of payload items to multiple criteria using different logicial conditions.
167-
Performs same function as the "search" operator and contains additional features.
168-
169-
value: the payload items
170-
condition: one of:
171-
* all2all - true if all payload items match all criteria items
172-
* all2any - true if all payload items match any criteria items
173-
* any2any - true if any payload items match any criteria items
174-
* any2all - true if any payload items match all criteria items
175-
* all - same as all2all (useful to maintain backward compatibility with search operator)
176-
* any - same as any2all (useful to maintain backward compatibility with search operator)
177-
pattern: a dictionary of criteria to apply to each item of the list
178-
179-
This operator has O(n) algorithmic complexity in terms of number of child patterns.
180-
This operator has O(n) algorithmic complexity in terms of number of payload fields.
181-
182-
However, it has O(n_patterns * n_payloads) algorithmic complexity, where:
183-
n_patterns = number of child patterns
184-
n_payloads = number of fields in payload
185-
It is therefore very easy to write a slow rule when using this operator.
186-
187-
This operator should ONLY be used when trying to match a small number of child patterns and/or
188-
a small number of payload list elements.
189-
190-
Data from the trigger:
191-
192-
{
193-
"fields": [
194-
{
195-
"field_name": "waterLevel",
196-
"to_value": 45,
197-
}
198-
]
199-
}
200-
201-
And an example usage in criteria:
114+
---
202115
116+
Example #2
117+
203118
---
204119
criteria:
205120
trigger.fields:
206-
type: multiple
121+
type: search
207122
# Controls whether this criteria has to match any or all items of the list
208123
condition: all2all # all2any, any2all or any2any
209124
pattern:
@@ -224,7 +139,7 @@ def multiple(value, criteria_pattern, criteria_condition, check_function):
224139
(len(criteria_condition_list) == 2 and (criteria_condition_list[0] == 'any' or criteria_condition_list[0] == 'all') and
225140
(criteria_condition_list[1] == 'any' or criteria_condition_list[1] == 'all')))):
226141
raise UnrecognizedConditionError(
227-
"The '%s' condition is not recognized for type multiple, 'any', 'all', 'any2any', 'any2all', 'all2any'"
142+
"The '%s' condition is not recognized for type search, 'any', 'all', 'any2any', 'any2all', 'all2any'"
228143
" and 'all2all are allowed" % criteria_condition
229144
)
230145
payloadItemMatch = any
@@ -509,7 +424,6 @@ def ensure_operators_are_strings(value, criteria_pattern):
509424
NINSIDE_LONG = "ninside"
510425
NINSIDE_SHORT = "nin"
511426
SEARCH = "search"
512-
MULTIPLE = "multiple"
513427

514428
# operator lookups
515429
operators = {
@@ -546,5 +460,4 @@ def ensure_operators_are_strings(value, criteria_pattern):
546460
NINSIDE_LONG: ninside,
547461
NINSIDE_SHORT: ninside,
548462
SEARCH: search,
549-
MULTIPLE: multiple
550463
}

0 commit comments

Comments
 (0)