mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-23 23:43:47 +00:00
Merge branch 'fix-advanced-search-filters-for-empty-query' into 'dev'
Fix advanced search filters for empty query See merge request re2o/re2o!577
This commit is contained in:
commit
08aa4c043e
1 changed files with 62 additions and 53 deletions
113
search/engine.py
113
search/engine.py
|
@ -42,6 +42,20 @@ from preferences.models import GeneralOption
|
||||||
from re2o.base import SortTable, re2o_paginator
|
from re2o.base import SortTable, re2o_paginator
|
||||||
|
|
||||||
|
|
||||||
|
# List of fields the search applies to
|
||||||
|
FILTER_FIELDS = [
|
||||||
|
"users",
|
||||||
|
"clubs",
|
||||||
|
"machines",
|
||||||
|
"factures",
|
||||||
|
"bans",
|
||||||
|
"whitelists",
|
||||||
|
"rooms",
|
||||||
|
"ports",
|
||||||
|
"switches",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Query:
|
class Query:
|
||||||
"""Class representing a query.
|
"""Class representing a query.
|
||||||
It can contain the user-entered text, the operator for the query,
|
It can contain the user-entered text, the operator for the query,
|
||||||
|
@ -53,6 +67,7 @@ class Query:
|
||||||
subqueries: list of Query objects when the current query is split in
|
subqueries: list of Query objects when the current query is split in
|
||||||
several parts.
|
several parts.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text="", case_sensitive=False):
|
def __init__(self, text="", case_sensitive=False):
|
||||||
"""Initialise an instance of Query.
|
"""Initialise an instance of Query.
|
||||||
|
|
||||||
|
@ -98,27 +113,14 @@ class Query:
|
||||||
return self.operator.join([q.plaintext for q in self.subqueries])
|
return self.operator.join([q.plaintext for q in self.subqueries])
|
||||||
|
|
||||||
if self.case_sensitive:
|
if self.case_sensitive:
|
||||||
return "\"{}\"".format(self.text)
|
return '"{}"'.format(self.text)
|
||||||
|
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
|
|
||||||
def filter_fields():
|
|
||||||
"""Return the list of fields the search applies to."""
|
|
||||||
return ["users",
|
|
||||||
"clubs",
|
|
||||||
"machines",
|
|
||||||
"factures",
|
|
||||||
"bans",
|
|
||||||
"whitelists",
|
|
||||||
"rooms",
|
|
||||||
"ports",
|
|
||||||
"switches"]
|
|
||||||
|
|
||||||
|
|
||||||
def empty_filters():
|
def empty_filters():
|
||||||
"""Build empty filters used by Django."""
|
"""Build empty filters used by Django."""
|
||||||
return {f: Q() for f in filter_fields()}
|
return {f: Q() for f in FILTER_FIELDS}
|
||||||
|
|
||||||
|
|
||||||
def is_int(variable):
|
def is_int(variable):
|
||||||
|
@ -176,10 +178,9 @@ def finish_results(request, results, col, order):
|
||||||
max_result = GeneralOption.get_cached_value("search_display_page")
|
max_result = GeneralOption.get_cached_value("search_display_page")
|
||||||
for name, val in results.items():
|
for name, val in results.items():
|
||||||
page_arg = name + "_page"
|
page_arg = name + "_page"
|
||||||
results[name] = re2o_paginator(request,
|
results[name] = re2o_paginator(
|
||||||
val.distinct(),
|
request, val.distinct(), max_result, page_arg=page_arg
|
||||||
max_result,
|
)
|
||||||
page_arg=page_arg)
|
|
||||||
|
|
||||||
results.update({"max_result": max_result})
|
results.update({"max_result": max_result})
|
||||||
|
|
||||||
|
@ -206,9 +207,9 @@ def contains_filter(attribute, word, case_sensitive=False):
|
||||||
return Q(**{attr: word})
|
return Q(**{attr: word})
|
||||||
|
|
||||||
|
|
||||||
def search_single_word(word, filters, user, start, end,
|
def search_single_word(
|
||||||
user_state, email_state, aff,
|
word, filters, user, start, end, user_state, email_state, aff, case_sensitive=False
|
||||||
case_sensitive=False):
|
):
|
||||||
"""Construct the correct filters to match differents fields of some models
|
"""Construct the correct filters to match differents fields of some models
|
||||||
with the given query according to the given filters.
|
with the given query according to the given filters.
|
||||||
The match fields are either CharField or IntegerField that will be displayed
|
The match fields are either CharField or IntegerField that will be displayed
|
||||||
|
@ -230,10 +231,7 @@ def search_single_word(word, filters, user, start, end,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Users have a name whereas clubs only have a surname
|
# Users have a name whereas clubs only have a surname
|
||||||
filter_users = (
|
filter_users = filter_clubs | contains_filter("name", word, case_sensitive)
|
||||||
filter_clubs
|
|
||||||
| contains_filter("name", word, case_sensitive)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not User.can_view_all(user)[0]:
|
if not User.can_view_all(user)[0]:
|
||||||
filter_clubs &= Q(id=user.id)
|
filter_clubs &= Q(id=user.id)
|
||||||
|
@ -252,12 +250,15 @@ def search_single_word(word, filters, user, start, end,
|
||||||
if "1" in aff:
|
if "1" in aff:
|
||||||
filter_machines = (
|
filter_machines = (
|
||||||
contains_filter("name", word, case_sensitive)
|
contains_filter("name", word, case_sensitive)
|
||||||
| (contains_filter("user__pseudo", word, case_sensitive)
|
| (
|
||||||
|
contains_filter("user__pseudo", word, case_sensitive)
|
||||||
& Q(user__state__in=user_state)
|
& Q(user__state__in=user_state)
|
||||||
& Q(user__email_state__in=email_state))
|
& Q(user__email_state__in=email_state)
|
||||||
|
)
|
||||||
| contains_filter("interface__domain__name", word, case_sensitive)
|
| contains_filter("interface__domain__name", word, case_sensitive)
|
||||||
| contains_filter("interface__domain__related_domain__name",
|
| contains_filter(
|
||||||
word, case_sensitive)
|
"interface__domain__related_domain__name", word, case_sensitive
|
||||||
|
)
|
||||||
| contains_filter("interface__mac_address", word, case_sensitive)
|
| contains_filter("interface__mac_address", word, case_sensitive)
|
||||||
| contains_filter("interface__ipv4__ipv4", word, case_sensitive)
|
| contains_filter("interface__ipv4__ipv4", word, case_sensitive)
|
||||||
)
|
)
|
||||||
|
@ -339,13 +340,12 @@ def search_single_word(word, filters, user, start, end,
|
||||||
# Switch ports
|
# Switch ports
|
||||||
if "6" in aff and User.can_view_all(user):
|
if "6" in aff and User.can_view_all(user):
|
||||||
filter_ports = (
|
filter_ports = (
|
||||||
contains_filter("machine_interface__domain__name",
|
contains_filter("machine_interface__domain__name", word, case_sensitive)
|
||||||
word, case_sensitive)
|
| contains_filter(
|
||||||
| contains_filter("related__switch__interface__domain__name",
|
"related__switch__interface__domain__name", word, case_sensitive
|
||||||
word, case_sensitive)
|
)
|
||||||
| contains_filter("custom_profile__name", word, case_sensitive)
|
| contains_filter("custom_profile__name", word, case_sensitive)
|
||||||
| contains_filter("custom_profile__profil_default",
|
| contains_filter("custom_profile__profil_default", word, case_sensitive)
|
||||||
word, case_sensitive)
|
|
||||||
| contains_filter("details", word, case_sensitive)
|
| contains_filter("details", word, case_sensitive)
|
||||||
# Added through annotate
|
# Added through annotate
|
||||||
| contains_filter("room_full_name", word, case_sensitive)
|
| contains_filter("room_full_name", word, case_sensitive)
|
||||||
|
@ -360,8 +360,7 @@ def search_single_word(word, filters, user, start, end,
|
||||||
filter_switches = (
|
filter_switches = (
|
||||||
contains_filter("interface__domain__name", word, case_sensitive)
|
contains_filter("interface__domain__name", word, case_sensitive)
|
||||||
| contains_filter("interface__ipv4__ipv4", word, case_sensitive)
|
| contains_filter("interface__ipv4__ipv4", word, case_sensitive)
|
||||||
| contains_filter("switchbay__building__name",
|
| contains_filter("switchbay__building__name", word, case_sensitive)
|
||||||
word, case_sensitive)
|
|
||||||
| contains_filter("stack__name", word, case_sensitive)
|
| contains_filter("stack__name", word, case_sensitive)
|
||||||
| contains_filter("model__reference", word, case_sensitive)
|
| contains_filter("model__reference", word, case_sensitive)
|
||||||
| contains_filter("model__constructor__name", word, case_sensitive)
|
| contains_filter("model__constructor__name", word, case_sensitive)
|
||||||
|
@ -399,13 +398,11 @@ def apply_filters(filters, user, aff):
|
||||||
# Users and clubs
|
# Users and clubs
|
||||||
if "0" in aff:
|
if "0" in aff:
|
||||||
results["users"] = Adherent.objects.annotate(
|
results["users"] = Adherent.objects.annotate(
|
||||||
room_full_name=Concat("room__building__name",
|
room_full_name=Concat("room__building__name", Value(" "), "room__name"),
|
||||||
Value(" "), "room__name"),
|
|
||||||
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
||||||
).filter(filters["users"])
|
).filter(filters["users"])
|
||||||
results["clubs"] = Club.objects.annotate(
|
results["clubs"] = Club.objects.annotate(
|
||||||
room_full_name=Concat("room__building__name",
|
room_full_name=Concat("room__building__name", Value(" "), "room__name"),
|
||||||
Value(" "), "room__name"),
|
|
||||||
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
||||||
).filter(filters["clubs"])
|
).filter(filters["clubs"])
|
||||||
|
|
||||||
|
@ -435,8 +432,7 @@ def apply_filters(filters, user, aff):
|
||||||
# Switch ports
|
# Switch ports
|
||||||
if "6" in aff and User.can_view_all(user):
|
if "6" in aff and User.can_view_all(user):
|
||||||
results["ports"] = Port.objects.annotate(
|
results["ports"] = Port.objects.annotate(
|
||||||
room_full_name=Concat("room__building__name",
|
room_full_name=Concat("room__building__name", Value(" "), "room__name"),
|
||||||
Value(" "), "room__name"),
|
|
||||||
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
room_full_name_stuck=Concat("room__building__name", "room__name"),
|
||||||
).filter(filters["ports"])
|
).filter(filters["ports"])
|
||||||
|
|
||||||
|
@ -455,24 +451,32 @@ def search_single_query(query, filters, user, start, end, user_state, email_stat
|
||||||
newfilters = empty_filters()
|
newfilters = empty_filters()
|
||||||
for q in query.subqueries:
|
for q in query.subqueries:
|
||||||
# Construct an independent filter for each subquery
|
# Construct an independent filter for each subquery
|
||||||
subfilters = search_single_query(q, empty_filters(), user,
|
subfilters = search_single_query(
|
||||||
start, end, user_state,
|
q, empty_filters(), user, start, end, user_state, email_state, aff
|
||||||
email_state, aff)
|
)
|
||||||
|
|
||||||
# Apply the subfilter
|
# Apply the subfilter
|
||||||
for field in filter_fields():
|
for field in FILTER_FIELDS:
|
||||||
newfilters[field] &= subfilters[field]
|
newfilters[field] &= subfilters[field]
|
||||||
|
|
||||||
# Add these filters to the existing ones
|
# Add these filters to the existing ones
|
||||||
for field in filter_fields():
|
for field in FILTER_FIELDS:
|
||||||
filters[field] |= newfilters[field]
|
filters[field] |= newfilters[field]
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
# Handle standard queries
|
# Handle standard queries
|
||||||
return search_single_word(query.text, filters, user, start, end,
|
return search_single_word(
|
||||||
user_state, email_state, aff,
|
query.text,
|
||||||
query.case_sensitive)
|
filters,
|
||||||
|
user,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
user_state,
|
||||||
|
email_state,
|
||||||
|
aff,
|
||||||
|
query.case_sensitive,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_queries(query):
|
def create_queries(query):
|
||||||
|
@ -564,4 +568,9 @@ def create_queries(query):
|
||||||
|
|
||||||
queries.append(current_query)
|
queries.append(current_query)
|
||||||
|
|
||||||
|
# Make sure there is at least one query, even if it's empty
|
||||||
|
# Otherwise, display filters (for advanced search) won't work
|
||||||
|
# when the search text field is empty
|
||||||
|
queries = queries or [Query()]
|
||||||
|
|
||||||
return queries
|
return queries
|
||||||
|
|
Loading…
Reference in a new issue