diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-06-29 18:45:21 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-08-13 18:26:03 +0200 |
commit | 9de2c94a7a528e1ae24bc2a0a9bb9354329d0a93 (patch) | |
tree | 3acc5fe0568d4bc91e6f38abbadff7e05af35aa7 /ishtar_common | |
parent | f94890fe2d2ab241ee1cd5b980bf88409b47a998 (diff) | |
download | Ishtar-9de2c94a7a528e1ae24bc2a0a9bb9354329d0a93.tar.bz2 Ishtar-9de2c94a7a528e1ae24bc2a0a9bb9354329d0a93.zip |
Searches: manage AND, OR, parentheses and open search '*' (refs #4180)
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/views_item.py | 117 |
1 files changed, 111 insertions, 6 deletions
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 513035903..f5e47a832 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -268,11 +268,107 @@ def _get_values(request, val): return new_vals +def _push_to_list(obj, current_group, depth): + """ + parse_parentheses helper function + """ + try: + while depth > 0: + current_group = current_group[-1] + depth -= 1 + except IndexError: + # tolerant to parentheses mismatch + pass + if current_group and type(obj) in (unicode, str) and \ + type(current_group[-1]) in (unicode, str): + current_group[-1] += obj + else: + current_group.append(obj) + + +def _parse_parentheses(s): + """ + Parse parentheses into list. + (OA01 & (pierre | ciseau)) -> ["0A01 &", ["pierre | ciseau"]] + """ + + groups = [] + depth = 0 + + for char in s: + if char == u'(': + _push_to_list([], groups, depth) + depth += 1 + elif char == ')': + if depth > 0: + depth -= 1 + else: + _push_to_list(char, groups, depth) + # for non tolerant to parentheses mismatch check depth is equal to 0 + return groups + + +FORBIDDEN_CHAR = [u":"] +RESERVED_CHAR = [u"|", u"&"] + + +def _parse_query_string(string): + string = string.strip().lower() + for reserved_char in FORBIDDEN_CHAR: + string = string.replace(reserved_char, u"") + if len(string) != 1: + for reserved_char in RESERVED_CHAR: + string = string.replace(reserved_char, u"") + # like search + if string.endswith(u'*'): + string = string[:-1] + u':*' + + return string + + +def _parse_parentheses_groups(groups): + """ + Transform parentheses groups to query + """ + if type(groups) is not list: + string = groups.strip() + # split into many groups if spaces + if ' ' not in string: + return _parse_query_string(groups) + return _parse_parentheses_groups(string.split(u" ")) + if not groups: # empty list + return "" + query = u"(" + previous_sep, has_item = None, False + for item in groups: + q = _parse_parentheses_groups(item).strip() + if not q: + continue + if q in (u"|", u"&"): + if previous_sep or not has_item: + continue # multiple sep is not relevant + previous_sep = q + continue + if has_item: + if previous_sep: + query += previous_sep + else: + query += u" & " + query += q + has_item = True + previous_sep = None + query += u")" + return unidecode(query) + + def _search_manage_search_vector(dct): if 'search_vector' in dct: - dct['search_vector'] = SearchQuery( - unidecode(dct['search_vector']), - config=settings.ISHTAR_SEARCH_LANGUAGE + parentheses_groups = _parse_parentheses(dct['search_vector'].strip()) + query = _parse_parentheses_groups(parentheses_groups) + dct['extras'].append( + {'where': ["search_vector @@ (to_tsquery(%s, %s)) = true"], + 'params': [settings.ISHTAR_SEARCH_LANGUAGE, + query]} ) return dct @@ -594,7 +690,13 @@ def get_item(model, func_name, default_name, extra_request_keys=[], reqs |= q and_reqs.append(reqs) break + dct['extras'] = [] dct = _search_manage_search_vector(dct) + search_vector = "" + if 'search_vector' in dct: + search_vector = dct.pop('search_vector') + extras = dct.pop('extras') + query = Q(**dct) for k, or_req in or_reqs: alt_dct = dct.copy() @@ -665,11 +767,14 @@ def get_item(model, func_name, default_name, extra_request_keys=[], dct = {upper_key: current} query &= Q(**dct) - items = model.objects.filter(query).distinct() + items = model.objects.filter(query) + for extra in extras: + items = items.extra(**extra) + items = items.distinct() # print(items.query) - if 'search_vector' in dct: # for serialization - dct['search_vector'] = dct['search_vector'].value + if search_vector: # for serialization + dct['search_vector'] = search_vector # table cols if own_table_cols: |