"""
Available Queries
-----------------
Queries are used for actual searching - things like relevancy scores,
Levenstein distance, and partial matches.
View the `elasticsearch documentation <query_docs>`_ to see what other options
are available, and put 'em here if you end up using any of 'em.
.. _`query_docs`: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-queries.html
"""
import re
from .filters import date_range, range_filter
MUST = "must"
MUST_NOT = "must_not"
SHOULD = "should"
BOOL = "bool"
def BOOL_CLAUSE(query, **kwargs):
return _CLAUSE(BOOL, query, **kwargs)
def MUST_CLAUSE(query, **kwargs):
return _CLAUSE(MUST, query, **kwargs)
def MUST_NOT_CLAUSE(query, **kwargs):
return _CLAUSE(MUST_NOT, query, **kwargs)
def SHOULD_CLAUSE(query, **kwargs):
return _CLAUSE(SHOULD, query, **kwargs)
def _CLAUSE(clause, query, **kwargs):
clause = {clause: query}
clause.update(kwargs)
return clause
CLAUSES = {
MUST: MUST_CLAUSE,
MUST_NOT: MUST_NOT_CLAUSE,
SHOULD: SHOULD_CLAUSE,
BOOL: BOOL_CLAUSE
}
[docs]def match_all():
"""No-op query used because a default must be specified"""
return {"match_all": {}}
def _smart_query_string(search_string):
special_chars = ['&&', '||', '!', '(', ')', '{', '}', '[', ']', '^', '"',
'~', '*', '?', ':', '\\', '/']
for char in special_chars:
if char in search_string:
return True, search_string
r = re.compile(r'\w+')
tokens = r.findall(search_string)
return False, "*{}*".format("* *".join(tokens))
[docs]def search_string_query(search_string, default_fields=None):
"""
Allows users to use advanced query syntax, but if ``search_string`` does
not use the ES query string syntax, default to doing an infix search for
each term. (This may later change to some kind of fuzzy matching).
This is also available via the main ESQuery class.
"""
if not search_string:
return match_all()
# use simple_query_string for user-provided syntax, since it won't error
uses_syntax, query_string = _smart_query_string(search_string)
query_method = "simple_query_string" if uses_syntax else "query_string"
return {
query_method: {
"query": query_string,
"default_operator": "AND",
"fields": default_fields if not uses_syntax else None,
}
}
def ids_query(doc_ids):
return {"ids": {"values": doc_ids}}
def match(search_string, field, fuzziness="AUTO"):
return {
"match": {
field: {
"query": search_string,
"fuzziness": fuzziness,
}
}
}
[docs]def nested(path, query, *args, **kwargs):
"""
Creates a nested query for use with nested documents
Keyword arguments such as score_mode and others can be added.
"""
nested = {
"path": path,
"query": query
}
nested.update(kwargs)
return {
"nested": nested
}
[docs]def nested_filter(path, filter_, *args, **kwargs):
"""
Creates a nested query for use with nested documents
Keyword arguments such as score_mode and others can be added.
"""
nested = {
"path": path,
"filter": filter_
}
nested.update(kwargs)
return {
"nested": nested
}
[docs]def filtered(query, filter_):
"""
Filtered query for performing both filtering and querying at once
"""
return {
"bool": {
"filter": [filter_],
"must": query
}
}
def regexp(field, regex):
return {
'regexp': {
field: {
'value': regex,
}
}
}
range_query = range_filter
date_range = date_range