[Python-modules-commits] [django-haystack] 01/07: Import django-haystack_2.6.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Tue Mar 21 12:15:03 UTC 2017


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch experimental
in repository django-haystack.

commit 94262c46ad4df6b0f7f23cf2478acc2ecf0c52fe
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Mon Jan 9 11:21:15 2017 +0100

    Import django-haystack_2.6.0.orig.tar.gz
---
 .travis.yml                                        |   28 +-
 AUTHORS                                            |    1 +
 README.rst                                         |    2 +-
 docs/backend_support.rst                           |    2 +-
 docs/changelog.rst                                 |  284 +++-
 docs/installing_search_engines.rst                 |    6 +-
 docs/management_commands.rst                       |   16 +-
 docs/searchqueryset_api.rst                        |    2 +-
 haystack/__init__.py                               |   10 +-
 haystack/backends/__init__.py                      |    2 +
 haystack/backends/elasticsearch2_backend.py        |  333 +++++
 haystack/fields.py                                 |    2 +-
 haystack/models.py                                 |    2 +-
 setup.py                                           |    4 +-
 test_haystack/core/custom_identifier.py            |    8 +-
 test_haystack/elasticsearch2_tests/__init__.py     |   29 +
 test_haystack/elasticsearch2_tests/test_backend.py | 1498 ++++++++++++++++++++
 test_haystack/elasticsearch2_tests/test_inputs.py  |   85 ++
 test_haystack/elasticsearch2_tests/test_query.py   |  209 +++
 test_haystack/elasticsearch_tests/__init__.py      |   14 +-
 test_haystack/settings.py                          |   10 +
 test_haystack/solr_tests/test_solr_backend.py      |    7 +
 test_haystack/test_managers.py                     |    2 +-
 test_haystack/test_utils.py                        |   14 +-
 tox.ini                                            |  184 ++-
 25 files changed, 2682 insertions(+), 72 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 48dd983..1b0b723 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,4 @@
-sudo: false
+sudo: true
 language: python
 python:
     - 2.7
@@ -26,10 +26,22 @@ addons:
 
 before_install:
     - mkdir -p $HOME/download-cache
+    # See https://www.elastic.co/guide/en/elasticsearch/reference/current/deb.html#deb-repo
+    - wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
+    - >
+      if [[ $VERSION_ES == '>=2.0.0,<3.0.0' ]];
+      then
+        echo "deb http://packages.elastic.co/elasticsearch/2.x/debian stable main" | sudo tee -a /etc/apt/sources.list.d/elasticsearch-2.x.list
+      else
+        echo "deb http://packages.elastic.co/elasticsearch/1.7/debian stable main" | sudo tee -a /etc/apt/sources.list.d/elasticsearch-1.7.list
+      fi
+    - sudo apt-get update
+    - sudo apt-get -y install elasticsearch
+    - sudo service elasticsearch restart
 
 install:
     - pip install --upgrade setuptools
-    - pip install requests "Django${DJANGO_VERSION}"
+    - pip install requests "Django${DJANGO_VERSION}" "elasticsearch${VERSION_ES}"
     - python setup.py clean build install
 
 before_script:
@@ -41,17 +53,17 @@ script:
 
 env:
     matrix:
-        - DJANGO_VERSION=">=1.8,<1.9"
-        - DJANGO_VERSION=">=1.9,<1.10"
-        - DJANGO_VERSION=">=1.10,<1.11"
+        - DJANGO_VERSION=">=1.8,<1.9" VERSION_ES=">=1.0.0,<2.0.0"
+        - DJANGO_VERSION=">=1.9,<1.10" VERSION_ES=">=1.0.0,<2.0.0"
+        - DJANGO_VERSION=">=1.10,<1.11" VERSION_ES=">=1.0.0,<2.0.0"
+        - DJANGO_VERSION=">=1.8,<1.9" VERSION_ES=">=2.0.0,<3.0.0"
+        - DJANGO_VERSION=">=1.9,<1.10" VERSION_ES=">=2.0.0,<3.0.0"
+        - DJANGO_VERSION=">=1.10,<1.11" VERSION_ES=">=2.0.0,<3.0.0"
 
 matrix:
     allow_failures:
         - python: 'pypy'
 
-services:
-    - elasticsearch
-
 notifications:
     irc: "irc.freenode.org#haystack"
     email: false
diff --git a/AUTHORS b/AUTHORS
index 7fdf08d..46c970c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -115,3 +115,4 @@ Thanks to
     * Tim Babych (@tymofij) for enabling backend-specific parameters in ``.highlight()``
     * Antony Raj (@antonyr) for adding endswith input type and fixing contains input type
     * Morgan Aubert (@ellmetha) for Django 1.10 support
