[Python-modules-commits] [drf-haystack] 01/10: Import drf-haystack_1.6.1.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Tue Mar 21 12:13:41 UTC 2017


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

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

commit 2f1644596809464c3afe3363e8ce24f58b31f26d
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Tue Mar 21 12:48:23 2017 +0100

    Import drf-haystack_1.6.1.orig.tar.gz
---
 MANIFEST.in                                        |   2 +-
 PKG-INFO                                           |   6 +-
 README.md                                          |  66 ++
 README.rst                                         |  52 --
 docs/{basic_usage.rst => 01_intro.rst}             |  49 +-
 docs/02_autocomplete.rst                           |  48 ++
 docs/03_geospatial.rst                             |  86 ++
 docs/04_highlighting.rst                           | 132 +++
 docs/05_more_like_this.rst                         |  64 ++
 docs/06_term_boost.rst                             |  55 ++
 docs/07_faceting.rst                               | 316 +++++++
 docs/08_permissions.rst                            |  34 +
 docs/09_multiple_indexes.rst                       | 168 ++++
 docs/10_tips_n_tricks.rst                          | 101 +++
 docs/advanced_usage.rst                            | 936 ---------------------
 docs/apidoc/drf_haystack.rst                       |  64 ++
 docs/apidoc/modules.rst                            |   7 +
 docs/conf.py                                       |  23 +-
 docs/index.rst                                     | 109 ++-
 drf_haystack.egg-info/PKG-INFO                     |   6 +-
 drf_haystack.egg-info/SOURCES.txt                  |  22 +-
 drf_haystack.egg-info/pbr.json                     |   1 +
 drf_haystack.egg-info/requires.txt                 |   6 +-
 drf_haystack/__init__.py                           |   2 +-
 drf_haystack/constants.py                          |   4 +
 drf_haystack/fields.py                             |  51 +-
 drf_haystack/filters.py                            | 377 +++------
 drf_haystack/generics.py                           | 112 +--
 drf_haystack/mixins.py                             | 122 +++
 drf_haystack/query.py                              | 311 +++++++
 drf_haystack/serializers.py                        | 330 +++++---
 drf_haystack/viewsets.py                           |  52 +-
 requirements.txt                                   |   6 +-
 setup.cfg                                          |   2 +-
 setup.py                                           |  10 +-
 tests/__init__.py                                  |  11 +-
 tests/mockapp/apps.py                              |   9 +
 tests/mockapp/fixtures/mockperson.json             | 702 +++++++++-------
 tests/mockapp/migrations/0004_load_fixtures.py     |   1 +
 .../migrations/0005_mockperson_birthdate.py        |  21 +
 tests/mockapp/models.py                            |  12 +
 tests/mockapp/search_indexes.py                    |  11 +
 tests/mockapp/serializers.py                       |  10 +-
 tests/mockapp/views.py                             |  28 +-
 tests/runtests.py                                  |   7 +-
 tests/settings.py                                  |  49 +-
 tests/test_filters.py                              |  74 +-
 tests/test_serializers.py                          | 391 ++++-----
 tests/test_viewsets.py                             | 128 ++-
 tests/urls.py                                      |  14 +-
 tox.ini                                            | 132 +--
 51 files changed, 3006 insertions(+), 2326 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index 4fb1247..b8951d5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include README.rst
+include README.md
 include LICENSE.txt
 include requirements.txt
 include tox.ini
diff --git a/PKG-INFO b/PKG-INFO
index 4a054d3..857c785 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: drf-haystack
-Version: 1.5.6
+Version: 1.6.1
 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
+Author: Rolf Håvard Blindheim
+Author-email: rolf.blindheim at inonit.no
 License: MIT License
 Download-URL: https://github.com/inonit/drf-haystack.git
 Description: Implements a ViewSet, FiltersBackends and Serializers in order to play nice with Haystack.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0a5b798
