mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-27 07:02:26 +00:00
Handle queries with "+" operators
This commit is contained in:
parent
fe11867ba9
commit
5eac7f6614
1 changed files with 98 additions and 45 deletions
143
search/views.py
143
search/views.py
|
@ -62,11 +62,22 @@ def is_int(variable):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
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():
|
||||||
|
"""Build empty filters used by Django"""
|
||||||
|
filters = [Q() for f in filter_fields()]
|
||||||
|
|
||||||
|
|
||||||
def finish_results(request, results, col, order):
|
def finish_results(request, results, col, order):
|
||||||
"""Sort the results by applying filters and then limit them to the
|
"""Sort the results by applying filters and then limit them to the
|
||||||
number of max results. Finally add the info of the nmax number of results
|
number of max results. Finally add the info of the nmax number of results
|
||||||
to the dict"""
|
to the dict"""
|
||||||
|
|
||||||
|
results["users"] += results["clubs"]
|
||||||
results["users"] = SortTable.sort(
|
results["users"] = SortTable.sort(
|
||||||
results["users"], col, order, SortTable.USERS_INDEX
|
results["users"], col, order, SortTable.USERS_INDEX
|
||||||
)
|
)
|
||||||
|
@ -121,12 +132,6 @@ def search_single_word(word, filters, user, start, end, user_state, aff):
|
||||||
)
|
)
|
||||||
filter_users = (filter_clubs | Q(name__icontains=word))
|
filter_users = (filter_clubs | Q(name__icontains=word))
|
||||||
|
|
||||||
if len(word.split(" ")) >= 2:
|
|
||||||
# Assume the room is in 1 word, and the building may be in multiple words
|
|
||||||
building = " ".join(word.split(" ")[:-1])
|
|
||||||
room = word.split(" ")[-1]
|
|
||||||
filter_users |= (Q(room__name__icontains=room) & Q(room__building__name__icontains=building))
|
|
||||||
|
|
||||||
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)
|
||||||
filter_users &= Q(id=user.id)
|
filter_users &= Q(id=user.id)
|
||||||
|
@ -211,12 +216,6 @@ def search_single_word(word, filters, user, start, end, user_state, aff):
|
||||||
Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word)
|
Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word)
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(word.split(" ")) >= 2:
|
|
||||||
# Assume the room is in 1 word, and the building may be in multiple words
|
|
||||||
building = " ".join(word.split(" ")[:-1])
|
|
||||||
room = word.split(" ")[-1]
|
|
||||||
filter_rooms |= (Q(name__icontains=room) & Q(building__name__icontains=building))
|
|
||||||
|
|
||||||
filters["rooms"] |= filter_rooms
|
filters["rooms"] |= filter_rooms
|
||||||
|
|
||||||
# Switch ports
|
# Switch ports
|
||||||
|
@ -252,7 +251,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff):
|
||||||
|
|
||||||
|
|
||||||
def apply_filters(filters, user, aff):
|
def apply_filters(filters, user, aff):
|
||||||
""" Apply the filters constructed by search_single_word.
|
""" Apply the filters constructed by search_single_query.
|
||||||
It also takes into account the visual filters defined during
|
It also takes into account the visual filters defined during
|
||||||
the search query.
|
the search query.
|
||||||
"""
|
"""
|
||||||
|
@ -305,53 +304,117 @@ def apply_filters(filters, user, aff):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def get_words(query):
|
def search_single_query(query, filters, user, start, end, user_state, aff):
|
||||||
"""Function used to split the uery in different words to look for.
|
""" Handle different queries an construct the correct filters using
|
||||||
The rules are simple :
|
search_single_word"""
|
||||||
|
if query["operator"] == "+":
|
||||||
|
# Special queries with "+" operators should use & rather than |
|
||||||
|
for q in query["subqueries"]:
|
||||||
|
# Construct an independent filter for each subquery
|
||||||
|
subfilters = empty_filters()
|
||||||
|
subfilters = search_single_word(q, subfilters, user, start, end, user_state, aff)
|
||||||
|
|
||||||
|
# Apply the new filter
|
||||||
|
for field in filter_fields():
|
||||||
|
filters[field] &= subfilters[field]
|
||||||
|
|
||||||
|
return filters
|
||||||
|
|
||||||
|
# Handle standard queries
|
||||||
|
q = query["text"]
|
||||||
|
return search_single_word(q, filters, user, start, end, user_state, aff)
|
||||||
|
|
||||||
|
|
||||||
|
def create_queries(query):
|
||||||
|
"""Function used to split the query in different words to look for.
|
||||||
|
The rules are the following :
|
||||||
- anti-slash ('\\') is used to escape characters
|
- anti-slash ('\\') is used to escape characters
|
||||||
- anything between quotation marks ('"') is kept intact (not
|
- anything between quotation marks ('"') is kept intact (not
|
||||||
interpreted as separators) excepts anti-slashes used to escape
|
interpreted as separators) excepts anti-slashes used to escape
|
||||||
|
Values in between quotation marks are not searched accross
|
||||||
|
multiple field in the database (contrary to +)
|
||||||
- spaces (' ') and commas (',') are used to separated words
|
- spaces (' ') and commas (',') are used to separated words
|
||||||
|
- "+" signs are used as "and" operators
|
||||||
"""
|
"""
|
||||||
|
# A dict representing the different queries extracted from the user's text
|
||||||
|
queries = []
|
||||||
|
|
||||||
words = []
|
# Format: {
|
||||||
i = 0
|
# "text": "", # Content of the query
|
||||||
|
# "operator": None, # Whether a special char ("+") was used
|
||||||
|
# "subqueries": None # When splitting the query in subparts (ex when using "+")
|
||||||
|
# }
|
||||||
|
current_query = None
|
||||||
|
|
||||||
|
# Whether the query is between "
|
||||||
keep_intact = False
|
keep_intact = False
|
||||||
|
|
||||||
|
# Whether the previous char was a \
|
||||||
escaping_char = False
|
escaping_char = False
|
||||||
|
|
||||||
for char in query:
|
for char in query:
|
||||||
if i >= len(words):
|
if current_query is None:
|
||||||
# We are starting a new word
|
# We are starting a new word
|
||||||
words.append("")
|
current_query = { "text": "", "operator": None, "subqueries": None }
|
||||||
|
|
||||||
if escaping_char:
|
if escaping_char:
|
||||||
# The last char war a \ so we escape this char
|
# The last char war a \ so we escape this char
|
||||||
escaping_char = False
|
escaping_char = False
|
||||||
words[i] += char
|
current_query["text"] += char
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == "\\":
|
if char == "\\":
|
||||||
# We need to escape the next char
|
# We need to escape the next char
|
||||||
escaping_char = True
|
escaping_char = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == '"':
|
if char == '"':
|
||||||
# Toogle the keep_intact state, if true, we are between two "
|
# Toogle the keep_intact state, if true, we are between two "
|
||||||
keep_intact = not keep_intact
|
keep_intact = not keep_intact
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if keep_intact:
|
if keep_intact:
|
||||||
# If we are between two ", ignore separators
|
# If we are between two ", ignore separators
|
||||||
words[i] += char
|
current_query["text"] += char
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == "+":
|
if char == "+":
|
||||||
# If we encouter a + outside of ", we replace it with a space
|
# Can't sart a query with a "+", consider it escaped
|
||||||
words[i] += " "
|
if len(current_query["text"]) == 0:
|
||||||
|
current_query["text"] = char
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Build a slightly more complicate data structure
|
||||||
|
# This is need for queries like '"A B"+C'
|
||||||
|
if current_query["operator"] is None:
|
||||||
|
current_query["operator"] = "+"
|
||||||
|
current_query["subqueries"] = []
|
||||||
|
|
||||||
|
current_query["subqueries"].append(current_query["text"])
|
||||||
|
current_query["text"] = ""
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == " " or char == ",":
|
if char == " " or char == ",":
|
||||||
# If we encouter a separator outside of ", we create a new word
|
# If we encouter a separator outside of ", we create a new word
|
||||||
if words[i] is not "":
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
# If we haven't encountered any special case, add the char to the word
|
|
||||||
words[i] += char
|
|
||||||
|
|
||||||
return words
|
if len(current_query["text"]) == 0:
|
||||||
|
# Discard empty queries
|
||||||
|
continue
|
||||||
|
|
||||||
|
if current_query["operator"] is not None:
|
||||||
|
# If we were building a special structure, finish building it
|
||||||
|
current_query["subqueries"].append(current_query["text"])
|
||||||
|
current_query["text"] = ""
|
||||||
|
|
||||||
|
# Save the query and start a new one
|
||||||
|
queries.append(current_query)
|
||||||
|
current_query = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If we haven't encountered any special case, add the char to the word
|
||||||
|
current_query["text"].append(char)
|
||||||
|
|
||||||
|
return queries
|
||||||
|
|
||||||
|
|
||||||
def get_results(query, request, params):
|
def get_results(query, request, params):
|
||||||
|
@ -365,22 +428,12 @@ def get_results(query, request, params):
|
||||||
user_state = params.get("u", initial_choices(CHOICES_USER))
|
user_state = params.get("u", initial_choices(CHOICES_USER))
|
||||||
aff = params.get("a", initial_choices(CHOICES_AFF))
|
aff = params.get("a", initial_choices(CHOICES_AFF))
|
||||||
|
|
||||||
filters = {
|
filters = empty_filters()
|
||||||
"users": Q(),
|
|
||||||
"clubs": Q(),
|
|
||||||
"machines": Q(),
|
|
||||||
"factures": Q(),
|
|
||||||
"bans": Q(),
|
|
||||||
"whitelists": Q(),
|
|
||||||
"rooms": Q(),
|
|
||||||
"ports": Q(),
|
|
||||||
"switches": Q(),
|
|
||||||
}
|
|
||||||
|
|
||||||
words = get_words(query)
|
queries = create_queries(query)
|
||||||
for word in words:
|
for query in queries:
|
||||||
filters = search_single_word(
|
filters = search_single_query(
|
||||||
word, filters, request.user, start, end, user_state, aff
|
query, filters, request.user, start, end, user_state, aff
|
||||||
)
|
)
|
||||||
|
|
||||||
results = apply_filters(filters, request.user, aff)
|
results = apply_filters(filters, request.user, aff)
|
||||||
|
|
Loading…
Reference in a new issue