+    * João Junior (@joaojunior) and Bruno Marques (@ElSaico) for Elasticsearch 2.x support
diff --git a/README.rst b/README.rst
index d5959e2..b8c47db 100644
--- a/README.rst
+++ b/README.rst
@@ -10,7 +10,7 @@ API that allows you to plug in different search backends (such as Solr_,
 Elasticsearch_, Whoosh_, Xapian_, etc.) without having to modify your code.
 
 .. _Solr: http://lucene.apache.org/solr/
-.. _Elasticsearch: http://elasticsearch.org/
+.. _Elasticsearch: https://www.elastic.co/products/elasticsearch
 .. _Whoosh: https://bitbucket.org/mchaput/whoosh/
 .. _Xapian: http://xapian.org/
 
diff --git a/docs/backend_support.rst b/docs/backend_support.rst
index a328c38..6669580 100644
--- a/docs/backend_support.rst
+++ b/docs/backend_support.rst
@@ -50,7 +50,7 @@ Elasticsearch
 * Stored (non-indexed) fields
 * Highlighting
 * Spatial search
-* Requires: elasticsearch-py > 1.0 & Elasticsearch 1.0+ (Elasticsearch 2.X is not supported yet `#1247 <https://github.com/django-haystack/django-haystack/issues/1247>`_)
+* Requires: `elasticsearch <https://pypi.python.org/pypi/elasticsearch>`_ 1.x or 2.x. Elasticsearch 5.X is currently unsupported: see `#1383 <https://github.com/django-haystack/django-haystack/issues/1383>`_.
 
 Whoosh
 ------
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 5a5e7a1..9f1247f 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,8 +1,232 @@
 Changelog
 =========
 