--- /dev/null
+++ b/README.md
@@ -0,0 +1,66 @@
+Haystack for Django REST Framework
+==================================
+
+Build status
+------------
+
+[![Build Status](https://travis-ci.org/inonit/drf-haystack.svg?branch=master)](https://travis-ci.org/inonit/drf-haystack)
+[![Coverage Status](https://coveralls.io/repos/github/inonit/drf-haystack/badge.svg?branch=master)](https://coveralls.io/github/inonit/drf-haystack?branch=master)
+[![PyPI version](https://badge.fury.io/py/drf-haystack.svg)](https://badge.fury.io/py/drf-haystack)
+[![Documentation Status](https://readthedocs.org/projects/drf-haystack/badge/?version=latest)](http://drf-haystack.readthedocs.io/en/latest/?badge=latest)
+
+
+About
+-----
+
+Small library which tries to simplify integration of Haystack with Django REST Framework.
+Fresh [documentation available](https://drf-haystack.readthedocs.io/en/latest/>) on Read the docs!
+
+Supported versions
+------------------
+
+- Python 2.7+ and Python 3.4+
+- [All supported versions of Django](https://www.djangoproject.com/download/#supported-versions>)
+- Haystack 2.5 and above
+- Django REST Framework 3.2 and above
+    
+
+Installation
+------------
+
+    $ pip install drf-haystack
+
+Supported features
+------------------
+We aim to support most features Haystack does (or at least those which can be used in a REST API).
+Currently we support:
+
+- Autocomplete
+- Boost (Experimental)
+- Faceting
+- Geo Spatial Search
+- Highlighting
+- More Like This
+    
+Show me more!
+-------------
+
+```
+from drf_haystack.serializers import HaystackSerializer
+from drf_haystack.viewsets import HaystackViewSet
+
+from myapp.search_indexes import PersonIndex  # BYOI™ (Bring Your Own Index)
+
+# Serializer
+class PersonSerializer(HaystackSerializer):
+    class Meta:
+        index_classes = [PersonIndex]
+        fields = ["firstname", "lastname", "full_name"]
+
+# ViewSet
+class PersonSearchViewSet(HaystackViewSet):
+    index_models = [Person]
+    serializer_class = PersonSerializer
+```
+
+That's it, you're good to go. Hook it up to a DRF router and happy searching!
diff --git a/README.rst b/README.rst
deleted file mode 100644
index eb4b3f7..0000000
--- a/README.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-Haystack for Django REST Framework
-==================================
-
-Build status
-------------
-
-.. image:: https://travis-ci.org/inonit/drf-haystack.svg?branch=master
-    :target: https://travis-ci.org/inonit/drf-haystack
-
-.. image:: https://readthedocs.org/projects/drf-haystack/badge/?version=latest
-    :target: https://readthedocs.org/projects/drf-haystack/?badge=latest
-    :alt: Documentation Status
-    
-.. image:: https://pypip.in/d/drf-haystack/badge.png
-    :target: https://pypi.python.org/pypi/drf-haystack
-
-About
------
-
-Small library which tries to simplify integration of Haystack with Django REST Framework.
-Contains a Generic ViewSet, a Serializer and a couple of Filters in order to make search as
-painless as possible.
-
-Fresh `documentation available <http://drf-haystack.readthedocs.org/en/latest/>`_ on Read the docs!
-
-
-
-Supported Python and Django versions
-------------------------------------
-
-Tested with the following configurations:
-
-    - Python 2.6
-        - Django 1.5 and 1.6
-    - Python 2.7, 3.3 and 3.4
-        - Django 1.5, 1.6, 1.7 and 1.8
-
-Installation
-------------
-
-    $ pip install drf-haystack
-
-Supported features
-------------------
-We aim to support most features Haystack does (or at least those which can be used in a REST API).
-Currently we support:
-
-    * Autocomplete
-    * GEO Spatial searching
-    * Highlighting
-    * More Like This
-    * Faceting
diff --git a/docs/basic_usage.rst b/docs/01_intro.rst
similarity index 81%
rename from docs/basic_usage.rst
rename to docs/01_intro.rst
index 66a5185..719ee96 100644
--- a/docs/basic_usage.rst
+++ b/docs/01_intro.rst
@@ -53,7 +53,7 @@ Let's say we have an app which contains a model `Location`. It could look someth
 search_indexes.py
 -----------------
 
-We would have to make a `search_indexes.py` file for haystack to pick it up.
+We would have to make a ``search_indexes.py`` file for haystack to pick it up.
 
 .. code-block:: python
 
@@ -131,7 +131,7 @@ For a generic Django REST Framework view, you could do something like this.
         # a way to filter out those of no interest for this particular view.
         # (Translates to `SearchQuerySet().models(*index_models)` behind the scenes.
         index_models = [Location]
-            
+
         serializer_class = LocationSerializer
 
 
@@ -183,39 +183,30 @@ basic search by querying any of the field included in the `fields` attribute on
 Would perform a query looking up all documents where the `city field` equals "Oslo".
 
 
-Regular Search View
-===================
-
-Sometimes you might not need all the bells and whistles of a `ViewSet`,
-but can do with a regular view. In such scenario you could do something like this.
-
-.. code-block:: python
-
-    #
-    # views.py
-    #
-
-    from rest_framework.mixins import ListModelMixin
-    from drf_haystack.generics import HaystackGenericAPIView
+Field Lookups
+.............
 
+You can also use field lookups in your field queries. See the
+Haystack `field lookups <https://django-haystack.readthedocs.io/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:
 
-    class SearchView(ListModelMixin, HaystackGenericAPIView):
+.. code-block:: none
 
-        serializer_class = LocationSerializer
+    http://example.com/api/v1/location/search/?city__startswith=Os
 
-        def get(self, request, *args, **kwargs):
-            return self.list(request, *args, **kwargs)
+This would perform a query looking up all documents where the `city field` started with "Os".
+You might get "Oslo", "Osaka", and "Ostrava".
 
+Term Negation
+.............
 
-    #
-    # urls.py
-    #
+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``.
 
-    urlpatterns = (
-       ...
-        url(r'^search/', SearchView.as_view()),
-       ...
-    )
+.. 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
 
-Next, check out the :ref:`advanced-usage-label`.
diff --git a/docs/02_autocomplete.rst b/docs/02_autocomplete.rst
new file mode 100644
index 0000000..98ac0a1
--- /dev/null
+++ b/docs/02_autocomplete.rst
@@ -0,0 +1,48 @@
+.. _autocomplete-label:
+
+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 :class:`drf_haystack.filters.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 :class:`drf_haystack.filters.HaystackAutocompleteFilter` reduce query
+parameters down to a single filter (using an ``SQ`` object), and performs a bitwise ``AND``.
+
+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
+
+    from drf_haystack.filters import HaystackAutocompleteFilter
+    from drf_haystack.serializers import HaystackSerializer
+    from drf_haystack.viewsets import HaystackViewSet
+
+    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]
+
diff --git a/docs/03_geospatial.rst b/docs/03_geospatial.rst
new file mode 100644
index 0000000..1f9995e
--- /dev/null
+++ b/docs/03_geospatial.rst
@@ -0,0 +1,86 @@
+.. _geospatial-label:
+
+GEO spatial locations
+=====================
+
+Some search backends support geo spatial searching. In order to take advantage of this we
+have the :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-c1 (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`` (See :ref:`search-index-example-label` for example).
+   If your ``LocationField`` is named something other than ``coordinates``, subclass the ``HaystackGEOSpatialFilter``
+   and make sure to set the :attr:`drf_haystack.filters.HaystackGEOSpatialFilter.point_field` to the name of the field.
+#. 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"]
+
+        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]
+
+
+**Example subclassing the HaystackGEOSpatialFilter**
+
+Assuming that your ``LocationField`` is named ``location``.
+
+.. code-block:: python
+
+    from drf_haystack.filters import HaystackGEOSpatialFilter
+
+    class CustomHaystackGEOSpatialFilter(HaystackGEOSpatialFilter):
+        point_field = 'location'
+
+
+    class LocationGeoSearchViewSet(HaystackViewSet):
+
+        index_models = [Location]
+        serializer_class = LocationSerializer
+        filter_backends = [CustomHaystackGEOSpatialFilter]
+
+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.
diff --git a/docs/04_highlighting.rst b/docs/04_highlighting.rst
new file mode 100644
index 0000000..890538e
--- /dev/null
+++ b/docs/04_highlighting.rst
@@ -0,0 +1,132 @@
+.. _highlighting-label:
+
+Highlighting
+============
+
+Haystack supports two kinds of `Highlighting <https://django-haystack.readthedocs.io/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
+:class:`drf_haystack.filters.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.
+
+
+**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 :class:`drf_haystack.serializers.HighlighterMixin` mixin class, and must be applied on the ``Serializer``.
+This is somewhat slower, but more configurable than the :class:`drf_haystack.filters.HaystackHighlightFilter` filter class.
+
+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"
+        }
+    ]
diff --git a/docs/05_more_like_this.rst b/docs/05_more_like_this.rst
new file mode 100644
index 0000000..2d9d34f
--- /dev/null
+++ b/docs/05_more_like_this.rst
@@ -0,0 +1,64 @@
+.. _more-like-this-label:
+
+More Like This
+==============
+
+Some search backends supports ``More Like This`` features. In order to take advantage of this,
+we have a mixin class :class:`drf_haystack.mixins.MoreLikeThisMixin`, which will append a ``more-like-this``
+detail route 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
+:class:`drf_haystack.mixins.MoreLikeThisMixin` 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"]
+
+
+    class SearchViewSet(MoreLikeThisMixin, HaystackViewSet):
+        index_models = [Person]
+        serializer_class = SearchSerializer
+
+
+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/"
+        }
+    ]
diff --git a/docs/06_term_boost.rst b/docs/06_term_boost.rst
new file mode 100644
index 0000000..cc4302e
--- /dev/null
+++ b/docs/06_term_boost.rst
@@ -0,0 +1,55 @@
+.. _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.io/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 :class:`drf_haystack.filters.HaystackBoostFilter` filter backend.
+The ``HaystackBoostFilter`` does not perform any filtering by itself, and should therefore be combined with
+some other filter that does, for example the :class:`drf_haystack.filters.HaystackFilter`.
+
+.. code-block:: python
+
+    from drf_haystack.filters import HaystackBoostFilter
+
+    class SearchViewSet(HaystackViewSet):
+        ...
+        filter_backends = [HaystackFilter, 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``.
diff --git a/docs/07_faceting.rst b/docs/07_faceting.rst
new file mode 100644
index 0000000..1425fe6
--- /dev/null
+++ b/docs/07_faceting.rst
@@ -0,0 +1,316 @@
+.. _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
+:class:`drf_haystack.mixins.FacetMixin` 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 <https://django-haystack.readthedocs.io/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 :class:`drf_haystack.serializers.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.
+
+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": "http://example.com/api/v1/search/facets/?selected_facets=firstname_exact%3AJohn"
+              },
+              {
+                "text": "Randall",
+                "count": 2,
+                "narrow_url": "http://example.com/api/v1/search/facets/?selected_facets=firstname_exact%3ARandall"
+              },
+              {
+                "text": "Nehru",
+                "count": 2,
+                "narrow_url": "http://example.com/api/v1/search/facets/?selected_facets=firstname_exact%3ANehru"
+              }
+            ],
+            "lastname": [
+              {
+                "text": "Porter",
+                "count": 2,
+                "narrow_url": "http://example.com/api/v1/search/facets/?selected_facets=lastname_exact%3APorter"
+              },
+              {
+                "text": "Odonnell",
+                "count": 2,
+                "narrow_url": "http://example.com/api/v1/search/facets/?selected_facets=lastname_exact%3AOdonnell"
+              },
+              {
+                "text": "Hood",
+                "count": 2,
+                "narrow_url": "http://example.com/api/v1/search/facets/?selected_facets=lastname_exact%3AHood"
+              }
+            ]
+          },
+          "dates": {
+            "created": [
+              {
+                "text": "2015-05-15T00:00:00",
+                "count": 100,
+                "narrow_url": "http://example.com/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 by default be serialized using the view's ``serializer_class``.
+If you wish to use a different serializer for serializing the results, set the
+:attr:`drf_haystack.mixins.FacetMixin.facet_objects_serializer_class` class attribute to whatever serializer you want
+to use, or override the :meth:`drf_haystack.mixins.FacetMixin.get_facet_objects_serializer_class` method.
+
+**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 :class:`drf_haystack.mixins.FacetMixin` 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 ``FacetMixin``
+    class has a couple of hooks for dealing with faceting, namely:
+
+        - :attr:`drf_haystack.mixins.FacetMixin.facet_filter_backends` - A list of filter backends that will be used to
+          apply faceting to the queryset. Defaults to :class:drf_haystack.filters.HaystackFacetFilter`, which should be
+          sufficient in most cases.
+        - :attr:`drf_haystack.mixins.FacetMixin.facet_serializer_class` - The :class:`drf_haystack.serializers.HaystackFacetSerializer`
+          instance that will be used for serializing the result.
+        - :attr:`drf_haystack.mixins.FacetMixin.facet_objects_serializer_class` - Optional. Set to the serializer class
+          which should be used for serializing faceted objects. If not set, defaults to ``self.serializer_class``.
+        - :attr:`drf_haystack.mixins.FacetMixin.filter_facet_queryset()` - Works exactly as the normal
+          :meth:`drf_haystack.generics.HaystackGenericAPIView.filter_queryset` method, but will only filter on
+          backends in the ``self.facet_filter_backends`` list.
+        - :meth:`drf_haystack.mixins.FacetMixin.get_facet_serializer_class` - Returns the ``self.facet_serializer_class``
+          class attribute.
+        - :meth:`drf_haystack.mixins.FacetMixin.get_facet_serializer` - Instantiates and returns the
+          :class:`drf_haystack.serializers.HaystackFacetSerializer` class returned from
+          :meth:`drf_haystack.mixins.FacetMixin.get_facet_serializer_class` method.
+        - :meth:`drf_haystack.mixins.FacetMixin.get_facet_objects_serializer` - Instantiates and returns the serializer
+          class which will be used to serialize faceted objects.
+        - :meth:`drf_haystack.mixins.FacetMixin.get_facet_objects_serializer_class` - Returns the
+          ``self.facet_objects_serializer_class``, or if not set, the ``self.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.
+
+We can also change the query param text from ``selected_facets`` to our own choice like ``params`` or ``p``. For this 
+to make happen please provide ``facet_query_params_text`` attribute as shown in the example. 
+
+.. code-block:: python
+
+    class SearchPersonViewSet(FacetMixin, 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.
+        facet_query_params_text = 'params' #Default is 'selected_facets'
+
+
+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``
+    - Pop the :attr:`drf_haystack.serializers.HaystackFacetSerializer.paginate_by_param` parameter if any in order to
+      always start at the first page if returning a paginated result.
+    - Return a ``serializers.Hyperlink`` with URL encoded query parameters
... 6546 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