[Python-modules-commits] [drf-haystack] 04/17: Import drf-haystack_1.5.5.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Sat Jan 2 17:45:41 UTC 2016


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

fladi pushed a commit to branch master
in repository drf-haystack.

commit 7125c89ce546985031495b0a69bfac1378c50603
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Fri Nov 13 08:26:20 2015 +0100

    Import drf-haystack_1.5.5.orig.tar.gz
---
 LICENSE.txt                                        |   21 +
 MANIFEST.in                                        |    6 +
 PKG-INFO                                           |    6 +-
 README.rst                                         |    3 +-
 docs/Makefile                                      |  177 +++
 docs/advanced_usage.rst                            |  918 ++++++++++++++
 docs/basic_usage.rst                               |  221 ++++
 docs/conf.py                                       |  298 +++++
 docs/index.rst                                     |  152 +++
 docs/make.bat                                      |  242 ++++
 drf_haystack.egg-info/PKG-INFO                     |    6 +-
 drf_haystack.egg-info/SOURCES.txt                  |   42 +-
 drf_haystack.egg-info/pbr.json                     |    1 -
 drf_haystack.egg-info/requires.txt                 |    2 +
 drf_haystack/__init__.py                           |    4 +-
 drf_haystack/filters.py                            |  143 ++-
 drf_haystack/generics.py                           |   61 +-
 drf_haystack/serializers.py                        |  180 ++-
 drf_haystack/utils.py                              |   31 +
 drf_haystack/viewsets.py                           |   36 +-
 requirements.txt                                   |   13 +
 setup.cfg                                          |    2 +-
 setup.py                                           |    9 +-
 tests/__init__.py                                  |   48 +
 tests/constants.py                                 |   17 +
 tests/mixins.py                                    |   17 +
 tests/mockapp/__init__.py                          |    0
 tests/mockapp/admin.py                             |    3 +
 tests/mockapp/fixtures/mocklocation.json           | 1302 ++++++++++++++++++++
 tests/mockapp/fixtures/mockperson.json             | 1002 +++++++++++++++
 tests/mockapp/fixtures/mockpet.json                |  102 ++
 tests/mockapp/migrations/0001_initial.py           |   29 +
 tests/mockapp/migrations/0002_mockperson.py        |   27 +
 tests/mockapp/migrations/0003_mockpet.py           |   28 +
 tests/mockapp/migrations/0004_load_fixtures.py     |   56 +
 tests/mockapp/migrations/__init__.py               |    0
 tests/mockapp/models.py                            |   57 +
 tests/mockapp/search_indexes.py                    |   86 ++
 tests/mockapp/serializers.py                       |   63 +
 .../search/indexes/mockapp/mocklocation_text.txt   |    3 +
 .../search/indexes/mockapp/mockperson_text.txt     |    1 +
 .../search/indexes/mockapp/mockpet_text.txt        |    1 +
 tests/mockapp/views.py                             |   39 +
 tests/runtests.py                                  |   25 +
 tests/settings.py                                  |  113 ++
 tests/test_filters.py                              |  462 +++++++
 tests/test_serializers.py                          |  677 ++++++++++
 tests/test_utils.py                                |   55 +
 tests/test_viewsets.py                             |  224 ++++
 tests/urls.py                                      |   17 +
 tests/wsgi.py                                      |   14 +
 tox.ini                                            |  152 +++
 52 files changed, 7154 insertions(+), 40 deletions(-)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..0258290
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Inonit AS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..4fb1247
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include README.rst
+include LICENSE.txt
+include requirements.txt
+include tox.ini
+recursive-include docs Makefile *.rst *.py *.bat
+recursive-include tests *.py *.json *.txt
diff --git a/PKG-INFO b/PKG-INFO
index 4daae50..e1434a0 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,16 +1,16 @@
 Metadata-Version: 1.1
 Name: drf-haystack
-Version: 1.5.2
+Version: 1.5.5
 Summary: Makes Haystack play nice with Django REST Framework
 Home-page: https://github.com/inonit/drf-haystack
 Author: Rolf Håvard Blindheim, Eirik Krogstad
 Author-email: rolf.blindheim at inonit.no, eirik.krogstad at inonit.no
 License: MIT License
 Download-URL: https://github.com/inonit/drf-haystack.git