-%%version%% (unreleased)
-------------------------
+v2.6.0 (2017-01-02)
+-------------------
+
+- Merge #1460: backend support for Elasticsearch 2.x. [Chris Adams]
+
+  Thanks to João Junior (@joaojunior) and Bruno Marques (@ElSaico) for the
+  patch
+
+  Closes #1460
+  Closes #1391
+  Closes #1336
+  Closes #1247
+
+- Docs: update Elasticsearch support status. [Chris Adams]
+
+- Tests: avoid unrelated failures when elasticsearch is not installed.
+  [Chris Adams]
+
+  This avoids spurious failures in tests for other search engines when the
+  elasticsearch client library is not installed at all but the ES backend
+  is still declared in the settings.
+
+- Tests: friendlier log message for ES version checks. [Chris Adams]
+
+  This avoids a potentially scary-looking ImportError flying by in the
+  test output for what's expected in normal usage.
+
+- Tests: update ES version detection in settings. [Chris Adams]
+
+  This allows the tests to work when run locally or otherwise outside of
+  our Travis / Tox scripts by obtaining the version from the installed
+  `elasticsearch` client library.
+
+- Tests: update ES1 client version check message. [Chris Adams]
+
+  The name of the Python module changed over time and this now matches the
+  ES2 codebase behaviour of having the error message give you the exact
+  package to install including the version.
+
+- Update travis script with ES documentation. [Chris Adams]
+
+  Add a comment for anyone wondering why this isn't a simple
+  `add-apt-repository` call
+
+- Fixed More Like This test with deferred query on Elasticsearch 2.x.
+  [Bruno Marques]
+
+- Fixed expected query behaviour on ES2.x test. [Bruno Marques]
+
+- Install elasticsearch2.0 via apt. [joaojunior]
+
+- Install elasticsearch2.0 via apt. [joaojunior]
+
+- Remove typo. [joaojunior]
+
+- Remove services elasticsearch. [joaojunior]
+
+- Fix typo. [joaojunior]
+
+- Sudo=true in .travis.yml to install elasticsearch from apt-get.
+  [joaojunior]
+
+- Fix .travis. [joaojunior]
+
+- Add logging in __init__ tests elasticsearch. [joaojunior]
+
+- Get changes from Master to resolve conflicts. [joaojunior]
+
+- Install elasticsearch1.7 via apt. [joaojunior]
+
+- Update Files to run tests in Elasticsearch2.x. [joaojunior]
+
+- Refactoring the code in pull request #1336 . This pull request is to
+  permit use ElasticSearch 2.X. [joaojunior]
+
+- Improved custom object identifier test. [Chris Adams]
+
+  This provides an example for implementors and ensures that failing to
+  use the custom class would cause a test failure.
+
+- Update management backend documentation for `--using` [flinkflonk]
+
+  Thanks to @flinkflonk for the patch!
+
+  Closes #1215
+
+- Fix filtered "more like this" queries (#1459) [David Cook]
+
+  Now the Solr backend correctly handles a `more_like_this()` query which is subsequently `filter()`-ed.
+
+  Thanks to @divergentdave for the patch and tests!
+
+- ReStructuredText link format fixes. (#1458) [John Heasly]
+
+- Add note to Backend Support docs about lack of ES 5.X support. (#1457)
+  [John Heasly]
+
+- Replace deprecated Point.get_coords() calls. [Chris Adams]
+
+  This works as far back as Django 1.8, which is the earliest which we
+  support.
+
+  See #1454
+
+- Use setuptools_scm to manage package version numbers. [Chris Adams]
+
+v2.5.1 (2016-10-28)
+-------------------
+
+New
+~~~
+
+- Support for Django 1.10. [Chris Adams]
+
+  Thanks to Morgan Aubert (@ellmetha) for the patch
+
+  Closes #1434
+  Closes #1437
+  Closes #1445
+
+Fix
+~~~
+
+- Contains filter, add endswith filter. [Antony]
+
+  * `__contains` now works in a more intuitive manner (the previous behaviour remains the default for `=` shortcut queries and can be requested explicitly with `__content`)
+  * `__endswith` is now supported as the logical counterpart to `__startswith`
+
+  Thanks to @antonyr for the patch and @sebslomski for code review and testing.
+
+Other
+~~~~~
+
+- V2.5.1. [Chris Adams]
+
+- Add support for Django 1.10 (refs: #1437, #1434) [Morgan Aubert]
+
+- Docs: fix Sphinx hierarchy issue. [Chris Adams]
+
+- Fix multiprocessing regression in update_index. [Chris Adams]
+
+  4e1e2e1c5df1ed1c5432b9d26fcb9dc1abab71f4 introduced a bug because it
+  used a property name which exists on haystack.ConnectionHandler but not
+  the Django ConnectionHandler class it's modeled on. Long-term, we should
+  rename the Haystack class to something like `SearchConnectionHandler`
+  to avoid future confusion.
+
+  Closes #1449
+
+- Doc: cleanup searchindex_api.rst. [Jack Norman]
+
+  Thanks to Jack Norman (@jwnorman) for the patch
+
+- Merge pull request #1444 from jeremycline/master. [Chris Adams]
+
+  Upgrade setuptools in Travis so urllib3-1.18 installs
+
+- Upgrade setuptools in Travis so urllib3-1.18 installs. [Jeremy Cline]
+
+  The version of setuptools in Travis is too old to handle <= as an
+  environment marker.
+
+- Tests: accept Solr/ES config from environment. [Chris Adams]
+
+  This makes it easy to override these values for e.g. running test
+  instances using Docker images with something like this:
+
+  ```
+  TEST_ELASTICSEARCH_1_URL="http://$(docker port elasticsearch-1.7 9200/tcp)/" TEST_SOLR_URL="http://$(docker port solr-6 8983/tcp)/solr/" test_haystack/run_tests.py
+  ```
+
+  See #1408
+
+- Merge pull request #1418 from Alkalit/master. [Steve Byerly]
+
+  Added link for 2.5.x version docs
+
+- Added link for 2.5.x version. [Alexey Kalinin]
+
+- Merge pull request #1432 from farooqaaa/master. [Steve Byerly]
+
+  Added missing `--batch-size` argument for `rebuild_index` management command.
+
+- Added missing --batch-size argument. [Farooq Azam]
+
+- Merge pull request #1036 from merwok/patch-1. [Steve Byerly]
+
+  Documentation update
+
+- Use ellipsis instead of pass. [Éric Araujo]
+
+- Fix code to enable highlighting. [Éric Araujo]
+
+- Merge pull request #1392 from browniebroke/bugfix/doc-error. [Steve
+  Byerly]
+
+  Fix Sphinx errors in the changelog
+
+- Fix Sphinx errors in the changelog. [Bruno Alla]
+
+- Merge pull request #1341 from tymofij/solr-hl-options. [Steve Byerly]
+
+- Merge master > tymofij/solr-hl-options. [Steve Byerly]
+
+- Make solr backend accept both shortened and full-form highlighting
+  options. [Tim Babych]
+
+- Autoprefix 'hl.' for solr options. [Tim Babych]
+
+- Update gitignore to not track test artifacts. [Steve Byerly]
+
+- Merge pull request #1413 from tymofij/patch-2. [Steve Byerly]
+
+  typo: suite -> suit
+
+- Typo: suite -> suit. [Tim Babych]
+
+- Merge pull request #1412 from SteveByerly/highlight_sqs_docs. [Steve
+  Byerly]
+
+  improve sqs highlight docs - illustrate custom parameters
+
+- Improve highlight docs for custom options. [Steve Byerly]
+
+v2.5.0 (2016-07-11)
+-------------------
 
 New
 ~~~
@@ -29,7 +253,7 @@ New
   This allows the default values to be overriden and arbitrary
   backend-specific parameters may be provided to Solr or ElasticSearch.
 
-  Thanks to Tim Babych (@tymofij) for the patch
+  Thanks to @tymofij for the patch
 
   Closes #1334
 
@@ -110,6 +334,13 @@ Changes
 Fix
 ~~~
 
+- Attribute resolution on models which have a property named `all`
+  (#1405) [Henrique Chehad]
+
+  Thanks to Henrique Chehad (@henriquechehad) for the patch
+
+  Closes #1404
+
 - Tests will fall back to the Apache archive server. [Chris Adams]
 
   The Apache 4.10.4 release was quietly removed from the mirrors without a
@@ -201,6 +432,34 @@ Fix
 Other
 ~~~~~
 
+- Docs: update unsupported backends notes. [Chris Adams]
+
+  * Officially suggest developing backends as separate projects
+  * Recommend Sphinx users consider django-sphinxql
+
+- V2.5.0. [Chris Adams]
+
+- Bump version to 2.5.dev2. [Chris Adams]
+
+- AUTHORS. [Tim Babych]
+
+- Expand my username into name in changelog.txt. [Tim Babych]
+
+- Corrected non-ascii characters in comments. (#1390) [Mark Walker]
+
+- Add lower and upper bounds for django versions. [Simon Hanna]
+
+- Convert readthedocs link for their .org -> .io migration for hosted
+  projects. [Adam Chainz]
+
+  As per [their blog post of the 27th April](https://blog.readthedocs.com/securing-subdomains/) ‘Securing subdomains’:
+
+  > Starting today, Read the Docs will start hosting projects from subdomains on the domain readthedocs.io, instead of on readthedocs.org. This change addresses some security concerns around site cookies while hosting user generated data on the same domain as our dashboard.
+
+  Test Plan: Manually visited all the links I’ve modified.
+
+- V2.5.dev1. [Chris Adams]
+
 - Merge pull request #1349 from sbussetti/master. [Chris Adams]
 
   Fix logging call in `update_index`
@@ -679,7 +938,7 @@ v2.4.0 (2015-06-09)
 - App_loading cleanup. [Chris Adams]
 
   * Add support for Django 1.7+ AppConfig
-  * Rename internal app_loading functions to have haystack\_ prefix to make
+  * Rename internal app_loading functions to have haystack_ prefix to make
     it immediately obvious that they are not Django utilities and start
   * Add tests to avoid regressions for apps nested with multiple levels of
     module hierarchy like `raven.contrib.django.raven_compat`
@@ -1183,12 +1442,9 @@ v2.2.0 (2014-08-03)
   * Massively simplified test runner (``python setup.py test``)
 
   Minor updates:
-
   * Travis:
-
-    - Test Python 3.4
-    - Use Solr 4.6.1
-
+      - Test Python 3.4
+      - Use Solr 4.6.1
   * Simplified legacy test code which can now be replaced by the test utilities in newer versions of Django
   * Update ElasticSearch client & tests for ES 1.0+
   * Add option for SearchModelAdmin to specify the haystack connection to use
@@ -1593,8 +1849,10 @@ v2.2.0 (2014-08-03)
 
 - Merge pull request #413 from phill-tornroth/patch-1. [Justin Caratzas]
 
+  Silly little change, I know.. but I actually ran into a case where I acci
+
 - Silly little change, I know.. but I actually ran into a case where I
-  accidentally passed a list of models in without \*ing them. When that
+  accidentally passed a list of models in without *ing them. When that
   happens, we get a string formatting exception (not all arguments were
   formatted) instead of the useful "that ain't a model, kid" business.
   [Phill Tornroth]
@@ -3739,7 +3997,7 @@ v1.1 (2010-11-23)
 
 - We actually want `repr`, not `str`. [Daniel Lindsley]
 
-- Pushed the `model_attr` check lower down into the `SearchField` and
+- Pushed the `model_attr` check lower down into the `SearchField`s and
   make it occur later, so that exceptions come at a point where Django
   can better deal with them. [Daniel Lindsley]
 
@@ -3961,7 +4219,7 @@ v1.1 (2010-11-23)
   SmileyChris for patches. [Daniel Lindsley]
 
 - Added a note and an exception about consistent fieldnames for the
-  document field across all `SearchIndex` classes. Thanks sk1p\_! [Daniel
+  document field across all `SearchIndex` classes. Thanks sk1p_! [Daniel
   Lindsley]
 
 - Possible thread-safety fix related to registration handling. [Daniel
@@ -4304,7 +4562,7 @@ v1.1 (2010-11-23)
 - Corrected Xapian's capabilities. Thanks richardb! [Daniel Lindsley]
 
 - BACKWARD INCOMPATIBLE - Altered all settings to be prefixed with
-  HAYSTACK\_. Thanks Collin! [Daniel Lindsley]
+  HAYSTACK_. Thanks Collin! [Daniel Lindsley]
 
 - Test cleanup from previous commits. [Daniel Lindsley]
 
diff --git a/docs/installing_search_engines.rst b/docs/installing_search_engines.rst
index 7fb42e5..c3f316c 100644
--- a/docs/installing_search_engines.rst
+++ b/docs/installing_search_engines.rst
@@ -114,9 +114,9 @@ Official Download Location: http://www.elasticsearch.org/download/
 
 Elasticsearch is Java but comes in a pre-packaged form that requires very
 little other than the JRE. It's also very performant, scales easily and has
-an advanced featureset. Haystack currently only supports ElasticSearch 1.x.
-ElasticSearch 2.x is not supported yet, if you would like to help, please see
-`#1247 <https://github.com/django-haystack/django-haystack/issues/1247>`_.
+an advanced featureset. Haystack currently only supports Elasticsearch 1.x and 2.x.
+Elasticsearch 5.x is not supported yet, if you would like to help, please see
+`#1383 <https://github.com/django-haystack/django-haystack/issues/1383>`_.
 
 Installation is best done using a package manager::
 
diff --git a/docs/management_commands.rst b/docs/management_commands.rst
index e167923..600369d 100644
--- a/docs/management_commands.rst
+++ b/docs/management_commands.rst
@@ -21,8 +21,8 @@ following arguments::
     ``--verbosity``:
         Accepted but ignored.
     ``--using``:
-        If provided, determines which connection should be used. Default is
-        ``default``.
+        Update only the named backend (can be used multiple times). By default,
+        all backends will be updated.
     ``--nocommit``:
         If provided, it will pass commit=False to the backend.  This means that the
         update will not become immediately visible and will depend on another explicit commit
@@ -86,8 +86,8 @@ arguments::
           * ``2`` = Full output, including everything from ``1`` plus output
             on each batch that is indexed, which is useful when debugging.
     ``--using``:
-        If provided, determines which connection should be used. Default is
-        ``default``.
+        Update only the named backend (can be used multiple times). By default,
+        all backends will be updated.
     ``--nocommit``:
         If provided, it will pass commit=False to the backend.  This means that the
         updates will not become immediately visible and will depend on another explicit commit
@@ -161,8 +161,8 @@ of the arguments of the following arguments::
           * ``2`` = Full output, including everything from ``1`` plus output
             on each batch that is indexed, which is useful when debugging.
     ``--using``:
-        If provided, determines which connection should be used. Default is
-        ``default``.
+        Update only the named backend (can be used multiple times). By default,
+        all backends will be updated.
     ``--nocommit``:
         If provided, it will pass commit=False to the backend.  This means that the
         update will not become immediately visible and will depend on another explicit commit
@@ -181,8 +181,8 @@ following arguments::
     ``--filename``:
         If provided, directs output to a file instead of stdout.
     ``--using``:
-        If provided, determines which connection should be used. Default is
-        ``default``.
+        Update only the named backend (can be used multiple times). By default
+        all backends will be updated.
 
 .. warning::
 
diff --git a/docs/searchqueryset_api.rst b/docs/searchqueryset_api.rst
index d4f29d2..ea8e5bb 100644
--- a/docs/searchqueryset_api.rst
+++ b/docs/searchqueryset_api.rst
@@ -304,7 +304,7 @@ Example::
 
     # For SOLR (setting f.author.facet.*; see http://wiki.apache.org/solr/SimpleFacetParameters#Parameters)
     SearchQuerySet().facet('author', mincount=1, limit=10)
-    # For ElasticSearch (see http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html)
+    # For Elasticsearch (see http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html)
     SearchQuerySet().facet('author', size=10, order='term')
 
 In the search results you get back, facet counts will be populated in the
diff --git a/haystack/__init__.py b/haystack/__init__.py
index 4c8430c..201dcbc 100644
--- a/haystack/__init__.py
+++ b/haystack/__init__.py
@@ -4,14 +4,18 @@ from __future__ import absolute_import, division, print_function, unicode_litera
 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
+from pkg_resources import DistributionNotFound, get_distribution
 
-from haystack.constants import DEFAULT_ALIAS
 from haystack import signals
+from haystack.constants import DEFAULT_ALIAS
 from haystack.utils import loading
 
-
 __author__ = 'Daniel Lindsley'
-__version__ = (2, 5, 0)
+
+try:
+    __version__ = get_distribution(__name__).version
+except DistributionNotFound:
+    __version__ = (0, 0, 'dev0')
 
 default_app_config = 'haystack.apps.HaystackConfig'
 
diff --git a/haystack/backends/__init__.py b/haystack/backends/__init__.py
index e326d01..0ce6c4c 100644
--- a/haystack/backends/__init__.py
+++ b/haystack/backends/__init__.py
@@ -1007,6 +1007,8 @@ class BaseSearchQuery(object):
         clone._raw_query = self._raw_query
         clone._raw_query_params = self._raw_query_params
         clone.spelling_query = self.spelling_query
+        clone._more_like_this = self._more_like_this
+        clone._mlt_instance = self._mlt_instance
 
         return clone
 
diff --git a/haystack/backends/elasticsearch2_backend.py b/haystack/backends/elasticsearch2_backend.py
new file mode 100644
index 0000000..1e020ed
--- /dev/null
+++ b/haystack/backends/elasticsearch2_backend.py
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import datetime
+
+from django.conf import settings
+
+from haystack.backends import BaseEngine
+from haystack.backends.elasticsearch_backend import ElasticsearchSearchBackend, ElasticsearchSearchQuery
+from haystack.constants import DJANGO_CT
+from haystack.exceptions import MissingDependency
+from haystack.utils import get_identifier, get_model_ct
+from haystack.utils import log as logging
+
+try:
+    import elasticsearch
+    if not ((2, 0, 0) <= elasticsearch.__version__ < (3, 0, 0)):
+        raise ImportError
+    from elasticsearch.helpers import bulk, scan
+except ImportError:
+    raise MissingDependency("The 'elasticsearch2' backend requires the \
+                            installation of 'elasticsearch>=2.0.0,<3.0.0'. \
+                            Please refer to the documentation.")
+
+
+class Elasticsearch2SearchBackend(ElasticsearchSearchBackend):
+    def __init__(self, connection_alias, **connection_options):
+        super(Elasticsearch2SearchBackend, self).__init__(connection_alias, **connection_options)
+        self.content_field_name = None
+
+    def clear(self, models=None, commit=True):
+        """
+        Clears the backend of all documents/objects for a collection of models.
+
+        :param models: List or tuple of models to clear.
+        :param commit: Not used.
+        """
+        if models is not None:
+            assert isinstance(models, (list, tuple))
+
+        try:
+            if models is None:
+                self.conn.indices.delete(index=self.index_name, ignore=404)
+                self.setup_complete = False
+                self.existing_mapping = {}
+                self.content_field_name = None
+            else:
+                models_to_delete = []
+
+                for model in models:
+                    models_to_delete.append("%s:%s" % (DJANGO_CT, get_model_ct(model)))
+
+                # Delete using scroll API
+                query = {'query': {'query_string': {'query': " OR ".join(models_to_delete)}}}
+                generator = scan(self.conn, query=query, index=self.index_name, doc_type='modelresult')
+                actions = ({
+                    '_op_type': 'delete',
+                    '_id': doc['_id'],
+                } for doc in generator)
+                bulk(self.conn, actions=actions, index=self.index_name, doc_type='modelresult')
+                self.conn.indices.refresh(index=self.index_name)
+
+        except elasticsearch.TransportError as e:
+            if not self.silently_fail:
+                raise
+
+            if models is not None:
+                self.log.error("Failed to clear Elasticsearch index of models '%s': %s",
+                               ','.join(models_to_delete), e, exc_info=True)
+            else:
+                self.log.error("Failed to clear Elasticsearch index: %s", e, exc_info=True)
+
+    def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_offset=None,
+                            fields='', highlight=False, facets=None,
+                            date_facets=None, query_facets=None,
+                            narrow_queries=None, spelling_query=None,
+                            within=None, dwithin=None, distance_point=None,
+                            models=None, limit_to_registered_models=None,
+                            result_class=None):
+        kwargs = super(Elasticsearch2SearchBackend, self).build_search_kwargs(query_string, sort_by,
+                                                                              start_offset, end_offset,
+                                                                              fields, highlight,
+                                                                              spelling_query=spelling_query,
+                                                                              within=within, dwithin=dwithin,
+                                                                              distance_point=distance_point,
+                                                                              models=models,
+                                                                              limit_to_registered_models=
+                                                                              limit_to_registered_models,
+                                                                              result_class=result_class)
+
+        filters = []
+        if start_offset is not None:
+            kwargs['from'] = start_offset
+
+        if end_offset is not None:
+            kwargs['size'] = end_offset - start_offset
+
+        if narrow_queries is None:
+            narrow_queries = set()
+
+        if facets is not None:
+            kwargs.setdefault('aggs', {})
+
+            for facet_fieldname, extra_options in facets.items():
+                facet_options = {
+                    'meta': {
+                        '_type': 'terms',
+                    },
+                    'terms': {
+                        'field': facet_fieldname,
+                    }
+                }
+                if 'order' in extra_options:
+                    facet_options['meta']['order'] = extra_options.pop('order')
+                # Special cases for options applied at the facet level (not the terms level).
+                if extra_options.pop('global_scope', False):
+                    # Renamed "global_scope" since "global" is a python keyword.
+                    facet_options['global'] = True
+                if 'facet_filter' in extra_options:
+                    facet_options['facet_filter'] = extra_options.pop('facet_filter')
+                facet_options['terms'].update(extra_options)
+                kwargs['aggs'][facet_fieldname] = facet_options
+
+        if date_facets is not None:
+            kwargs.setdefault('aggs', {})
+
+            for facet_fieldname, value in date_facets.items():
+                # Need to detect on gap_by & only add amount if it's more than one.
+                interval = value.get('gap_by').lower()
+
+                # Need to detect on amount (can't be applied on months or years).
+                if value.get('gap_amount', 1) != 1 and interval not in ('month', 'year'):
+                    # Just the first character is valid for use.
+                    interval = "%s%s" % (value['gap_amount'], interval[:1])
+
+                kwargs['aggs'][facet_fieldname] = {
+                    'meta': {
+                        '_type': 'date_histogram',
+                    },
+                    'date_histogram': {
+                        'field': facet_fieldname,
+                        'interval': interval,
+                    },
+                    'aggs': {
+                        facet_fieldname: {
+                            'date_range': {
+                                'field': facet_fieldname,
+                                'ranges': [
+                                    {
+                                        'from': self._from_python(value.get('start_date')),
+                                        'to': self._from_python(value.get('end_date')),
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                }
+
+        if query_facets is not None:
+            kwargs.setdefault('aggs', {})
+
+            for facet_fieldname, value in query_facets:
+                kwargs['aggs'][facet_fieldname] = {
+                    'meta': {
+                        '_type': 'query',
+                    },
+                    'filter': {
+                        'query_string': {
+                            'query': value,
+                        }
+                    },
+                }
+
+        for q in narrow_queries:
+            filters.append({
+                'query_string': {
+                    'query': q
+                }
+            })
+
+        # if we want to filter, change the query type to filteres
+        if filters:
+            kwargs["query"] = {"filtered": {"query": kwargs.pop("query")}}
+            filtered = kwargs["query"]["filtered"]
+            if 'filter' in filtered:
+                if "bool" in filtered["filter"].keys():
+                    another_filters = kwargs['query']['filtered']['filter']['bool']['must']
+                else:
+                    another_filters = [kwargs['query']['filtered']['filter']]
+            else:
+                another_filters = filters
+
+            if len(another_filters) == 1:
+                kwargs['query']['filtered']["filter"] = another_filters[0]
+            else:
+                kwargs['query']['filtered']["filter"] = {"bool": {"must": another_filters}}
+
+        return kwargs
+
+    def more_like_this(self, model_instance, additional_query_string=None,
+                       start_offset=0, end_offset=None, models=None,
+                       limit_to_registered_models=None, result_class=None, **kwargs):
+        from haystack import connections
+
+        if not self.setup_complete:
+            self.setup()
+
+        # Deferred models will have a different class ("RealClass_Deferred_fieldname")
+        # which won't be in our registry:
+        model_klass = model_instance._meta.concrete_model
+
+        index = connections[self.connection_alias].get_unified_index().get_index(model_klass)
+        field_name = index.get_content_field()
+        params = {}
+
+        if start_offset is not None:
+            params['from_'] = start_offset
+
+        if end_offset is not None:
+            params['size'] = end_offset - start_offset
+
+        doc_id = get_identifier(model_instance)
+
+        try:
+            # More like this Query
+            # https://www.elastic.co/guide/en/elasticsearch/reference/2.2/query-dsl-mlt-query.html
+            mlt_query = {
+                'query': {
+                    'more_like_this': {
+                        'fields': [field_name],
+                        'like': [{
+                            "_id": doc_id
+                        }]
+                    }
+                }
+            }
+
+            narrow_queries = []
+
+            if additional_query_string and additional_query_string != '*:*':
+                additional_filter = {
+                    "query": {
+                        "query_string": {
+                            "query": additional_query_string
+                        }
+                    }
+                }
+                narrow_queries.append(additional_filter)
+
+            if limit_to_registered_models is None:
+                limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
+
+            if models and len(models):
+                model_choices = sorted(get_model_ct(model) for model in models)
+            elif limit_to_registered_models:
+                # Using narrow queries, limit the results to only models handled
+                # with the current routers.
+                model_choices = self.build_models_list()
+            else:
+                model_choices = []
+
+            if len(model_choices) > 0:
+                model_filter = {"terms": {DJANGO_CT: model_choices}}
+                narrow_queries.append(model_filter)
+
+            if len(narrow_queries) > 0:
+                mlt_query = {
+                    "query": {
+                        "filtered": {
+                            'query': mlt_query['query'],
+                            'filter': {
+                                'bool': {
+                                    'must': list(narrow_queries)
+                                }
+                            }
+                        }
+                    }
+                }
+
+            raw_results = self.conn.search(
+                body=mlt_query,
+                index=self.index_name,
+                doc_type='modelresult',
+                _source=True, **params)
+        except elasticsearch.TransportError as e:
+            if not self.silently_fail:
+                raise
+
+            self.log.error("Failed to fetch More Like This from Elasticsearch for document '%s': %s",
+                           doc_id, e, exc_info=True)
+            raw_results = {}
+
+        return self._process_results(raw_results, result_class=result_class)
+
+    def _process_results(self, raw_results, highlight=False,
+                         result_class=None, distance_point=None,
+                         geo_sort=False):
+        results = super(Elasticsearch2SearchBackend, self)._process_results(raw_results, highlight,
+                                                                            result_class, distance_point,
+                                                                            geo_sort)
+        facets = {}
+        if 'aggregations' in raw_results:
+            facets = {
+                'fields': {},
+                'dates': {},
+                'queries': {},
+            }
+
+            for facet_fieldname, facet_info in raw_results['aggregations'].items():
+                facet_type = facet_info['meta']['_type']
+                if facet_type == 'terms':
+                    facets['fields'][facet_fieldname] = [(individual['key'], individual['doc_count']) for individual in facet_info['buckets']]
+                    if 'order' in facet_info['meta']:
+                        if facet_info['meta']['order'] == 'reverse_count':
+                            srt = sorted(facets['fields'][facet_fieldname], key=lambda x: x[1])
+                            facets['fields'][facet_fieldname] = srt
+                elif facet_type == 'date_histogram':
+                    # Elasticsearch provides UTC timestamps with an extra three
+                    # decimals of precision, which datetime barfs on.
+                    facets['dates'][facet_fieldname] = [(datetime.datetime.utcfromtimestamp(individual['key'] / 1000), individual['doc_count']) for individual in facet_info['buckets']]
+                elif facet_type == 'query':
+                    facets['queries'][facet_fieldname] = facet_info['doc_count']
+        results['facets'] = facets
+        return results
+
+
+class Elasticsearch2SearchQuery(ElasticsearchSearchQuery):
+    pass
+
+
+class Elasticsearch2SearchEngine(BaseEngine):
+    backend = Elasticsearch2SearchBackend
+    query = Elasticsearch2SearchQuery
diff --git a/haystack/fields.py b/haystack/fields.py
index 0fa1abc..7024631 100644
--- a/haystack/fields.py
+++ b/haystack/fields.py
@@ -223,7 +223,7 @@ class LocationField(SearchField):
             return None
 
         pnt = ensure_point(value)
-        pnt_lng, pnt_lat = pnt.get_coords()
+        pnt_lng, pnt_lat = pnt.coords
         return "%s,%s" % (pnt_lat, pnt_lng)
 
     def convert(self, value):
diff --git a/haystack/models.py b/haystack/models.py
index b12a2ee..637230c 100644
--- a/haystack/models.py
+++ b/haystack/models.py
@@ -127,7 +127,7 @@ class SearchResult(object):
             if not hasattr(self, self._point_of_origin['field']):
                 raise SpatialError("The field '%s' was not included in search results, so the distance could not be calculated." % self._point_of_origin['field'])
 
-            po_lng, po_lat = self._point_of_origin['point'].get_coords()
+            po_lng, po_lat = self._point_of_origin['point'].coords
             location_field = getattr(self, self._point_of_origin['field'])
 
             if location_field is None:
diff --git a/setup.py b/setup.py
index 4261171..4967036 100755
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,6 @@ install_requires = [
 ]
 
 tests_require = [
-    'elasticsearch>=1.0.0,<2.0.0',
     'pysolr>=3.3.2',
     'whoosh>=2.5.4,<3.0',
     'python-dateutil',
@@ -30,7 +29,7 @@ tests_require = [
 
 setup(
     name='django-haystack',
-    version='2.5.1',
+    use_scm_version=True,
     description='Pluggable search for Django.',
     author='Daniel Lindsley',
     author_email='daniel at toastdriven.com',
@@ -66,4 +65,5 @@ setup(
     install_requires=install_requires,
     tests_require=tests_require,
     test_suite="test_haystack.run_tests.run_all",
+    setup_requires=['setuptools_scm'],
 )
diff --git a/test_haystack/core/custom_identifier.py b/test_haystack/core/custom_identifier.py
index 11c4a48..73af83e 100644
--- a/test_haystack/core/custom_identifier.py
+++ b/test_haystack/core/custom_identifier.py
@@ -2,10 +2,16 @@
... 2231 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-haystack.git



More information about the Python-modules-commits mailing list