summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
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
commit9de2c94a7a528e1ae24bc2a0a9bb9354329d0a93 (patch)
tree3acc5fe0568d4bc91e6f38abbadff7e05af35aa7 /ishtar_common
parentf94890fe2d2ab241ee1cd5b980bf88409b47a998 (diff)
downloadIshtar-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.py117
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: