Simplifies query trees by combining unnecessary 'and' connection nodes into a single Q-object.
| 29 | |
| 30 | |
| 31 | class SimplificationVisitor(QNodeVisitor): |
| 32 | """Simplifies query trees by combining unnecessary 'and' connection nodes |
| 33 | into a single Q-object. |
| 34 | """ |
| 35 | |
| 36 | def visit_combination(self, combination): |
| 37 | if combination.operation == combination.AND: |
| 38 | # The simplification only applies to 'simple' queries |
| 39 | if all(isinstance(node, Q) for node in combination.children): |
| 40 | queries = [n.query for n in combination.children] |
| 41 | try: |
| 42 | return Q(**self._query_conjunction(queries)) |
| 43 | except DuplicateQueryConditionsError: |
| 44 | # Cannot be simplified |
| 45 | pass |
| 46 | return combination |
| 47 | |
| 48 | def _query_conjunction(self, queries): |
| 49 | """Merges query dicts - effectively &ing them together.""" |
| 50 | query_ops = set() |
| 51 | combined_query = {} |
| 52 | for query in queries: |
| 53 | ops = set(query.keys()) |
| 54 | # Make sure that the same operation isn't applied more than once |
| 55 | # to a single field |
| 56 | intersection = ops.intersection(query_ops) |
| 57 | if intersection: |
| 58 | raise DuplicateQueryConditionsError() |
| 59 | |
| 60 | query_ops.update(ops) |
| 61 | combined_query.update(copy.deepcopy(query)) |
| 62 | return combined_query |
| 63 | |
| 64 | |
| 65 | class QueryCompilerVisitor(QNodeVisitor): |