-Description: Implements a ViewSet, some filters and serializers in order to play nice with Haystack.
+Description: Implements a ViewSet, FiltersBackends and Serializers in order to play nice with Haystack.
 Platform: UNKNOWN
 Classifier: Operating System :: OS Independent
-Classifier: Development Status :: 4 - Beta
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Framework :: Django
 Classifier: Intended Audience :: Developers
diff --git a/README.rst b/README.rst
index a5e0ab7..a9bc670 100644
--- a/README.rst
+++ b/README.rst
@@ -32,7 +32,7 @@ Tested with the following configurations:
 
     - Python 2.6
         - Django 1.5 and 1.6
-    - Python 2.7, 3.3 and 3.4
+    - Python 2.7, 3.3, 3.4 and 3.5
         - Django 1.5, 1.6, 1.7 and 1.8
 
 Installation
@@ -49,3 +49,4 @@ Currently we support:
     * GEO Spatial searching
     * Highlighting
     * More Like This
+    * Faceting
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..57cb7b3
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  xml        to make Docutils-native XML files"
+	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-pushit.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-pushit.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/django-pushit"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-pushit"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through platex and dvipdfmx..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+	@echo
+	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst
new file mode 100644
index 0000000..7664628
--- /dev/null
+++ b/docs/advanced_usage.rst
@@ -0,0 +1,918 @@
+.. _advanced-usage-label:
+
+==============
+Advanced Usage
+==============
+
+Make sure you've read through the :ref:`basic-usage-label`.
+
+
+Query Field Lookups
+===================
+
+You can also use field lookups in your field queries. See the
+Haystack `field lookups <https://django-haystack.readthedocs.org/en/latest/searchqueryset_api.html?highlight=lookups#id1>`_
+documentation for info on what lookups are available.  A query using a lookup might look like the
+following:
+
+.. code-block:: none
+
+    http://example.com/api/v1/location/search/?city__startswith=Os
+
+This would perform a query looking up all documents where the `city field` started with "Os".
+You might get "Oslo", "Osaka", and "Ostrava".
+
+Query Term Negation
+-------------------
+You can also specify terms to exclude from the search results using the negation keyword.
+The default keyword is "not", but is configurable via settings using ``DRF_HAYSTACK_NEGATION_KEYWORD``.
+
+.. code-block:: none
+
+    http://example.com/api/v1/location/search/?city__not=Oslo
+    http://example.com/api/v1/location/search/?city__not__contains=Los
+    http://example.com/api/v1/location/search/?city__contains=Los&city__not__contains=Angeles
+
+Autocomplete
+============
+
+Some kind of data such as ie. cities and zip codes could be useful to autocomplete.
+We have a Django REST Framework filter for performing autocomplete queries. It works
+quite like the regular ``HaystackFilter`` but *must* be run against an ``NgramField`` or
+``EdgeNgramField`` in order to work properly. The main difference is that while the
+HaystackFilter performs a bitwise ``OR`` on terms for the same parameters, the
+``HaystackAutocompleteFilter`` reduce query parameters down to a single filter
+(using an ``SQ`` object), and performs a bitwise ``AND``.
+
+.. class:: drf_haystack.filters.HaystackAutocompleteFilter
+
+By adding a list or tuple of ``ignore_fields`` to the serializer's Meta class,
+we can tell the REST framework to ignore these fields. This is handy in cases,
+where you do not want to serialize and transfer the content of a text, or n-gram
+index down to the client.
+
+An example using the autocomplete filter might look something like this.
+
+
+.. code-block:: python
+
+    class AutocompleteSerializer(HaystackSerializer):
+
+        class Meta:
+            index_classes = [LocationIndex]
+            fields = ["address", "city", "zip_code", "autocomplete"]
+            ignore_fields = ["autocomplete"]
+
+            # The `field_aliases` attribute can be used in order to alias a
+            # query parameter to a field attribute. In this case a query like
+            # /search/?q=oslo would alias the `q` parameter to the `autocomplete`
+            # field on the index.
+            field_aliases = {
+                "q": "autocomplete"
+            }
+
+    class AutocompleteSearchViewSet(HaystackViewSet):
+
+        index_models = [Location]
+        serializer_class = AutocompleteSerializer
+        filter_backends = [HaystackAutocompleteFilter]
+
+
+GEO Locations
+=============
+
+Some search backends support geo spatial searching. In order to take advantage of this we
+have the ``HaystackGEOSpatialFilter``.
+
+.. class:: drf_haystack.filters.HaystackGEOSpatialFilter
+
+.. note::
+
+    The ``HaystackGEOSpatialFilter`` depends on ``geopy`` and ``libgeos``. Make sure to install these
+    libraries in order to use this filter.
+
+    .. code-block:: none
+
+        $ pip install geopy
+        $ apt-get install libgeos (for debian based linux distros)
+          or
+        $ brew install geos (for homebrew on OS X)
+
+The geospatial filter is somewhat special, and for the time being, relies on a few assumptions.
+
+#. The index model **must** to have a ``LocationField`` named ``coordinates`` (See :ref:`search-index-example-label` for example).
+#. The query **must** contain a ``unit`` parameter where the unit is a valid ``UNIT`` in the ``django.contrib.gis.measure.Distance`` class.
+#. The query **must** contain a ``from`` parameter which is a comma separated longitude and latitude value.
+
+
+**Example Geospatial view**
+
+.. code-block:: python
+
+    class DistanceSerializer(serializers.Serializer):
+        m = serializers.FloatField()
+        km = serializers.FloatField()
+
+
+    class LocationSerializer(HaystackSerializer):
+
+        distance = SerializerMethodField()
+
+        class Meta:
+            index_classes = [LocationIndex]
+            fields = ["address", "city", "zip_code", "location"]
+
+        def get_distance(self, obj):
+            if hasattr(obj, "distance"):
+                return DistanceSerializer(obj.distance, many=False).data
+
+
+    class LocationGeoSearchViewSet(HaystackViewSet):
+
+        index_models = [Location]
+        serializer_class = LocationSerializer
+        filter_backends = [HaystackGEOSpatialFilter]
+
+
+Assuming the above code works as it should, we would be able to do queries like this:
+
+.. code-block:: none
+
+    /api/v1/search/?zip_code=0351&km=10&from=59.744076,10.152045
+
+
+The above query would return all entries with zip_code 0351 within 10 kilometers
+from the location with latitude 59.744076 and longitude 10.152045.
+
+
+Highlighting
+============
+
+Haystack supports two kinds of `Highlighting <https://django-haystack.readthedocs.org/en/latest/highlighting.html>`_,
+and we support them both.
+
+#. SearchQuerySet highlighting. This kind of highlighting requires a search backend which has support for
+   highlighting, such as Elasticsearch or Solr.
+#. Pure python highlighting. This implementation is somewhat slower, but enables highlighting support
+   even if your search backend does not support it.
+
+
+.. note::
+
+    The highlighter will always use the ``document=True`` field on your index to hightlight on.
+    See examples below.
+
+SearchQuerySet Highlighting
+---------------------------
+
+In order to add support for ``SearchQuerySet().highlight()``, all you have to do is to add the
+``HaystackHighlightFilter`` to the ``filter_backends`` in your view. The ``HaystackSerializer`` will
+check if your queryset has highlighting enabled, and render an additional ``highlighted`` field to
+your result. The highlighted words will be encapsulated in an ``<em>words go here</em>`` html tag.
+
+.. warning::
+
+    The ``SQHighlighterMixin()`` is deprecated in favor of the  ``HaystackHighlightFilter()`` filter backend.
+
+.. class:: drf_haystack.filters.HaystackHighlightFilter
+
+
+**Example view with highlighting enabled**
+
+.. code-block:: python
+
+    from drf_haystack.viewsets import HaystackViewSet
+    from drf_haystack.filters import HaystackHighlightFilter
+
+    from .models import Person
+    from .serializers import PersonSerializer
+
+
+    class SearchViewSet(HaystackViewSet):
+        index_models = [Person]
+        serializer_class = PersonSerializer
+        filter_backends = [HaystackHighlightFilter]
+
+
+Given a query like below
+
+.. code-block:: none
+
+    /api/v1/search/?firstname=jeremy
+
+
+We would get a result like this
+
+.. code-block:: json
+
+    [
+        {
+            "lastname": "Rowland",
+            "full_name": "Jeremy Rowland",
+            "firstname": "Jeremy",
+            "highlighted": "<em>Jeremy</em> Rowland\nCreated: May 19, 2015, 10:48 a.m.\nLast modified: May 19, 2015, 10:48 a.m.\n"
+        },
+        {
+            "lastname": "Fowler",
+            "full_name": "Jeremy Fowler",
+            "firstname": "Jeremy",
+            "highlighted": "<em>Jeremy</em> Fowler\nCreated: May 19, 2015, 10:48 a.m.\nLast modified: May 19, 2015, 10:48 a.m.\n"
+        }
+    ]
+
+
+
+Pure Python Highlighting
+------------------------
+
+This implementation make use of the haystack ``Highlighter()`` class.
+It is implemented as a mixin class, and must be applied on the ``Serializer``.
+This is somewhat slower, but more configurable than the ``SQHighlighterMixin()``.
+
+.. class:: drf_haystack.serializers.HighlighterMixin
+
+The Highlighter class will be initialized with the following default options, but can be overridden by
+changing any of the following class attributes.
+
+    .. code-block:: python
+
+        highlighter_class = Highlighter
+        highlighter_css_class = "highlighted"
+        highlighter_html_tag = "span"
+        highlighter_max_length = 200
+        highlighter_field = None
+
+The Highlighter class will usually highlight the ``document_field`` (the field marked ``document=True`` on your
+search index class), but this may be overridden by changing the ``highlighter_field``.
+
+You can of course also use your own ``Highlighter`` class by overriding the ``highlighter_class = MyFancyHighLighter``
+class attribute.
+
+
+**Example serializer with highlighter support**
+
+.. code-block:: python
+
+    from drf_haystack.serializers import HighlighterMixin, HaystackSerializer
+
+    class PersonSerializer(HighlighterMixin, HaystackSerializer):
+
+        highlighter_css_class = "my-highlighter-class"
+        highlighter_html_tag = "em"
+
+        class Meta:
+            index_classes = [PersonIndex]
+            fields = ["firstname", "lastname", "full_name"]
+
+
+Response
+
+.. code-block:: json
+
+    [
+        {
+            "full_name": "Jeremy Rowland",
+            "lastname": "Rowland",
+            "firstname": "Jeremy",
+            "highlighted": "<em class=\"my-highlighter-class\">Jeremy</em> Rowland\nCreated: May 19, 2015, 10:48 a.m.\nLast modified: May 19, 2015, 10:48 a.m.\n"
+        },
+        {
+            "full_name": "Jeremy Fowler",
+            "lastname": "Fowler",
+            "firstname": "Jeremy",
+            "highlighted": "<em class=\"my-highlighter-class\">Jeremy</em> Fowler\nCreated: May 19, 2015, 10:48 a.m.\nLast modified: May 19, 2015, 10:48 a.m.\n"
+        }
+    ]
+
+
+.. _more-like-this-label:
+
+More Like This
+==============
+
+Some search backends supports ``More Like This`` features. In order to take advantage of this,
+the ``HaystackViewSet`` includes a ``more-like-this`` detail route which is appended to the base name of the
+ViewSet. Lets say you have a router which looks like this:
+
+.. code-block:: python
+
+    router = routers.DefaultRouter()
+    router.register("search", viewset=SearchViewSet, base_name="search")  # MLT name will be 'search-more-like-this'.
+
+    urlpatterns = patterns(
+        "",
+        url(r"^", include(router.urls))
+    )
+
+The important thing here is that the ``SearchViewSet`` class inherits from the ``HaystackViewSet`` class
+in order to get the ``more-like-this`` route automatically added. The view name will be
+``{base_name}-more-like-this``, which in this case would be for example ``search-more-like-this``.
+
+
+Serializing the More Like This URL
+----------------------------------
+
+In order to include the ``more-like-this`` url in your result you only have to add a ``HyperlinkedIdentityField``
+to your serializer.
+Something like this should work okay.
+
+**Example serializer with More Like This**
+
+.. code-block:: python
+
+    class SearchSerializer(HaystackSerializer):
+
+        more_like_this = serializers.HyperlinkedIdentityField(view_name="search-more-like-this", read_only=True)
+
+        class Meta:
+            index_classes = [PersonIndex]
+            fields = ["firstname", "lastname", "full_name"]
+
+
+Now, every result you render with this serializer will include a ``more_like_this`` field containing the url
+for similar results.
+
+Example response
+
+.. code-block:: json
+
+    [
+        {
+            "full_name": "Jeremy Rowland",
+            "lastname": "Rowland",
+            "firstname": "Jeremy",
+            "more_like_this": "http://example.com/search/5/more-like-this/"
+        }
+    ]
+
+.. _term-boost-label:
+
+Term Boost
+==========
+
+.. warning::
+
+    **BIG FAT WARNING**
+
+    As far as I can see, the term boost functionality is implemented by the specs in the
+    `Haystack documentation <https://django-haystack.readthedocs.org/en/v2.4.0/boost.html#term-boost>`_,
+    however it does not really work as it should!
+
+    When applying term boost, results are discarded from the search result, and not re-ordered by
+    boost weight as they should.
+    These are known problems and there exists open issues for them:
+
+        - https://github.com/inonit/drf-haystack/issues/21
+        - https://github.com/django-haystack/django-haystack/issues/1235
+        - https://github.com/django-haystack/django-haystack/issues/508
+
+    **Please do not use this unless you really know what you are doing!**
+
+    (And please let me know if you know how to fix it!)
+
+
+Term boost is achieved on the SearchQuerySet level by calling ``SearchQuerySet().boost()``. It is
+implemented as a filter backend, and applies boost **after** regular filtering has occurred.
+
+.. class:: drf_haystack.filters.HaystackBoostFilter
+
+.. code-block:: python
+
+    from drf_haystack.filters import HaystackBoostFilter
+
+    class SearchViewSet(HaystackViewSet):
+        ...
+        filter_backends = [HaystackBoostFilter]
+
+
+The filter expects the query string to contain a ``boost`` parameter, which is a comma separated string
+of the term to boost and the boost value. The boost value must be either an integer or float value.
+
+**Example query**
+
+.. code-block:: none
+
+    /api/v1/search/?firstname=robin&boost=hood,1.1
+
+The query above will first filter on ``firstname=robin`` and next apply a slight boost on any document containing
+the word ``hood``.
+
+.. note::
+
+    Term boost are only applied on terms existing in the ``document field``.
+
+.. _faceting-label:
+
+Faceting
+========
+
+Faceting is a way of grouping and narrowing search results by a common factor, for example we can group
+all results which are registered on a certain date. Similar to :ref:`more-like-this-label`, the faceting
+functionality is implemented by setting up a special ``^search/facets/$`` route on any view which inherits from the
+``HaystackViewSet`` class.
+
+
+.. note::
+
+    Options used for faceting is **not** portable across search backends. Make sure to provide
+    options suitable for the backend you're using.
+
+
+First, read the `Haystack faceting docs <http://django-haystack.readthedocs.org/en/latest/faceting.html>`_ and set up
+your search index for faceting.
+
+Serializing faceted counts
+--------------------------
+
+Faceting is a little special in terms that it *does not* care about SearchQuerySet filtering. Faceting is performed
+by calling the ``SearchQuerySet().facet(field, **options)`` and ``SearchQuerySet().date_facet(field, **options)``
+methods, which will apply facets to the SearchQuerySet. Next we need to call the ``SearchQuerySet().facet_counts()``
+in order to retrieve a dictionary with all the *counts* for the faceted fields.
+We have a special ``HaystackFacetSerializer`` class which is designed to serialize these results.
+
+.. tip::
+
+    It *is* possible to perform faceting on a subset of the queryset, in which case you'd have to override the
+    ``get_queryset()`` method of the view to limit the queryset before it is passed on to the
+    ``filter_facet_queryset()`` method.
+
+The ``HaystackFacetSerializer`` overrides a number of methods is customized to only serialize facets in a very
+specific format. Using this serializer for other stuff will probably not work very good. Consider yourself warned!
+
+Any serializer subclassed from the ``HaystackFacetSerializer`` is expected to have a ``field_options`` dictionary
+containing a set of default options passed to ``facet()`` and ``date_facet()``.
+
+**Facet serializer example**
+
+.. code-block:: python
+
+    class PersonFacetSerializer(HaystackFacetSerializer):
+
+        serialize_objects = False  # Setting this to True will serialize the
+                                   # queryset into an `objects` list. This
+                                   # is useful if you need to display the faceted
+                                   # results. Defaults to False.
+        class Meta:
+            index_classes = [PersonIndex]
+            fields = ["firstname", "lastname", "created"]
+            field_options = {
+                "firstname": {},
+                "lastname": {},
+                "created": {
+                    "start_date": datetime.now() - timedelta(days=3 * 365),
+                    "end_date": datetime.now(),
+                    "gap_by": "month",
+                    "gap_amount": 3
+                }
+            }
+
+The declared ``field_options`` will be used as default options when faceting is applied to the queryset, but can be
+overridden by supplying query string parameters in the following format.
+
+    .. code-block:: none
+
+        ?firstname=limit:1&created=start_date:20th May 2014,gap_by:year
+
+Each field can be fed options as ``key:value`` pairs. Multiple ``key:value`` pairs can be supplied and
+will be separated by the ``view.lookup_sep`` attribute (which defaults to comma). Any ``start_date`` and ``end_date``
+parameters will be parsed by the python-dateutil
+`parser() <https://labix.org/python-dateutil#head-a23e8ae0a661d77b89dfb3476f85b26f0b30349c>`_ (which can handle most
+common date formats).
+
+    .. note::
+
+        - The ``HaystackFacetFilter`` parses query string parameter options, separated with the ``view.lookup_sep``
+          attribute. Each option is parsed as ``key:value`` pairs where the ``:`` is a hardcoded separator. Setting
+          the ``view.lookup_sep`` attribute to ``":"`` will raise an AttributeError.
+
+        - The date parsing in the ``HaystackFacetFilter`` does intentionally blow up if fed a string format it can't
+          handle. No exception handling is done, so make sure to convert values to a format you know it can handle
+          before passing it to the filter. Ie., don't let your users feed their own values in here ;)
+
+    .. warning::
+
+        Do *not* use the ``HaystackFacetFilter`` in the regular ``filter_backends`` list on the serializer.
+        It will almost certainly produce errors or weird results. Faceting filters should go in the
+        ``facet_filter_backends`` list.
+
+**Example serialized content**
+
+The serialized content will look a little different than the default Haystack faceted output.
+The top level items will *always* be **queries**, **fields** and **dates**, each containing a subset of fields
+matching the category. In the example below, we have faceted on the fields *firstname* and *lastname*, which will
+make them appear under the **fields** category. We also have faceted on the date field *created*, which will show up
+under the **dates** category. Next, each faceted result will have a ``text``, ``count`` and ``narrow_url``
+attribute which should be quite self explaining.
+
+    .. code-block:: json
+
+        {
+          "queries": {},
+          "fields": {
+            "firstname": [
+              {
+                "text": "John",
+                "count": 3,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=firstname_exact%3AJohn"
+              },
+              {
+                "text": "Randall",
+                "count": 2,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=firstname_exact%3ARandall"
+              },
+              {
+                "text": "Nehru",
+                "count": 2,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=firstname_exact%3ANehru"
+              }
+            ],
+            "lastname": [
+              {
+                "text": "Porter",
+                "count": 2,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=lastname_exact%3APorter"
+              },
+              {
+                "text": "Odonnell",
+                "count": 2,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=lastname_exact%3AOdonnell"
+              },
+              {
+                "text": "Hood",
+                "count": 2,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=lastname_exact%3AHood"
+              }
+            ]
+          },
+          "dates": {
+            "created": [
+              {
+                "text": "2015-05-15T00:00:00",
+                "count": 100,
+                "narrow_url": "/api/v1/search/facets/?selected_facets=created_exact%3A2015-05-15+00%3A00%3A00"
+              }
+            ]
+          }
+        }
+
+
+Serializing faceted results
+---------------------------
+
+When a ``HaystackFacetSerializer`` class determines what fields to serialize, it will check
+the ``serialize_objects`` class attribute to see if it is ``True`` or ``False``. Setting this value to ``True``
+will add an additional ``objects`` field to the serialized results, which will contain the results for the
+faceted ``SearchQuerySet``. The results will be serialized using the view's ``serializer_class``.
+
+**Example faceted results with paginated serialized objects**
+
+.. code-block:: json
+
+    {
+      "fields": {
+        "firstname": [
+          {"...": "..."}
+        ],
+        "lastname": [
+          {"...": "..."}
+        ]
+      },
+      "dates": {
+        "created": [
+          {"...": "..."}
+        ]
+      },
+      "queries": {},
+      "objects": {
+        "count": 3,
+        "next": "http://example.com/api/v1/search/facets/?page=2&selected_facets=firstname_exact%3AJohn",
+        "previous": null,
+        "results": [
+          {
+            "lastname": "Baker",
+            "firstname": "John",
+            "full_name": "John Baker",
+            "text": "John Baker\n"
+          },
+          {
+            "lastname": "McClane",
+            "firstname": "John",
+            "full_name": "John McClane",
+            "text": "John McClane\n"
+          }
+        ]
+      }
+    }
+
+
+
+Setting up the view
+-------------------
+
+Any view that inherits the ``HaystackViewSet`` will have a special
+`action route <http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing>`_ added as
+``^<view-url>/facets/$``. This view action will not care about regular filtering but will by default use the
+``HaystackFacetFilter`` to perform filtering.
+
+.. note::
+
+    In order to avoid confusing the filtering mechanisms in Django Rest Framework, the ``HaystackGenericAPIView`` base
+    class has a couple of new hooks for dealing with faceting, namely:
+
+        - ``facet_filter_backends`` - A list of filter backends that will be used to apply faceting to the queryset.
+          Defaults to ``HaystackFacetFilter``, which should be sufficient in most cases.
+        - ``facet_serializer_class`` - The ``HaystackFacetSerializer`` subclass instance that will be used for
+          serializing the result.
+        - ``filter_facet_queryset()`` - Works exactly as the normal ``filter_queryset()`` method, but will only filter
+          on backends in the ``facet_filter_backends`` list.
+        - ``get_facet_serializer_class()`` - Returns the ``facet_serializer_class`` class attribute.
+        - ``get_facet_serializer()`` - Instantiates and returns the ``HaystackFacetSerializer`` class returned from
+          ``get_facet_serializer_class()``.
+
+
+In order to set up a view which can respond to regular queries under ie ``^search/$`` and faceted queries under
+``^search/facets/$``, we could do something like this.
+
+.. code-block:: python
+
+    class SearchPersonViewSet(HaystackViewSet):
+
+        index_models = [MockPerson]
+
+        # This will be used to filter and serialize regular queries as well
+        # as the results if the `facet_serializer_class` has the
+        # `serialize_objects = True` set.
+        serializer_class = SearchSerializer
+        filter_backends = [HaystackHighlightFilter, HaystackAutocompleteFilter]
+
+        # This will be used to filter and serialize faceted results
+        facet_serializer_class = PersonFacetSerializer  # See example above!
+        facet_filter_backends = [HaystackFacetFilter]   # This is the default facet filter, and
+                                                        # can be left out.
+
+
+Narrowing
+---------
+
+As we have seen in the examples above, the ``HaystackFacetSerializer`` will add a ``narrow_url`` attribute to each
+result it serializes. Follow that link to narrow the search result.
+
+The ``narrow_url`` is constructed like this:
+
+    - Read all query parameters from the request
+    - Get a list of ``selected_facets``
+    - Update the query parameters by adding the current item to ``selected_facets``
+    - Return a ``serializers.Hyperlink`` with URL encoded query parameters
+
+This means that for each drill-down performed, the original query parameters will be kept in order to make
+the ``HaystackFacetFilter`` happy. Additionally, all the previous ``selected_facets`` will be kept and applied
+to narrow the ``SearchQuerySet`` properly.
+
... 6758 lines suppressed ...

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



More information about the Python-modules-commits mailing list