Skip to content

Commit

Permalink
Another MongoDB workaround as "$size: 1" matches all documents (closes
Browse files Browse the repository at this point in the history
  • Loading branch information
ml-evs committed May 27, 2021
1 parent 9584fbf commit 8050405
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 30 deletions.
57 changes: 33 additions & 24 deletions optimade/filtertransformers/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def postprocess(self, query: Dict[str, Any]):
query = self._apply_unknown_or_null_filter(query)
query = self._apply_mongo_id_filter(query)
query = self._apply_mongo_date_filter(query)

return query

def value_list(self, arg):
Expand Down Expand Up @@ -95,28 +94,33 @@ def property_first_comparison(self, quantity, query):
# e.g. `("elements", {"$size": 2, "$all": ["Ag", "Au"]})` should become
# `{"elements": {"$all": ["Ag", "Au"]}, "nelements": 2}` if the `elements` -> `nelements`
# length alias is defined.
if (
"$size" in query
and getattr(self.backend_mapping.get(quantity), "length_quantity", None)
is not None
):

size_query = {
self.backend_mapping[quantity].length_quantity.backend_field: query.pop(
"$size"
)
}

final_query = {}
if query:
final_query = {quantity: query}
for q in size_query:
if q in query:
query[q].update(size_query[q])
else:
final_query[q] = size_query[q]

return final_query
if "$size" in query:
if (
getattr(self.backend_mapping.get(quantity), "length_quantity", None)
is not None
):
size_query = {
self.backend_mapping[
quantity
].length_quantity.backend_field: query.pop("$size")
}

final_query = {}
if query:
final_query = {quantity: query}
for q in size_query:
if q in query:
query[q].update(size_query[q])
else:
final_query[q] = size_query[q]

return final_query

# Another workaround: in the case that there is no length alias, and the field
# itself does not exist in the database, then `{"$size": 1}` matches all documents,
# so we must add another existence check
if "$exists" not in query:
query["$exists"] = True

return {quantity: query}

Expand Down Expand Up @@ -308,7 +312,12 @@ def replace_with_relationship(subdict, prop, expr):
subdict["$and"] = []
subdict["$and"].extend(
[
{f"relationships.{_prop}.data": {"$size": expr.pop("$size")}},
{
f"relationships.{_prop}.data": {
"$size": expr.pop("$size"),
"$exists": expr.pop("$exists"),
}
},
{f"relationships.{_prop}.data.{_field}": expr},
]
)
Expand Down
25 changes: 19 additions & 6 deletions tests/filtertransformers/test_mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def test_filtering_on_relationships(self, mapper):

assert t.transform(p.parse('structures.id HAS ONLY "dummy/2019"')) == {
"$and": [
{"relationships.structures.data": {"$size": 1}},
{"relationships.structures.data": {"$size": 1, "$exists": True}},
{"relationships.structures.data.id": {"$all": ["dummy/2019"]}},
]
}
Expand All @@ -304,7 +304,12 @@ def test_filtering_on_relationships(self, mapper):
"$and": [
{
"$and": [
{"relationships.structures.data": {"$size": 1}},
{
"relationships.structures.data": {
"$size": 1,
"$exists": True,
}
},
{"relationships.structures.data.id": {"$all": ["dummy/2019"]}},
]
},
Expand All @@ -328,7 +333,9 @@ def test_not_implemented(self):
with pytest.raises(VisitError, match="not implemented"):
self.transform("list HAS ANY > 3, < 6")

assert self.transform("list LENGTH 3") == {"list": {"$size": 3}}
assert self.transform("list LENGTH 3") == {
"list": {"$size": 3, "$exists": True}
}

with pytest.raises(VisitError):
self.transform("list:list HAS >=2:<=5")
Expand Down Expand Up @@ -441,7 +448,7 @@ def test_unaliased_length_operator(self):
"cartesian_site_positions.3": {"$exists": False}
}
assert self.transform("cartesian_site_positions LENGTH 3") == {
"cartesian_site_positions": {"$size": 3}
"cartesian_site_positions": {"$size": 3, "$exists": True}
}
assert self.transform("cartesian_site_positions LENGTH >= 10") == {
"cartesian_site_positions.10": {"$exists": True}
Expand Down Expand Up @@ -583,7 +590,7 @@ class MyStructureMapper(mapper("StructureMapper")):
def test_list_properties(self):
"""Test the HAS ALL, ANY and optional ONLY queries."""
assert self.transform('elements HAS ONLY "H","He","Ga","Ta"') == {
"elements": {"$all": ["H", "He", "Ga", "Ta"], "$size": 4}
"elements": {"$all": ["H", "He", "Ga", "Ta"], "$size": 4, "$exists": True}
}

assert self.transform('elements HAS ANY "H","He","Ga","Ta"') == {
Expand All @@ -601,7 +608,13 @@ def test_list_properties(self):
"$and": [
{"elements": {"$in": ["H"]}},
{"elements": {"$all": ["H", "He", "Ga", "Ta"]}},
{"elements": {"$all": ["H", "He", "Ga", "Ta"], "$size": 4}},
{
"elements": {
"$all": ["H", "He", "Ga", "Ta"],
"$size": 4,
"$exists": True,
}
},
{"elements": {"$in": ["H", "He", "Ga", "Ta"]}},
]
}
Expand Down

0 comments on commit 8050405

Please sign in to comment.