Searching¶
On models with ZomboDBIndex
, use methods from SearchQuerySet
/SearchQuerySetMixin
to perform various kinds of Elasticsearch queries:
query_string_search¶
The query_string_search()
method implements the simplest type of Elasticsearch queries: the ones with the query string syntax. To use it, just pass as an argument a string that follows the query string syntax.
Restaurant.objects.query_string_search("brasil~ AND steak*")
dsl_search¶
The query string syntax is user-friendly, but it’s limited. For supporting all kinds of Elasticsearch queries, the recommended way is to use the dsl_search()
method. It accepts arguments of elasticsearch-dsl-py Query
objects. Those objects have the same representation power of the Elasticsearch JSON Query DSL. You can do “match”, “term”, and even compound queries like “bool”.
Here we’re using the elasticsearch-dsl-py Q
shortcut to create Query
objects:
from elasticsearch_dsl import Q as ElasticsearchQ
query = ElasticsearchQ(
'bool',
must=[
ElasticsearchQ('match', name='pizza'),
ElasticsearchQ('match', street='school')
]
)
Restaurant.objects.dsl_search(query)
dict_search¶
If you already have a Elasticsearch JSON query mounted as a dict
, use the dict_search()
method. The dict
will be serialized using the JSONSerializer
of elasticsearch-py, the official Python Elasticsearch client. This means dict values of date
, datetime
, Decimal
, and UUID
types will be correctly serialized.
Validation¶
If you’re receiving queries from the end-user, particularly query string queries, you should call the search methods with validate=True
. This will perform Elasticsearch-side validation through the Validate API. When doing that, InvalidElasticsearchQuery
may be raised.
from django_zombodb.exceptions import InvalidElasticsearchQuery
queryset = Restaurant.objects.all()
try:
queryset = queryset.query_string_search("AND steak*", validate=True)
except InvalidElasticsearchQuery:
messages.error(request, "Invalid search query. Not filtering by search.")
Sorting by score¶
By default, the resulting queryset from the search methods is unordered. You can get results ordered by Elasticsearch’s score passing sort=True
.
Restaurant.objects.query_string_search("brasil~ AND steak*", sort=True)
Alternatively, if you want to combine with your own order_by
, you can use the method annotate_score()
:
Restaurant.objects.query_string_search(
"brazil* AND steak*"
).annotate_score(
attr='zombodb_score'
).order_by('-zombodb_score', 'name', 'pk')
Limiting¶
It’s a good practice to set a hard limit to the number of search results. For most search use cases, you shouldn’t need more than a certain number of results, either because users will only consume some of the high scoring results, or because documents with lower scores aren’t relevant to your process. To limit the results, use the limit
parameter on search methods:
Restaurant.objects.query_string_search("brasil~ AND steak*", limit=1000)
Lazy and Chainable¶
The search methods are like the traditional filter
method: they return a regular Django QuerySet
that supports all operations, and that’s lazy and chainable. Therefore, you can do things like:
Restaurant.objects.filter(
name__startswith='Pizza'
).query_string_search(
'name:Hut'
).filter(
street__contains='Road'
)
Warning
It’s fine to call filter
/exclude
/etc. before and after search. If possible, the best would be using only a Elasticsearch query. However, it’s definitely slow to call search methods multiple times on the same queryset! Please avoid this:
Restaurant.objects.query_string_search(
'name:Pizza'
).query_string_search(
'name:Hut'
)
While that may work as expected, it’s extremely inneficient. Instead, use compound queries like “bool”. They’ll be much faster. Note that “bool” queries might be quite confusing to implement. Check tutorials about them, like this one.
Missing features¶
Currently django-zombodb doesn’t support ZomboDB’s offset and sort functions that work on the Elasticsearch side. Regular SQL LIMIT/OFFSET/ORDER BY works fine, therefore traditional QuerySet
operations work, but aren’t as performant as doing the same on ES side.