kopia lustrzana https://github.com/wagtail/wagtail
Merge branch 'kaedroho-search-ordering'
commit
928bc0d25f
|
@ -138,7 +138,6 @@ Here's an example of using the "operator" keyword argument:
|
|||
# Only "hello world" returned as that's the only item that contains both terms
|
||||
[<Thing: Hello world>]
|
||||
|
||||
|
||||
For page, image and document models, the ``operator`` keyword argument is also supported on the QuerySet's ``search`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -149,6 +148,24 @@ For page, image and document models, the ``operator`` keyword argument is also s
|
|||
[<Page: Hello World>, <Page: Hello>, <Page: World>]
|
||||
|
||||
|
||||
Custom ordering
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
By default, search results are ordered by relevance, if the backend supports it. To preserve the QuerySet's existing ordering, the ``order_by_relevance`` keyword argument needs to be set to ``False`` on the ``search()`` method.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Get a list of events ordered by date
|
||||
>>> EventPage.objects.order_by('date').search("Event", order_by_relevance=False)
|
||||
|
||||
# Events ordered by date
|
||||
[<EventPage: Easter>, <EventPage: Halloween>, <EventPage: Christmas>]
|
||||
|
||||
|
||||
.. _wagtailsearch_frontend_views:
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ class SearchTest(models.Model, index.Indexed):
|
|||
# Return the child if there is one, otherwise return self
|
||||
return child or self
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class SearchTestChild(SearchTest):
|
||||
subtitle = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
|
|
@ -235,8 +235,10 @@ class PageManager(models.Manager):
|
|||
def not_public(self):
|
||||
return self.get_queryset().not_public()
|
||||
|
||||
def search(self, query_string, fields=None, backend='default'):
|
||||
return self.get_queryset().search(query_string, fields=fields, backend=backend)
|
||||
def search(self, query_string, fields=None,
|
||||
operator=None, order_by_relevance=True, backend='default'):
|
||||
return self.get_queryset().search(query_string, fields=fields,
|
||||
operator=operator, order_by_relevance=order_by_relevance, backend=backend)
|
||||
|
||||
def specific(self):
|
||||
return self.get_queryset().specific()
|
||||
|
|
|
@ -7,10 +7,10 @@ from django.apps import apps
|
|||
|
||||
from treebeard.mp_tree import MP_NodeQuerySet
|
||||
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
from wagtail.wagtailsearch.queryset import SearchableQuerySetMixin
|
||||
|
||||
|
||||
class PageQuerySet(MP_NodeQuerySet):
|
||||
class PageQuerySet(SearchableQuerySetMixin, MP_NodeQuerySet):
|
||||
def live_q(self):
|
||||
return Q(live=True)
|
||||
|
||||
|
@ -197,13 +197,6 @@ class PageQuerySet(MP_NodeQuerySet):
|
|||
"""
|
||||
return self.exclude(self.public_q())
|
||||
|
||||
def search(self, query_string, fields=None, operator=None, backend='default'):
|
||||
"""
|
||||
This runs a search query on all the pages in the QuerySet
|
||||
"""
|
||||
search_backend = get_search_backend(backend)
|
||||
return search_backend.search(query_string, self, fields=fields, operator=operator)
|
||||
|
||||
def unpublish(self):
|
||||
"""
|
||||
This unpublishes all pages in the QuerySet
|
||||
|
|
|
@ -322,6 +322,46 @@ class TestPageQuerySet(TestCase):
|
|||
self.assertTrue(pages.filter(id=event.id).exists())
|
||||
|
||||
|
||||
class TestPageQuerySetSearch(TestCase):
|
||||
fixtures = ['test.json']
|
||||
|
||||
def test_search(self):
|
||||
pages = EventPage.objects.search('moon', fields=['location'])
|
||||
|
||||
self.assertEqual(pages.count(), 2)
|
||||
self.assertIn(Page.objects.get(url_path='/home/events/tentative-unpublished-event/').specific, pages)
|
||||
self.assertIn(Page.objects.get(url_path='/home/events/someone-elses-event/').specific, pages)
|
||||
|
||||
def test_operators(self):
|
||||
results = EventPage.objects.search("moon ponies", operator='and')
|
||||
|
||||
self.assertEqual(list(results), [
|
||||
Page.objects.get(url_path='/home/events/tentative-unpublished-event/').specific
|
||||
])
|
||||
|
||||
results = EventPage.objects.search("moon ponies", operator='or')
|
||||
sorted_results = sorted(results, key=lambda page: page.url_path)
|
||||
self.assertEqual(sorted_results, [
|
||||
Page.objects.get(url_path='/home/events/someone-elses-event/').specific,
|
||||
Page.objects.get(url_path='/home/events/tentative-unpublished-event/').specific,
|
||||
])
|
||||
|
||||
def test_custom_order(self):
|
||||
pages = EventPage.objects.order_by('url_path').search('moon', fields=['location'], order_by_relevance=False)
|
||||
|
||||
self.assertEqual(list(pages), [
|
||||
Page.objects.get(url_path='/home/events/someone-elses-event/').specific,
|
||||
Page.objects.get(url_path='/home/events/tentative-unpublished-event/').specific,
|
||||
])
|
||||
|
||||
pages = EventPage.objects.order_by('-url_path').search('moon', fields=['location'], order_by_relevance=False)
|
||||
|
||||
self.assertEqual(list(pages), [
|
||||
Page.objects.get(url_path='/home/events/tentative-unpublished-event/').specific,
|
||||
Page.objects.get(url_path='/home/events/someone-elses-event/').specific,
|
||||
])
|
||||
|
||||
|
||||
class TestSpecificQuery(TestCase):
|
||||
"""
|
||||
Test the .specific() queryset method. This is isolated in its own test case
|
||||
|
|
|
@ -16,16 +16,11 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
from wagtail.wagtailadmin.taggable import TagSearchable
|
||||
from wagtail.wagtailadmin.utils import get_object_usage
|
||||
from wagtail.wagtailsearch import index
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
from wagtail.wagtailsearch.queryset import SearchableQuerySetMixin
|
||||
|
||||
|
||||
class DocumentQuerySet(models.QuerySet):
|
||||
def search(self, query_string, fields=None, operator=None, backend='default'):
|
||||
"""
|
||||
This runs a search query on all the documents in the QuerySet
|
||||
"""
|
||||
search_backend = get_search_backend(backend)
|
||||
return search_backend.search(query_string, self, fields=fields, operator=operator)
|
||||
class DocumentQuerySet(SearchableQuerySetMixin, models.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
|
|
|
@ -34,6 +34,26 @@ class TestDocumentQuerySet(TestCase):
|
|||
results = models.Document.objects.search("Test")
|
||||
self.assertEqual(list(results), [document])
|
||||
|
||||
def test_operators(self):
|
||||
aaa_document = models.Document.objects.create(title="AAA Test document")
|
||||
zzz_document = models.Document.objects.create(title="ZZZ Test document")
|
||||
|
||||
results = models.Document.objects.search("aaa test", operator='and')
|
||||
self.assertEqual(list(results), [aaa_document])
|
||||
|
||||
results = models.Document.objects.search("aaa test", operator='or')
|
||||
sorted_results = sorted(results, key=lambda doc: doc.title)
|
||||
self.assertEqual(sorted_results, [aaa_document, zzz_document])
|
||||
|
||||
def test_custom_ordering(self):
|
||||
aaa_document = models.Document.objects.create(title="AAA Test document")
|
||||
zzz_document = models.Document.objects.create(title="ZZZ Test document")
|
||||
|
||||
results = models.Document.objects.order_by('title').search("Test")
|
||||
self.assertEqual(list(results), [aaa_document, zzz_document])
|
||||
results = models.Document.objects.order_by('-title').search("Test")
|
||||
self.assertEqual(list(results), [zzz_document, aaa_document])
|
||||
|
||||
|
||||
class TestDocumentPermissions(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -28,7 +28,7 @@ from unidecode import unidecode
|
|||
from wagtail.wagtailcore import hooks
|
||||
from wagtail.wagtailadmin.taggable import TagSearchable
|
||||
from wagtail.wagtailsearch import index
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
from wagtail.wagtailsearch.queryset import SearchableQuerySetMixin
|
||||
from wagtail.wagtailimages.rect import Rect
|
||||
from wagtail.wagtailimages.exceptions import InvalidFilterSpecError
|
||||
from wagtail.wagtailadmin.utils import get_object_usage
|
||||
|
@ -42,13 +42,8 @@ class SourceImageIOError(IOError):
|
|||
pass
|
||||
|
||||
|
||||
class ImageQuerySet(models.QuerySet):
|
||||
def search(self, query_string, fields=None, operator=None, backend='default'):
|
||||
"""
|
||||
This runs a search query on all the images in the QuerySet
|
||||
"""
|
||||
search_backend = get_search_backend(backend)
|
||||
return search_backend.search(query_string, self, fields=fields, operator=operator)
|
||||
class ImageQuerySet(SearchableQuerySetMixin, models.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
def get_upload_to(instance, filename):
|
||||
|
|
|
@ -99,6 +99,38 @@ class TestImageQuerySet(TestCase):
|
|||
results = Image.objects.search("Test")
|
||||
self.assertEqual(list(results), [image])
|
||||
|
||||
def test_operators(self):
|
||||
aaa_image = Image.objects.create(
|
||||
title="AAA Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
zzz_image = Image.objects.create(
|
||||
title="ZZZ Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
results = Image.objects.search("aaa test", operator='and')
|
||||
self.assertEqual(list(results), [aaa_image])
|
||||
|
||||
results = Image.objects.search("aaa test", operator='or')
|
||||
sorted_results = sorted(results, key=lambda img: img.title)
|
||||
self.assertEqual(sorted_results, [aaa_image, zzz_image])
|
||||
|
||||
def test_custom_ordering(self):
|
||||
aaa_image = Image.objects.create(
|
||||
title="AAA Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
zzz_image = Image.objects.create(
|
||||
title="ZZZ Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
results = Image.objects.order_by('title').search("Test")
|
||||
self.assertEqual(list(results), [aaa_image, zzz_image])
|
||||
results = Image.objects.order_by('-title').search("Test")
|
||||
self.assertEqual(list(results), [zzz_image, aaa_image])
|
||||
|
||||
|
||||
class TestImagePermissions(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -18,11 +18,12 @@ class FieldError(Exception):
|
|||
class BaseSearchQuery(object):
|
||||
DEFAULT_OPERATOR = 'or'
|
||||
|
||||
def __init__(self, queryset, query_string, fields=None, operator=None):
|
||||
def __init__(self, queryset, query_string, fields=None, operator=None, order_by_relevance=True):
|
||||
self.queryset = queryset
|
||||
self.query_string = query_string
|
||||
self.fields = fields
|
||||
self.operator = operator or self.DEFAULT_OPERATOR
|
||||
self.order_by_relevance = order_by_relevance
|
||||
|
||||
def _get_searchable_field(self, field_attname):
|
||||
# Get field
|
||||
|
@ -203,7 +204,7 @@ class BaseSearch(object):
|
|||
def delete(self, obj):
|
||||
raise NotImplementedError
|
||||
|
||||
def search(self, query_string, model_or_queryset, fields=None, filters=None, prefetch_related=None, operator=None):
|
||||
def search(self, query_string, model_or_queryset, fields=None, filters=None, prefetch_related=None, operator=None, order_by_relevance=True):
|
||||
# Find model/queryset
|
||||
if isinstance(model_or_queryset, QuerySet):
|
||||
model = model_or_queryset.model
|
||||
|
@ -236,5 +237,5 @@ class BaseSearch(object):
|
|||
raise ValueError("operator must be either 'or' or 'and'")
|
||||
|
||||
# Search
|
||||
search_query = self.search_query_class(queryset, query_string, fields=fields, operator=operator)
|
||||
search_query = self.search_query_class(queryset, query_string, fields=fields, operator=operator, order_by_relevance=order_by_relevance)
|
||||
return self.search_results_class(self, search_query)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import six
|
||||
import warnings
|
||||
|
||||
from django.utils.six.moves.urllib.parse import urlparse
|
||||
|
||||
|
@ -11,6 +13,7 @@ from django.utils.crypto import get_random_string
|
|||
|
||||
from wagtail.wagtailsearch.backends.base import BaseSearch, BaseSearchQuery, BaseSearchResults
|
||||
from wagtail.wagtailsearch.index import SearchField, FilterField, class_is_indexed
|
||||
from wagtail.utils.deprecation import RemovedInWagtail14Warning
|
||||
|
||||
|
||||
INDEX_SETTINGS = {
|
||||
|
@ -245,8 +248,7 @@ class ElasticSearchQuery(BaseSearchQuery):
|
|||
|
||||
return filter_out
|
||||
|
||||
def to_es(self):
|
||||
# Query
|
||||
def get_inner_query(self):
|
||||
if self.query_string is not None:
|
||||
fields = self.fields or ['_all', '_partials']
|
||||
|
||||
|
@ -274,7 +276,9 @@ class ElasticSearchQuery(BaseSearchQuery):
|
|||
'match_all': {}
|
||||
}
|
||||
|
||||
# Filters
|
||||
return query
|
||||
|
||||
def get_filters(self):
|
||||
filters = []
|
||||
|
||||
# Filter by content type
|
||||
|
@ -289,35 +293,106 @@ class ElasticSearchQuery(BaseSearchQuery):
|
|||
if queryset_filters:
|
||||
filters.append(queryset_filters)
|
||||
|
||||
return filters
|
||||
|
||||
def get_query(self):
|
||||
inner_query = self.get_inner_query()
|
||||
filters = self.get_filters()
|
||||
|
||||
if len(filters) == 1:
|
||||
query = {
|
||||
return {
|
||||
'filtered': {
|
||||
'query': query,
|
||||
'query': inner_query,
|
||||
'filter': filters[0],
|
||||
}
|
||||
}
|
||||
elif len(filters) > 1:
|
||||
query = {
|
||||
return {
|
||||
'filtered': {
|
||||
'query': query,
|
||||
'query': inner_query,
|
||||
'filter': {
|
||||
'and': filters,
|
||||
}
|
||||
}
|
||||
}
|
||||
else:
|
||||
return inner_query
|
||||
|
||||
return query
|
||||
def get_sort(self):
|
||||
# Ordering by relevance is the default in Elasticsearch
|
||||
if self.order_by_relevance:
|
||||
return
|
||||
|
||||
# Get queryset and make sure its ordered
|
||||
if self.queryset.ordered:
|
||||
order_by_fields = self.queryset.query.order_by
|
||||
sort = []
|
||||
|
||||
for order_by_field in order_by_fields:
|
||||
reverse = False
|
||||
field_name = order_by_field
|
||||
|
||||
if order_by_field.startswith('-'):
|
||||
reverse = True
|
||||
field_name = order_by_field[1:]
|
||||
|
||||
field = self._get_filterable_field(field_name)
|
||||
field_index_name = field.get_index_name(self.queryset.model)
|
||||
|
||||
sort.append({
|
||||
field_index_name: 'desc' if reverse else 'asc'
|
||||
})
|
||||
|
||||
return sort
|
||||
|
||||
else:
|
||||
# Order by pk field
|
||||
return ['pk']
|
||||
|
||||
def __repr__(self):
|
||||
return json.dumps(self.to_es())
|
||||
return json.dumps(self.get_query())
|
||||
|
||||
def to_es(self):
|
||||
warnings.warn(
|
||||
"The ElasticSearchQuery.to_es() method is deprecated. "
|
||||
"Please use the ElasticSearchQuery.get_query() method instead.",
|
||||
RemovedInWagtail14Warning, stacklevel=2)
|
||||
|
||||
return self.get_query()
|
||||
|
||||
|
||||
class ElasticSearchResults(BaseSearchResults):
|
||||
def _get_es_body(self, for_count=False):
|
||||
# If to_es has been overridden, call it and raise a deprecation warning
|
||||
if isinstance(self.query, ElasticSearchQuery) and six.get_method_function(self.query.to_es) != ElasticSearchQuery.to_es:
|
||||
warnings.warn(
|
||||
"The .to_es() method on Elasticsearch query classes is deprecated. "
|
||||
"Please rename {class_name}.to_es() to {class_name}.get_query()".format(
|
||||
class_name=self.query.__class__.__name__
|
||||
),
|
||||
RemovedInWagtail14Warning, stacklevel=2)
|
||||
|
||||
body = {
|
||||
'query': self.query.to_es(),
|
||||
}
|
||||
else:
|
||||
body = {
|
||||
'query': self.query.get_query()
|
||||
}
|
||||
|
||||
if not for_count:
|
||||
sort = self.query.get_sort()
|
||||
|
||||
if sort is not None:
|
||||
body['sort'] = sort
|
||||
|
||||
return body
|
||||
|
||||
def _do_search(self):
|
||||
# Params for elasticsearch query
|
||||
params = dict(
|
||||
index=self.backend.es_index,
|
||||
body=dict(query=self.query.to_es()),
|
||||
body=self._get_es_body(),
|
||||
_source=False,
|
||||
fields='pk',
|
||||
from_=self.start,
|
||||
|
@ -345,13 +420,10 @@ class ElasticSearchResults(BaseSearchResults):
|
|||
return [results[str(pk)] for pk in pks if results[str(pk)]]
|
||||
|
||||
def _do_count(self):
|
||||
# Get query
|
||||
query = self.query.to_es()
|
||||
|
||||
# Get count
|
||||
hit_count = self.backend.es.count(
|
||||
index=self.backend.es_index,
|
||||
body=dict(query=query),
|
||||
body=self._get_es_body(for_count=True),
|
||||
)['count']
|
||||
|
||||
# Add limits
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
|
||||
|
||||
class SearchableQuerySetMixin(object):
|
||||
def search(self, query_string, fields=None,
|
||||
operator=None, order_by_relevance=True, backend='default'):
|
||||
"""
|
||||
This runs a search query on all the items in the QuerySet
|
||||
"""
|
||||
search_backend = get_search_backend(backend)
|
||||
return search_backend.search(query_string, self, fields=fields,
|
||||
operator=operator, order_by_relevance=order_by_relevance)
|
|
@ -167,6 +167,38 @@ class TestElasticSearchBackend(BackendTests, TestCase):
|
|||
# Should find the result
|
||||
self.assertEqual(len(results), 1)
|
||||
|
||||
def test_custom_ordering(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
|
||||
# Add some test data
|
||||
# a is more relevant, but b is more recent
|
||||
a = models.SearchTest()
|
||||
a.title = "Hello Hello World"
|
||||
a.live = True
|
||||
a.published_date = datetime.date(2015, 10, 11)
|
||||
a.save()
|
||||
self.backend.add(a)
|
||||
|
||||
b = models.SearchTest()
|
||||
b.title = "Hello World"
|
||||
b.live = True
|
||||
b.published_date = datetime.date(2015, 10, 12)
|
||||
b.save()
|
||||
self.backend.add(b)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
|
||||
# Do a search ordered by relevence
|
||||
results = self.backend.search("Hello", models.SearchTest.objects.all())
|
||||
self.assertEqual(list(results), [a, b])
|
||||
|
||||
# Do a search ordered by published date
|
||||
results = self.backend.search("Hello", models.SearchTest.objects.order_by('-published_date'), order_by_relevance=False)
|
||||
self.assertEqual(list(results), [b, a])
|
||||
|
||||
|
||||
class TestElasticSearchQuery(TestCase):
|
||||
def assertDictEqual(self, a, b):
|
||||
|
@ -191,7 +223,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'prefix': {'content_type': 'searchtests_searchtest'}}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_none_query_string(self):
|
||||
# Create a query
|
||||
|
@ -199,7 +231,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'prefix': {'content_type': 'searchtests_searchtest'}}, 'query': {'match_all': {}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_and_operator(self):
|
||||
# Create a query
|
||||
|
@ -207,7 +239,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'prefix': {'content_type': 'searchtests_searchtest'}}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials'], 'operator': 'and'}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_filter(self):
|
||||
# Create a query
|
||||
|
@ -215,7 +247,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'term': {'title_filter': 'Test'}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_and_filter(self):
|
||||
# Create a query
|
||||
|
@ -225,7 +257,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'and': [{'term': {'live_filter': True}}, {'term': {'title_filter': 'Test'}}]}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
|
||||
# Make sure field filters are sorted (as they can be in any order which may cause false positives)
|
||||
query = query.to_es()
|
||||
query = query.get_query()
|
||||
field_filters = query['filtered']['filter']['and'][1]['and']
|
||||
field_filters[:] = sorted(field_filters, key=lambda f: list(f['term'].keys())[0])
|
||||
|
||||
|
@ -236,7 +268,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
query = self.ElasticSearchQuery(models.SearchTest.objects.filter(Q(title="Test") | Q(live=True)), "Hello")
|
||||
|
||||
# Make sure field filters are sorted (as they can be in any order which may cause false positives)
|
||||
query = query.to_es()
|
||||
query = query.get_query()
|
||||
field_filters = query['filtered']['filter']['and'][1]['or']
|
||||
field_filters[:] = sorted(field_filters, key=lambda f: list(f['term'].keys())[0])
|
||||
|
||||
|
@ -250,7 +282,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'not': {'term': {'live_filter': True}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_fields(self):
|
||||
# Create a query
|
||||
|
@ -258,7 +290,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'prefix': {'content_type': 'searchtests_searchtest'}}, 'query': {'match': {'title': 'Hello'}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_fields_with_and_operator(self):
|
||||
# Create a query
|
||||
|
@ -266,7 +298,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'prefix': {'content_type': 'searchtests_searchtest'}}, 'query': {'match': {'title': 'Hello', 'operator': 'and'}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_exact_lookup(self):
|
||||
# Create a query
|
||||
|
@ -274,7 +306,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'term': {'title_filter': 'Test'}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_none_lookup(self):
|
||||
# Create a query
|
||||
|
@ -282,7 +314,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'missing': {'field': 'title_filter'}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_isnull_true_lookup(self):
|
||||
# Create a query
|
||||
|
@ -290,7 +322,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'missing': {'field': 'title_filter'}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_isnull_false_lookup(self):
|
||||
# Create a query
|
||||
|
@ -298,7 +330,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'not': {'missing': {'field': 'title_filter'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_startswith_lookup(self):
|
||||
# Create a query
|
||||
|
@ -306,7 +338,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'prefix': {'title_filter': 'Test'}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_gt_lookup(self):
|
||||
# This also tests conversion of python dates to strings
|
||||
|
@ -316,7 +348,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'range': {'published_date_filter': {'gt': '2014-04-29'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_lt_lookup(self):
|
||||
# Create a query
|
||||
|
@ -324,7 +356,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'range': {'published_date_filter': {'lt': '2014-04-29'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_gte_lookup(self):
|
||||
# Create a query
|
||||
|
@ -332,7 +364,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'range': {'published_date_filter': {'gte': '2014-04-29'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_lte_lookup(self):
|
||||
# Create a query
|
||||
|
@ -340,7 +372,7 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'range': {'published_date_filter': {'lte': '2014-04-29'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_range_lookup(self):
|
||||
start_date = datetime.datetime(2014, 4, 29)
|
||||
|
@ -351,7 +383,31 @@ class TestElasticSearchQuery(TestCase):
|
|||
|
||||
# Check it
|
||||
expected_result = {'filtered': {'filter': {'and': [{'prefix': {'content_type': 'searchtests_searchtest'}}, {'range': {'published_date_filter': {'gte': '2014-04-29', 'lte': '2014-08-19'}}}]}, 'query': {'multi_match': {'query': 'Hello', 'fields': ['_all', '_partials']}}}}
|
||||
self.assertDictEqual(query.to_es(), expected_result)
|
||||
self.assertDictEqual(query.get_query(), expected_result)
|
||||
|
||||
def test_custom_ordering(self):
|
||||
# Create a query
|
||||
query = self.ElasticSearchQuery(models.SearchTest.objects.order_by('published_date'), "Hello", order_by_relevance=False)
|
||||
|
||||
# Check it
|
||||
expected_result = [{'published_date_filter': 'asc'}]
|
||||
self.assertDictEqual(query.get_sort(), expected_result)
|
||||
|
||||
def test_custom_ordering_reversed(self):
|
||||
# Create a query
|
||||
query = self.ElasticSearchQuery(models.SearchTest.objects.order_by('-published_date'), "Hello", order_by_relevance=False)
|
||||
|
||||
# Check it
|
||||
expected_result = [{'published_date_filter': 'desc'}]
|
||||
self.assertDictEqual(query.get_sort(), expected_result)
|
||||
|
||||
def test_custom_ordering_multiple(self):
|
||||
# Create a query
|
||||
query = self.ElasticSearchQuery(models.SearchTest.objects.order_by('published_date', 'live'), "Hello", order_by_relevance=False)
|
||||
|
||||
# Check it
|
||||
expected_result = [{'published_date_filter': 'asc'}, {'live_filter': 'asc'}]
|
||||
self.assertDictEqual(query.get_sort(), expected_result)
|
||||
|
||||
|
||||
class TestElasticSearchResults(TestCase):
|
||||
|
@ -382,7 +438,8 @@ class TestElasticSearchResults(TestCase):
|
|||
backend = self.ElasticSearch({})
|
||||
query = mock.MagicMock()
|
||||
query.queryset = models.SearchTest.objects.all()
|
||||
query.to_es.return_value = 'QUERY'
|
||||
query.get_query.return_value = 'QUERY'
|
||||
query.get_sort.return_value = None
|
||||
return self.ElasticSearchResults(backend, query)
|
||||
|
||||
def construct_search_response(self, results):
|
||||
|
|
Ładowanie…
Reference in New Issue