Skip to content

Commit 7f60295

Browse files
authored
Merge pull request #473 from minos-framework/issue-471-add-contains
#471 - Add `Condition.CONTAINS`
2 parents 5a1da10 + 4e00936 commit 7f60295

File tree

6 files changed

+128
-64
lines changed

6 files changed

+128
-64
lines changed

packages/core/minos-microservice-aggregate/minos/aggregate/queries.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ def _evaluate(self, value: Model) -> bool:
146146
return self._get_field(value) in self.parameter
147147

148148

149+
class _ContainsCondition(_SimpleCondition):
150+
def _evaluate(self, value: Model) -> bool:
151+
return self.parameter in self._get_field(value)
152+
153+
149154
class _LikeCondition(_SimpleCondition):
150155
def _evaluate(self, value: Model) -> bool:
151156
return bool(self._pattern.fullmatch(self._get_field(value)))
@@ -205,6 +210,7 @@ class Condition:
205210
EQUAL = _EqualCondition
206211
NOT_EQUAL = _NotEqualCondition
207212
IN = _InCondition
213+
CONTAINS = _ContainsCondition
208214
LIKE = _LikeCondition
209215

210216

packages/core/minos-microservice-aggregate/tests/test_aggregate/test_queries.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class _Text(DeclarativeModel):
2121
value: str
2222

2323

24+
class _ListInt(DeclarativeModel):
25+
value: list[int]
26+
27+
2428
class TestCondition(unittest.TestCase):
2529
def test_hash(self):
2630
self.assertIsInstance(hash(Condition.EQUAL("value", 3)), int)
@@ -126,6 +130,13 @@ def test_condition_in(self):
126130
self.assertFalse(condition.evaluate(_Number(42)))
127131
self.assertTrue(condition.evaluate(_Number(56)))
128132

133+
def test_condition_contains(self):
134+
condition = Condition.CONTAINS("value", 1)
135+
self.assertEqual("_ContainsCondition('value', 1)", repr(condition))
136+
137+
self.assertFalse(condition.evaluate(_ListInt([42, 3, -5])))
138+
self.assertTrue(condition.evaluate(_ListInt([1, 2, 3])))
139+
129140
def test_condition_like(self):
130141
condition = Condition.LIKE("value", "a%[^ou]")
131142
self.assertEqual("_LikeCondition('value', 'a%[^ou]')", repr(condition))

packages/plugins/minos-database-aiopg/minos/plugins/aiopg/factories/aggregate/snapshots/queries.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
_AndCondition,
3232
_ComposedCondition,
3333
_Condition,
34+
_ContainsCondition,
3435
_EqualCondition,
3536
_FalseCondition,
3637
_GreaterCondition,
@@ -144,6 +145,8 @@ def _build_condition(self, condition: _Condition) -> Composable:
144145
return SQL("FALSE")
145146
if isinstance(condition, _LikeCondition):
146147
return self._build_condition_like(condition)
148+
if isinstance(condition, _ContainsCondition):
149+
return self._build_condition_contains(condition)
147150
if isinstance(condition, _SimpleCondition):
148151
return self._build_condition_simple(condition)
149152

@@ -184,7 +187,7 @@ def _build_condition_simple(self, condition: _SimpleCondition) -> Composable:
184187
name = Placeholder(name)
185188
return SQL("(data#>{field} {operator} {name}::jsonb)").format(field=field, operator=operator, name=name)
186189

187-
def _build_condition_like(self, condition: _SimpleCondition) -> Composable:
190+
def _build_condition_like(self, condition: _LikeCondition) -> Composable:
188191
field = condition.field
189192

190193
parameter = AvroDataEncoder(condition.parameter).build()
@@ -204,6 +207,21 @@ def _build_condition_like(self, condition: _SimpleCondition) -> Composable:
204207
name = Placeholder(name)
205208
return SQL("(data#>>{field} LIKE {name})").format(field=field, name=name)
206209

210+
def _build_condition_contains(self, condition: _ContainsCondition) -> Composable:
211+
field = condition.field
212+
213+
parameter = AvroDataEncoder(condition.parameter).build()
214+
215+
if field in self._FIXED_FIELDS_MAPPER:
216+
raise ValueError(f"Cannot use 'contains' over non-list field '{field}'")
217+
else:
218+
name = self.generate_random_str()
219+
self._parameters[name] = parameter
220+
221+
field = Literal("{{{}}}".format(field.replace(".", ",")))
222+
name = Placeholder(name)
223+
return SQL("(data#>{name} IN {field}::jsonb)").format(field=field, name=name)
224+
207225
def _build_ordering(self, ordering: _Ordering) -> Composable:
208226
field = ordering.by
209227
direction = self._ORDERING_MAPPER[ordering.reverse]

0 commit comments

Comments
 (0)