summaryrefslogtreecommitdiff
path: root/docs/ref
diff options
context:
space:
mode:
Diffstat (limited to 'docs/ref')
-rw-r--r--docs/ref/contrib/postgres/index.txt1
-rw-r--r--docs/ref/contrib/postgres/search.txt191
2 files changed, 192 insertions, 0 deletions
diff --git a/docs/ref/contrib/postgres/index.txt b/docs/ref/contrib/postgres/index.txt
index fe5b3be2ab..d04ed14889 100644
--- a/docs/ref/contrib/postgres/index.txt
+++ b/docs/ref/contrib/postgres/index.txt
@@ -37,4 +37,5 @@ release. Some fields require higher versions.
functions
lookups
operations
+ search
validators
diff --git a/docs/ref/contrib/postgres/search.txt b/docs/ref/contrib/postgres/search.txt
new file mode 100644
index 0000000000..21f41ff534
--- /dev/null
+++ b/docs/ref/contrib/postgres/search.txt
@@ -0,0 +1,191 @@
+================
+Full text search
+================
+
+.. versionadded:: 1.10
+
+The database functions in the ``django.contrib.postgres.search`` module ease
+the use of PostgreSQL's `full text search engine
+<http://www.postgresql.org/docs/current/static/textsearch.html>`_.
+
+For the examples in this document, we'll use the models defined in
+:doc:`/topics/db/queries`.
+
+.. seealso::
+
+ For a high-level overview of searching, see the :doc:`topic documentation
+ </topics/db/search>`.
+
+.. currentmodule:: django.contrib.postgres.search
+
+The ``search`` lookup
+=====================
+
+.. fieldlookup:: search
+
+The simplest way to use full text search is to search a single term against a
+single column in the database. For example::
+
+ >>> Entry.objects.filter(body_text__search='Cheese')
+ [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
+
+This creates a ``to_tsvector`` in the database from the ``body_text`` field
+and a ``plainto_tsquery`` from the search term ``'Potato'``, both using the
+default database search configuration. The results are obtained by matching the
+query and the vector.
+
+To use the ``search`` lookup, ``'django.contrib.postgres'`` must be in your
+:setting:`INSTALLED_APPS`.
+
+``SearchVector``
+================
+
+.. class:: SearchVector(\*expressions, config=None, weight=None)
+
+Searching against a single field is great but rather limiting. The ``Entry``
+instances we're searching belong to a ``Blog``, which has a ``tagline`` field.
+To query against both fields, use a ``SearchVector``::
+
+ >>> from django.contrib.postgres.search import SearchVector
+ >>> Entry.objects.annotate(
+ ... search=SearchVector('body_text', 'blog__tagline'),
+ ... ).filter(search='Cheese')
+ [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
+
+The arguments to ``SearchVector`` can be any
+:class:`~django.db.models.Expression` or the name of a field. Multiple
+arguments will be concatenated together using a space so that the search
+document includes them all.
+
+``SearchVector`` objects can be combined together, allowing you to reuse them.
+For example::
+
+ >>> Entry.objects.annotate(
+ ... search=SearchVector('body_text') + SearchVector('blog__tagline'),
+ ... ).filter(search='Cheese')
+ [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
+
+See :ref:`postgresql-fts-search-configuration` and
+:ref:`postgresql-fts-weighting-queries` for an explanation of the ``config``
+and ``weight`` parameters.
+
+``SearchQuery``
+===============
+
+.. class:: SearchQuery(value, config=None)
+
+``SearchQuery`` translates the terms the user provides into a search query
+object that the database compares to a search vector. By default, all the words
+the user provides are passed through the stemming algorithms, and then it
+looks for matches for all of the resulting terms.
+
+``SearchQuery`` terms can be combined logically to provide more flexibility::
+
+ >>> from django.contrib.postgres.search import SearchQuery
+ >>> SearchQuery('potato') & SearchQuery('ireland') # potato AND ireland
+ >>> SearchQuery('potato') | SearchQuery('penguin') # potato OR penguin
+ >>> ~SearchQuery('sausage') # NOT sausage
+
+See :ref:`postgresql-fts-search-configuration` for an explanation of the
+``config`` parameter.
+
+``SearchRank``
+==============
+
+.. class:: SearchRank(vector, query, weights=None)
+
+So far, we've just returned the results for which any match between the vector
+and the query are possible. It's likely you may wish to order the results by
+some sort of relevancy. PostgreSQL provides a ranking function which takes into
+account how often the query terms appear in the document, how close together
+the terms are in the document, and how important the part of the document is
+where they occur. The better the match, the higher the value of the rank. To
+order by relevancy::
+
+ >>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
+ >>> vector = SearchVector('body_text')
+ >>> query = SearchQuery('cheese')
+ >>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by('-rank')
+ [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
+
+See :ref:`postgresql-fts-weighting-queries` for an explanation of the
+``weights`` parameter.
+
+.. _postgresql-fts-search-configuration:
+
+Changing the search configuration
+=================================
+
+You can specify the ``config`` attribute to a :class:`SearchVector` and
+:class:`SearchQuery` to use a different search configuration. This allows using
+a different language parsers and dictionaries as defined by the database::
+
+ >>> from django.contrib.postgres.search import SearchQuery, SearchVector
+ >>> Entry.objects.annotate(
+ ... search=SearchVector('body_text', config='french'),
+ ... ).filter(search=SearchQuery('œuf', config='french'))
+ [<Entry: Pain perdu>]
+
+The value of ``config`` could also be stored in another column::
+
+ >>> from djanog.db.models import F
+ >>> Entry.objects.annotate(
+ ... search=SearchVector('body_text', config=F('blog__language')),
+ ... ).filter(search=SearchQuery('œuf', config=F('blog__language')))
+ [<Entry: Pain perdu>]
+
+.. _postgresql-fts-weighting-queries:
+
+Weighting queries
+=================
+
+Every field may not have the same relevance in a query, so you can set weights
+of various vectors before you combine them::
+
+ >>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
+ >>> vector = SearchVector('body_text', weight='A') + SearchVector('blog__tagline', weight='B')
+ >>> query = SearchQuery('cheese')
+ >>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by('rank')
+
+The weight should be one of the following letters: D, C, B, A. By default,
+these weights refer to the numbers ``0.1``, ``0.2``, ``0.4``, and ``1.0``,
+respectively. If you wish to weight them differently, pass a list of four
+floats to :class:`SearchRank` as ``weights`` in the same order above::
+
+ >>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
+ >>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by('-rank')
+
+Performance
+===========
+
+Special database configuration isn't necessary to use any of these functions,
+however, if you're searching more than a few hundred records, you're likely to
+run into performance problems. Full text search is a more intensive process
+than comparing the size of an integer, for example.
+
+In the event that all the fields you're querying on are contained within one
+particular model, you can create a functional index which matches the search
+vector you wish to use. For example:
+
+.. code-block:: sql
+
+ CREATE INDEX body_text_search ON blog_entry (to_tsvector(body_text));
+
+This index will then be used by subsequent queries. In many cases this will be
+sufficient.
+
+``SearchVectorField``
+---------------------
+
+.. class:: SearchVectorField
+
+If this approach becomes too slow, you can add a ``SearchVectorField`` to your
+model. You'll need to keep it populated with triggers, for example, as
+described in the `PostgreSQL documentation`_. You can then query the field as
+if it were an annotated ``SearchVector``::
+
+ >>> Entry.objects.update(search_vector=SearchVector('body_text'))
+ >>> Entry.objects.filter(search_vector='potato')
+ [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
+
+.. _PostgreSQL documentation: http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS