summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commit977bb2c573f8583f6b239f0499b12522be0d739a (patch)
tree343da69fef167299db9e03ee8aebe8b052418735
parent5cd466f393e9be2bc0bce6315257e3344306bb43 (diff)
downloadIshtar-977bb2c573f8583f6b239f0499b12522be0d739a.tar.bz2
Ishtar-977bb2c573f8583f6b239f0499b12522be0d739a.zip
✨ search: add AND operator "&&"
-rw-r--r--archaeological_operations/tests.py20
-rw-r--r--ishtar_common/views_item.py29
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