diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2024-11-16 14:18:26 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2025-02-19 14:45:55 +0100 |
commit | 977bb2c573f8583f6b239f0499b12522be0d739a (patch) | |
tree | 343da69fef167299db9e03ee8aebe8b052418735 | |
parent | 5cd466f393e9be2bc0bce6315257e3344306bb43 (diff) | |
download | Ishtar-977bb2c573f8583f6b239f0499b12522be0d739a.tar.bz2 Ishtar-977bb2c573f8583f6b239f0499b12522be0d739a.zip |
✨ search: add AND operator "&&"
-rw-r--r-- | archaeological_operations/tests.py | 20 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 29 |
2 files changed, 43 insertions, 6 deletions
diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index e46c79ee6..18e17cca5 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -3084,6 +3084,26 @@ class OperationSearchTest(TestCase, OperationInitTest, SearchText): response = c.get(reverse("get-operation"), {"search_vector": request}) self.assertEqual(json.loads(response.content.decode())["recordsTotal"], 2) + def test_and_search_vector(self): + operation_1 = models.Operation.objects.get(pk=self.operations[0].pk) + operation_2 = models.Operation.objects.get(pk=self.operations[1].pk) + villa = models.RemainType.objects.get(txt_idx="villa") + cairn = models.RemainType.objects.get(txt_idx="cairn") + operation_1.remains.add(villa) + operation_1.remains.add(cairn) + operation_2.remains.add(cairn) + + c = Client() + c.login(username=self.username, password=self.password) + + remain_key = pgettext_lazy("key for text search", "remain") + request = f'{remain_key}="{villa.label}" {remain_key}="{cairn.label}"' + response = c.get(reverse("get-operation"), {"search_vector": request}) + self.assertEqual(json.loads(response.content.decode())["recordsTotal"], 2) + request = f'{remain_key}="{villa.label}" && {remain_key}="{cairn.label}"' + response = c.get(reverse("get-operation"), {"search_vector": request}) + self.assertEqual(json.loads(response.content.decode())["recordsTotal"], 1) + def test_complex_search_vector(self): c = Client() c.login(username=self.username, password=self.password) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 639af1e88..d4a975e37 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -2006,12 +2006,18 @@ def _get_table_cols(data_type, own_table_cols, full, model): def split_dict(dct): if not dct.get("search_vector", None): - return [dct] + return [("OR", dct)] new_dcts = [] - for vector in dct["search_vector"].split(" || "): + + # TODO: manage || and && syntax in the same query + # example: to extract [[]] parenthesis re.findall(r"(.*)\[\[ (.*?) \]\](.*)", s) + split_key, split_type = " || ", "OR" + if " && " in dct["search_vector"]: + split_key, split_type = " && ", "AND" + for vector in dct["search_vector"].split(split_key): new_dct = deepcopy(dct) new_dct["search_vector"] = vector - new_dcts.append(new_dct) + new_dcts.append((split_type, new_dct)) return new_dcts @@ -2566,7 +2572,7 @@ def get_item( ) items = None - for sub_dct in split_dict(dct): + for split_type, sub_dct in split_dict(dct): query, exc_query, extras = main_manager( request, model, @@ -2583,7 +2589,7 @@ def get_item( reversed_many_counted_fields, my_dated_fields, my_number_fields, - and_reqs + and_reqs[:] ) # print("ishtar_common/views_item.py - 2515") @@ -2602,7 +2608,18 @@ def get_item( if not items: items = sub_items else: - items |= sub_items + if not sub_items.exists(): + if split_type == "AND": + items = model.objects.filter(pk__isnull=True) + continue + if split_type == "OR": + items |= sub_items + else: + # in Django m2m queries use the same JOIN... + # items &= sub_items do not work + items &= model.objects.filter(Q( + pk__in=list(sub_items.values_list("pk", flat=True)) + )) if return_query: return items |