kopia lustrzana https://github.com/wagtail/wagtail
Elasticsearch 2 support (#2573)
* Created Elasticsearch 2 backend * Added tests for Elasticsearch 2 backend * Split models up into different indices pages, images and documents are now in separate indices * Prefix fields of child models to prevent mapping clashes * Replaced index_analyzer with analyzer/search_analyzer index_analyzer has been removed in Elasticsearch 2.0 https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking_20_mapping_changes.html#_analyzer_mappings There's no indication in Elasticsearch's docs that this wouldn't work on Elasticsearch 1.x. However, we found that the new configuration isn't reliable on Elasticsearch 1.6 and below (causes the test_query_analyzer test to fail randomly). * Implemented new way of representing content types in search index Instead of using a long string of model names that is queried using a "prefix" query, we instead use a multi-value string field and query it using a simple "match" query. The only reason why this isn't implemented in the Elasticsearch 1.x backend yet is backwards compatibility * Added another child model of SearchTest with clashing field mapping This checks that the namespacing of fields on child models is working properly (if it doesn't the update_index tests will fail) * Added tests for get_model_root function * fixup! Added tests for get_model_root function * Docs updates for Elasticsearch 2 support Also tweak examples to use elasticsearch2 backend by default * Test against Elasticsearch 2 on travispull/2966/head
rodzic
68d19d1efb
commit
daa82936d7
22
.travis.yml
22
.travis.yml
|
@ -32,6 +32,15 @@ matrix:
|
|||
python: 2.7
|
||||
- env: TOXENV=py34-dj19-sqlite-elasticsearch
|
||||
python: 3.5
|
||||
- env: TOXENV=py27-dj18-sqlite-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
python: 2.7
|
||||
sudo: true
|
||||
- env: TOXENV=py27-dj19-postgres-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
python: 2.7
|
||||
sudo: true
|
||||
- env: TOXENV=py34-dj19-sqlite-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
python: 3.5
|
||||
sudo: true
|
||||
- env: TOXENV=py27-dj110-sqlite-noelasticsearch
|
||||
python: 2.7
|
||||
- env: TOXENV=py27-dj110-postgres-noelasticsearch
|
||||
|
@ -40,6 +49,9 @@ matrix:
|
|||
python: 2.7
|
||||
- env: TOXENV=py27-dj110-mysql-elasticsearch
|
||||
python: 2.7
|
||||
- env: TOXENV=py27-dj110-mysql-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
python: 2.7
|
||||
sudo: true
|
||||
- env: TOXENV=py34-dj110-postgres-noelasticsearch
|
||||
python: 3.4
|
||||
- env: TOXENV=py34-dj110-sqlite-noelasticsearch
|
||||
|
@ -54,12 +66,21 @@ matrix:
|
|||
python: 3.5
|
||||
- env: TOXENV=py35-dj110-postgres-elasticsearch
|
||||
python: 3.5
|
||||
- env: TOXENV=py35-dj110-postgres-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
python: 3.5
|
||||
sudo: true
|
||||
allow_failures:
|
||||
- env: TOXENV=py27-dj18-sqlite-elasticsearch
|
||||
- env: TOXENV=py27-dj19-postgres-elasticsearch
|
||||
- env: TOXENV=py34-dj19-sqlite-elasticsearch
|
||||
- env: TOXENV=py27-dj110-mysql-elasticsearch
|
||||
- env: TOXENV=py35-dj110-postgres-elasticsearch
|
||||
- env: TOXENV=py27-dj18-sqlite-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
- env: TOXENV=py27-dj19-postgres-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
- env: TOXENV=py34-dj19-sqlite-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
- env: TOXENV=py27-dj110-mysql-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
- env: TOXENV=py35-dj110-postgres-elasticsearch2 INSTALL_ELASTICSEARCH2=yes
|
||||
|
||||
|
||||
# Services
|
||||
services:
|
||||
|
@ -68,6 +89,7 @@ services:
|
|||
# Package installation
|
||||
install:
|
||||
- pip install tox coveralls
|
||||
- 'if [[ -n "$INSTALL_ELASTICSEARCH2" ]]; then ./scripts/travis/install_elasticsearch2.sh; fi'
|
||||
|
||||
# Pre-test configuration
|
||||
before_script:
|
||||
|
|
|
@ -185,7 +185,7 @@ Search
|
|||
# Replace the search backend
|
||||
WAGTAILSEARCH_BACKENDS = {
|
||||
'default': {
|
||||
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch',
|
||||
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch2',
|
||||
'INDEX': 'myapp'
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ URL Patterns
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^django-admin/', include(admin.site.urls)),
|
||||
|
||||
|
||||
url(r'^admin/', include(wagtailadmin_urls)),
|
||||
url(r'^search/', include(wagtailsearch_urls)),
|
||||
url(r'^documents/', include(wagtaildocs_urls)),
|
||||
|
@ -578,7 +578,7 @@ These two files should reside in your project directory (``myproject/myproject/`
|
|||
# Replace the search backend
|
||||
#WAGTAILSEARCH_BACKENDS = {
|
||||
# 'default': {
|
||||
# 'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch',
|
||||
# 'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch2',
|
||||
# 'INDEX': 'myapp'
|
||||
# }
|
||||
#}
|
||||
|
|
|
@ -82,20 +82,22 @@ If any of these features are important to you, we recommend using Elasticsearch
|
|||
Elasticsearch Backend
|
||||
---------------------
|
||||
|
||||
``wagtail.wagtailsearch.backends.elasticsearch``
|
||||
``wagtail.wagtailsearch.backends.elasticsearch`` (Elasticsearch 1.x)
|
||||
|
||||
``wagtail.wagtailsearch.backends.elasticsearch2`` (Elasticsearch 2.x)
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
|
||||
Before 1.1, the full path to the backend class had to be specified: ``wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch``
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
|
||||
Support for Elasticsearch 2.x was added
|
||||
|
||||
Prerequisites are the `Elasticsearch`_ service itself and, via pip, the `elasticsearch-py`_ package:
|
||||
|
||||
.. _Elasticsearch: https://www.elastic.co/downloads/past-releases/elasticsearch-1-7-3
|
||||
|
||||
.. note::
|
||||
|
||||
Wagtail doesn't support Elasticsearch 2.0 yet; please use 1.x in the meantime. Elasticsearch 2.0 support is scheduled for a future release.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install elasticsearch
|
||||
|
@ -106,7 +108,7 @@ The backend is configured in settings:
|
|||
|
||||
WAGTAILSEARCH_BACKENDS = {
|
||||
'default': {
|
||||
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch',
|
||||
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch2',
|
||||
'URLS': ['http://localhost:9200'],
|
||||
'INDEX': 'wagtail',
|
||||
'TIMEOUT': 5,
|
||||
|
|
|
@ -17,6 +17,7 @@ def make_parser():
|
|||
parser.add_argument('--deprecation', choices=['all', 'pending', 'imminent', 'none'], default='pending')
|
||||
parser.add_argument('--postgres', action='store_true')
|
||||
parser.add_argument('--elasticsearch', action='store_true')
|
||||
parser.add_argument('--elasticsearch2', action='store_true')
|
||||
parser.add_argument('rest', nargs='*')
|
||||
return parser
|
||||
|
||||
|
@ -49,6 +50,13 @@ def runtests():
|
|||
|
||||
if args.elasticsearch:
|
||||
os.environ.setdefault('ELASTICSEARCH_URL', 'http://localhost:9200')
|
||||
os.environ.setdefault('ELASTICSEARCH_VERSION', '1')
|
||||
|
||||
if args.elasticsearch2:
|
||||
raise RuntimeError("You cannot test both Elasticsearch 1 and 2 together")
|
||||
elif args.elasticsearch2:
|
||||
os.environ.setdefault('ELASTICSEARCH_URL', 'http://localhost:9200')
|
||||
os.environ.setdefault('ELASTICSEARCH_VERSION', '2')
|
||||
elif 'ELASTICSEARCH_URL' in os.environ:
|
||||
# forcibly delete the ELASTICSEARCH_URL setting to skip those tests
|
||||
del os.environ['ELASTICSEARCH_URL']
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
sudo apt-get autoremove --purge elasticsearch
|
||||
wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
|
||||
echo "deb http://packages.elastic.co/elasticsearch/2.x/debian stable main" | sudo tee -a /etc/apt/sources.list.d/elk.list
|
||||
sudo apt-get update && sudo apt-get install elasticsearch -y
|
||||
sudo service elasticsearch start
|
6
tox.ini
6
tox.ini
|
@ -2,14 +2,15 @@
|
|||
skipsdist = True
|
||||
usedevelop = True
|
||||
|
||||
envlist = py{27,33,34,35}-dj{18,19}-{sqlite,postgres,mysql}-{elasticsearch,noelasticsearch},
|
||||
py{27,34,35}-dj110-{sqlite,postgres,mysql}-{elasticsearch,noelasticsearch},
|
||||
envlist = py{27,33,34,35}-dj{18,19}-{sqlite,postgres,mysql}-{elasticsearch2,elasticsearch,noelasticsearch},
|
||||
py{27,34,35}-dj110-{sqlite,postgres,mysql}-{elasticsearch2,elasticsearch,noelasticsearch},
|
||||
flake8
|
||||
|
||||
[testenv]
|
||||
install_command = pip install -e ".[testing]" -U {opts} {packages}
|
||||
commands =
|
||||
elasticsearch: coverage run runtests.py wagtail.wagtailsearch wagtail.wagtaildocs wagtail.wagtailimages --elasticsearch
|
||||
elasticsearch2: coverage run runtests.py wagtail.wagtailsearch wagtail.wagtaildocs wagtail.wagtailimages --elasticsearch2
|
||||
noelasticsearch: coverage run runtests.py
|
||||
|
||||
basepython =
|
||||
|
@ -27,6 +28,7 @@ deps =
|
|||
dj110: Django>=1.10a1,<1.11
|
||||
postgres: psycopg2>=2.6
|
||||
mysql: mysqlclient==1.3.6
|
||||
elasticsearch2: elasticsearch>=2,<3
|
||||
|
||||
setenv =
|
||||
postgres: DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-08-25 15:17
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('searchtests', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AnotherSearchTestChild',
|
||||
fields=[
|
||||
('searchtest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='searchtests.SearchTest')),
|
||||
('subtitle', models.CharField(blank=True, max_length=255, null=True)),
|
||||
],
|
||||
bases=('searchtests.searchtest',),
|
||||
),
|
||||
]
|
|
@ -69,3 +69,13 @@ class SearchTestChild(SearchTest):
|
|||
index.FilterField('live'),
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
class AnotherSearchTestChild(SearchTest):
|
||||
# Checks that having the same field name in two child models with different
|
||||
# search configuration doesn't give an error
|
||||
subtitle = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
||||
search_fields = SearchTest.search_fields + [
|
||||
index.SearchField('subtitle', boost=10),
|
||||
]
|
||||
|
|
|
@ -168,8 +168,13 @@ WAGTAILSEARCH_BACKENDS = {
|
|||
AUTH_USER_MODEL = 'customuser.CustomUser'
|
||||
|
||||
if 'ELASTICSEARCH_URL' in os.environ:
|
||||
if os.environ.get('ELASTICSEARCH_VERSION') == '2':
|
||||
backend = 'wagtail.wagtailsearch.backends.elasticsearch2'
|
||||
else:
|
||||
backend = 'wagtail.wagtailsearch.backends.elasticsearch'
|
||||
|
||||
WAGTAILSEARCH_BACKENDS['elasticsearch'] = {
|
||||
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch',
|
||||
'BACKEND': backend,
|
||||
'URLS': [os.environ['ELASTICSEARCH_URL']],
|
||||
'TIMEOUT': 10,
|
||||
'max_retries': 1,
|
||||
|
|
|
@ -12,7 +12,8 @@ from elasticsearch.helpers import bulk
|
|||
from wagtail.utils.deprecation import RemovedInWagtail18Warning
|
||||
from wagtail.wagtailsearch.backends.base import (
|
||||
BaseSearchBackend, BaseSearchQuery, BaseSearchResults)
|
||||
from wagtail.wagtailsearch.index import FilterField, RelatedFields, SearchField, class_is_indexed
|
||||
from wagtail.wagtailsearch.index import (
|
||||
FilterField, Indexed, RelatedFields, SearchField, class_is_indexed)
|
||||
|
||||
|
||||
class ElasticsearchMapping(object):
|
||||
|
@ -42,9 +43,21 @@ class ElasticsearchMapping(object):
|
|||
'TimeField': 'date',
|
||||
}
|
||||
|
||||
# Contains the configuration required to use the edgengram_analyzer
|
||||
# on a field. It's different in Elasticsearch 2 so it's been put in
|
||||
# an attribute here to make it easier to override in a subclass.
|
||||
edgengram_analyzer_config = {
|
||||
'index_analyzer': 'edgengram_analyzer',
|
||||
}
|
||||
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
def get_parent(self):
|
||||
for base in self.model.__bases__:
|
||||
if issubclass(base, Indexed) and issubclass(base, models.Model):
|
||||
return type(self)(base)
|
||||
|
||||
def get_document_type(self):
|
||||
return self.model.indexed_get_content_type()
|
||||
|
||||
|
@ -75,7 +88,7 @@ class ElasticsearchMapping(object):
|
|||
mapping['boost'] = field.boost
|
||||
|
||||
if field.partial_match:
|
||||
mapping['index_analyzer'] = 'edgengram_analyzer'
|
||||
mapping.update(self.edgengram_analyzer_config)
|
||||
|
||||
mapping['include_in_all'] = True
|
||||
|
||||
|
@ -94,8 +107,9 @@ class ElasticsearchMapping(object):
|
|||
fields = {
|
||||
'pk': dict(type='string', index='not_analyzed', store='yes', include_in_all=False),
|
||||
'content_type': dict(type='string', index='not_analyzed', include_in_all=False),
|
||||
'_partials': dict(type='string', index_analyzer='edgengram_analyzer', include_in_all=False),
|
||||
'_partials': dict(type='string', include_in_all=False),
|
||||
}
|
||||
fields['_partials'].update(self.edgengram_analyzer_config)
|
||||
|
||||
fields.update(dict(
|
||||
self.get_field_mapping(field) for field in self.model.get_search_fields()
|
||||
|
@ -305,15 +319,18 @@ class ElasticsearchSearchQuery(BaseSearchQuery):
|
|||
|
||||
return query
|
||||
|
||||
def get_content_type_filter(self):
|
||||
return {
|
||||
'prefix': {
|
||||
'content_type': self.queryset.model.indexed_get_content_type()
|
||||
}
|
||||
}
|
||||
|
||||
def get_filters(self):
|
||||
filters = []
|
||||
|
||||
# Filter by content type
|
||||
filters.append({
|
||||
'prefix': {
|
||||
'content_type': self.queryset.model.indexed_get_content_type()
|
||||
}
|
||||
})
|
||||
filters.append(self.get_content_type_filter())
|
||||
|
||||
# Apply filters from queryset
|
||||
queryset_filters = self._get_filters_from_queryset()
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from wagtail.wagtailsearch.index import FilterField, RelatedFields, SearchField
|
||||
|
||||
from .elasticsearch import (
|
||||
ElasticsearchIndex, ElasticsearchMapping, ElasticsearchSearchBackend, ElasticsearchSearchQuery,
|
||||
ElasticsearchSearchResults)
|
||||
|
||||
|
||||
def get_model_root(model):
|
||||
"""
|
||||
This function finds the root model for any given model. The root model is
|
||||
the highest concrete model that it descends from. If the model doesn't
|
||||
descend from another concrete model then the model is it's own root model so
|
||||
it is returned.
|
||||
|
||||
Examples:
|
||||
>>> get_model_root(wagtailcore.Page)
|
||||
wagtailcore.Page
|
||||
|
||||
>>> get_model_root(myapp.HomePage)
|
||||
wagtailcore.Page
|
||||
|
||||
>>> get_model_root(wagtailimages.Image)
|
||||
wagtailimages.Image
|
||||
"""
|
||||
if model._meta.parents:
|
||||
parent_model = list(model._meta.parents.items())[0][0]
|
||||
return get_model_root(parent_model)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
class Elasticsearch2Mapping(ElasticsearchMapping):
|
||||
edgengram_analyzer_config = {
|
||||
'analyzer': 'edgengram_analyzer',
|
||||
'search_analyzer': 'standard',
|
||||
}
|
||||
|
||||
def get_field_column_name(self, field):
|
||||
# Fields in derived models get prefixed with their model name, fields
|
||||
# in the root model don't get prefixed at all
|
||||
# This is to prevent mapping clashes in cases where two page types have
|
||||
# a field with the same name but a different type.
|
||||
root_model = get_model_root(self.model)
|
||||
definition_model = field.get_definition_model(self.model)
|
||||
|
||||
if definition_model != root_model:
|
||||
prefix = definition_model._meta.app_label.lower() + '_' + definition_model.__name__.lower() + '__'
|
||||
else:
|
||||
prefix = ''
|
||||
|
||||
if isinstance(field, FilterField):
|
||||
return prefix + field.get_attname(self.model) + '_filter'
|
||||
elif isinstance(field, SearchField):
|
||||
return prefix + field.get_attname(self.model)
|
||||
elif isinstance(field, RelatedFields):
|
||||
return prefix + field.field_name
|
||||
|
||||
def get_content_type(self):
|
||||
"""
|
||||
Returns the content type as a string for the model.
|
||||
|
||||
For example: "wagtailcore.Page"
|
||||
"myapp.MyModel"
|
||||
"""
|
||||
return self.model._meta.app_label + '.' + self.model.__name__
|
||||
|
||||
def get_all_content_types(self):
|
||||
"""
|
||||
Returns all the content type strings that apply to this model.
|
||||
This includes the models' content type and all concrete ancestor
|
||||
models that inherit from Indexed.
|
||||
|
||||
For example: ["myapp.MyPageModel", "wagtailcore.Page"]
|
||||
["myapp.MyModel"]
|
||||
"""
|
||||
# Add our content type
|
||||
content_types = [self.get_content_type()]
|
||||
|
||||
# Add all ancestor classes content types as well
|
||||
ancestor = self.get_parent()
|
||||
while ancestor:
|
||||
content_types.append(ancestor.get_content_type())
|
||||
ancestor = ancestor.get_parent()
|
||||
|
||||
return content_types
|
||||
|
||||
def get_document(self, obj):
|
||||
# In the Elasticsearch 2 backend, we use a more efficient way to
|
||||
# represent the content type of a document.
|
||||
|
||||
# Instead of using a long string of model names that is queried using a
|
||||
# "prefix" query, we instead use a multi-value string field and query it
|
||||
# using a simple "match" query.
|
||||
|
||||
# The only reason why this isn't implemented in the Elasticsearch 1.x
|
||||
# backend yet is backwards compatibility
|
||||
doc = super(Elasticsearch2Mapping, self).get_document(obj)
|
||||
doc['content_type'] = self.get_all_content_types()
|
||||
return doc
|
||||
|
||||
|
||||
class Elasticsearch2Index(ElasticsearchIndex):
|
||||
pass
|
||||
|
||||
|
||||
class Elasticsearch2SearchQuery(ElasticsearchSearchQuery):
|
||||
mapping_class = Elasticsearch2Mapping
|
||||
|
||||
def get_content_type_filter(self):
|
||||
# Query content_type using a "match" query. See comment in
|
||||
# Elasticsearch2Mapping.get_document for more details
|
||||
content_type = self.mapping_class(self.queryset.model).get_content_type()
|
||||
|
||||
return {
|
||||
'match': {
|
||||
'content_type': content_type
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Elasticsearch2SearchResults(ElasticsearchSearchResults):
|
||||
pass
|
||||
|
||||
|
||||
class Elasticsearch2SearchBackend(ElasticsearchSearchBackend):
|
||||
mapping_class = Elasticsearch2Mapping
|
||||
index_class = Elasticsearch2Index
|
||||
query_class = Elasticsearch2SearchQuery
|
||||
results_class = Elasticsearch2SearchResults
|
||||
|
||||
def get_index_for_model(self, model):
|
||||
# Split models up into separate indices based on their root model.
|
||||
# For example, all page-derived models get put together in one index,
|
||||
# while images and documents each have their own index.
|
||||
root_model = get_model_root(model)
|
||||
index_suffix = '__' + root_model._meta.app_label.lower() + '_' + root_model.__name__.lower()
|
||||
|
||||
return self.index_class(self, self.index_name + index_suffix)
|
||||
|
||||
|
||||
SearchBackend = Elasticsearch2SearchBackend
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
|
||||
from django.apps import apps
|
||||
|
@ -178,6 +179,16 @@ class BaseField(object):
|
|||
except models.fields.FieldDoesNotExist:
|
||||
return self.field_name
|
||||
|
||||
def get_definition_model(self, cls):
|
||||
try:
|
||||
field = self.get_field(cls)
|
||||
return field.model
|
||||
except models.fields.FieldDoesNotExist:
|
||||
# Find where it was defined by walking the inheritance tree
|
||||
for base_cls in inspect.getmro(cls):
|
||||
if self.field_name in base_cls.__dict__:
|
||||
return base_cls
|
||||
|
||||
def get_type(self, cls):
|
||||
if 'type' in self.kwargs:
|
||||
return self.kwargs['type']
|
||||
|
@ -224,6 +235,10 @@ class RelatedFields(object):
|
|||
def get_field(self, cls):
|
||||
return cls._meta.get_field(self.field_name)
|
||||
|
||||
def get_definition_model(self, cls):
|
||||
field = self.get_field(cls)
|
||||
return field.model
|
||||
|
||||
def get_value(self, obj):
|
||||
field = self.get_field(obj.__class__)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from wagtail.wagtailsearch.backends import (
|
|||
InvalidSearchBackendError, get_search_backend, get_search_backends)
|
||||
from wagtail.wagtailsearch.backends.base import FieldError
|
||||
from wagtail.wagtailsearch.backends.db import DatabaseSearchBackend
|
||||
from wagtail.wagtailsearch.management.commands.update_index import group_models_by_index
|
||||
|
||||
|
||||
class BackendTests(WagtailTestUtils):
|
||||
|
@ -35,11 +36,22 @@ class BackendTests(WagtailTestUtils):
|
|||
|
||||
self.load_test_data()
|
||||
|
||||
def reset_index(self):
|
||||
if self.backend.rebuilder_class:
|
||||
for index, indexed_models in group_models_by_index(self.backend, [models.SearchTest, models.SearchTestChild]).items():
|
||||
rebuilder = self.backend.rebuilder_class(index)
|
||||
index = rebuilder.start()
|
||||
for model in indexed_models:
|
||||
index.add_model(model)
|
||||
rebuilder.finish()
|
||||
|
||||
def refresh_index(self):
|
||||
index = self.backend.get_index_for_model(models.SearchTest)
|
||||
if index:
|
||||
index.refresh()
|
||||
|
||||
def load_test_data(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
self.reset_index()
|
||||
|
||||
# Create a test database
|
||||
testa = models.SearchTest()
|
||||
|
@ -71,8 +83,7 @@ class BackendTests(WagtailTestUtils):
|
|||
self.backend.add(testd)
|
||||
self.testd = testd
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
def test_blank_search(self):
|
||||
results = self.backend.search("", models.SearchTest)
|
||||
|
@ -148,14 +159,14 @@ class BackendTests(WagtailTestUtils):
|
|||
# Delete one of the objects
|
||||
self.backend.delete(self.testa)
|
||||
self.testa.delete()
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
results = self.backend.search(None, models.SearchTest)
|
||||
self.assertEqual(set(results), {self.testb, self.testc.searchtest_ptr, self.testd.searchtest_ptr})
|
||||
|
||||
def test_update_index_command(self):
|
||||
# Reset the index, this should clear out the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
|
||||
# Give Elasticsearch some time to catch up...
|
||||
time.sleep(1)
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -50,7 +50,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
|
||||
def test_partial_search(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
|
||||
|
@ -62,7 +62,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(obj)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Search and check
|
||||
results = self.backend.search("HelloW", models.SearchTest.objects.all())
|
||||
|
@ -72,7 +72,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
|
||||
def test_child_partial_search(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
|
||||
|
@ -84,7 +84,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(obj)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Search and check
|
||||
results = self.backend.search("HelloW", models.SearchTest.objects.all())
|
||||
|
@ -94,7 +94,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
|
||||
def test_ascii_folding(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
|
||||
|
@ -106,7 +106,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(obj)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Search and check
|
||||
results = self.backend.search("Hello", models.SearchTest.objects.all())
|
||||
|
@ -120,7 +120,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
have it also as their query analyser
|
||||
"""
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
|
||||
|
@ -132,7 +132,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(obj)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Test search for "Hello"
|
||||
results = self.backend.search("Hello", models.SearchTest.objects.all())
|
||||
|
@ -154,7 +154,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
See: https://github.com/torchbox/wagtail/issues/937
|
||||
"""
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
self.backend.add_type(models.SearchTestChild)
|
||||
|
||||
|
@ -166,7 +166,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(obj)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Test search for "Hello-World"
|
||||
results = self.backend.search("Hello-World", models.SearchTest.objects.all())
|
||||
|
@ -176,7 +176,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
|
||||
def test_custom_ordering(self):
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
|
||||
# Add some test data
|
||||
|
@ -196,7 +196,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(b)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Do a search ordered by relevence
|
||||
results = self.backend.search("Hello", models.SearchTest.objects.all())
|
||||
|
@ -212,7 +212,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
# Testing for bug #1859
|
||||
|
||||
# Reset the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
self.backend.add_type(models.SearchTest)
|
||||
|
||||
a = models.SearchTest()
|
||||
|
@ -223,7 +223,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
self.backend.add(a)
|
||||
|
||||
# Refresh the index
|
||||
self.backend.refresh_index()
|
||||
self.refresh_index()
|
||||
|
||||
# Run query with "and" operator and single field
|
||||
results = self.backend.search("Hello World", models.SearchTest, operator='and', fields=['title'])
|
||||
|
@ -231,7 +231,7 @@ class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
|||
|
||||
def test_update_index_command_schema_only(self):
|
||||
# Reset the index, this should clear out the index
|
||||
self.backend.reset_index()
|
||||
self.reset_index()
|
||||
|
||||
# Give Elasticsearch some time to catch up...
|
||||
time.sleep(1)
|
||||
|
@ -979,6 +979,7 @@ class TestBackendConfiguration(TestCase):
|
|||
|
||||
|
||||
@unittest.skipUnless(os.environ.get('ELASTICSEARCH_URL', False), "ELASTICSEARCH_URL not set")
|
||||
@unittest.skipUnless(os.environ.get('ELASTICSEARCH_VERSION', '1') == '1', "ELASTICSEARCH_VERSION not set to 1")
|
||||
class TestRebuilder(TestCase):
|
||||
def assertDictEqual(self, a, b):
|
||||
default = JSONSerializer().default
|
||||
|
@ -1025,6 +1026,7 @@ class TestRebuilder(TestCase):
|
|||
|
||||
|
||||
@unittest.skipUnless(os.environ.get('ELASTICSEARCH_URL', False), "ELASTICSEARCH_URL not set")
|
||||
@unittest.skipUnless(os.environ.get('ELASTICSEARCH_VERSION', '1') == '1', "ELASTICSEARCH_VERSION not set to 1")
|
||||
class TestAtomicRebuilder(TestCase):
|
||||
def setUp(self):
|
||||
self.backend = get_search_backend('elasticsearch')
|
||||
|
|
Ładowanie…
Reference in New Issue