Skip to content

Commit

Permalink
Add recursive method to parse not/and/or queries
Browse files Browse the repository at this point in the history
  • Loading branch information
ml-evs committed Mar 10, 2020
1 parent 53f3746 commit 557426a
Showing 1 changed file with 27 additions and 10 deletions.
37 changes: 27 additions & 10 deletions optimade/filtertransformers/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,7 @@ def expression_clause(self, arg):

def expression_phrase(self, arg):
# expression_phrase: [ NOT ] ( comparison | "(" expression ")" )
if len(arg) == 1:
# without NOT
return arg[0]

if list(arg[1].keys()) == ["$or"]:
return {"$nor": arg[1]["$or"]}

# with NOT
# TODO: This implementation probably fails in the case of `"(" expression ")"`
return {prop: {"$not": expr} for prop, expr in arg[1].items()}
return self._recursive_expression_phrase(arg)

@v_args(inline=True)
def comparison(self, value):
Expand Down Expand Up @@ -208,3 +199,29 @@ def __default__(self, data, children, meta):
raise NotImplementedError(
f"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}"
)

def _recursive_expression_phrase(self, arg):
""" Helper function for parsing `expression_phrase`. Recursively sorts out
the correct precedence for $and, $or and $nor.
"""
if len(arg) == 1:
# without NOT
return arg[0]

# handle the case of {"$not": "$or": [expr1, expr2]} using $nor
if "$or" in arg[1]:
return {"$nor": self._recursive_expression_phrase([arg[1]["$or"]])}

# handle the case of {"$not": "$and": [expr1, expr2]} using per-expression negation,
# e.g. {"$and": [~expr1, ~expr2]}.
if "$and" in arg[1]:
return {
"$and": [
self._recursive_expression_phrase(["NOT", subdict])
for subdict in arg[1]["$and"]
]
}

# simple case of negating one expression, from NOT (expr) to ~expr.
return {prop: {"$not": expr} for prop, expr in arg[1].items()}

0 comments on commit 557426a

Please sign in to comment.