Skip to content

Instantly share code, notes, and snippets.

@erickpeirson
Created March 7, 2019 23:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erickpeirson/a078b369dfa1ec9bd001261227ffe954 to your computer and use it in GitHub Desktop.
Save erickpeirson/a078b369dfa1ec9bd001261227ffe954 to your computer and use it in GitHub Desktop.
class astr(str):
def __or__(self, other):
return astr(f'({self} | {other})')
def __and__(self, other):
return astr(f'({self} & {other})')
def __invert__(self):
return astr(f'~{self}')
def _grouped_terms_to_q(term_pair: tuple):
"""Generate a :class:`.Q` from grouped terms."""
term_a_raw, operator, term_b_raw = term_pair
if type(term_a_raw) is tuple and len(term_a_raw) == 3:
term_a = _grouped_terms_to_q(term_a_raw)
else:
term_a = astr(term_a_raw[1])
if type(term_b_raw) is tuple and len(term_b_raw) == 3:
term_b = _grouped_terms_to_q(term_b_raw)
else:
term_b = astr(term_b_raw[1])
if operator == 'OR':
return term_a | term_b
elif operator == 'AND':
return term_a & term_b
elif operator == 'NOT':
return term_a & ~term_b
else:
# TODO: Confirm proper exception.
raise TypeError("Invalid operator for terms")
def _get_operator(obj) -> str:
if type(obj) is tuple and len(obj) == 3:
return _get_operator(obj[0])
return obj[0] # type: ignore
def _group_terms(terms) -> tuple:
"""Group fielded search terms into a set of nested tuples."""
for operator in ['NOT', 'AND', 'OR']:
i = 0
while i < len(terms) - 1:
if _get_operator(terms[i+1]) == operator:
terms[i] = (terms[i], operator, terms[i+1])
terms.pop(i+1)
i -= 1
i += 1
assert len(terms) == 1
return terms[0] # type: ignore
_grouped_terms_to_q(
_group_terms([
('AND', '1'),
('OR', '2'),
('AND', '3'),
('OR', '4'),
('OR', '5'),
('NOT', '6'),
('AND', '7'),
])
)
# '(((1 | (2 & 3)) | 4) | ((5 & ~6) & 7))'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment