searching.rst 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. .. _wagtailsearch_searching:
  2. =========
  3. Searching
  4. =========
  5. .. _wagtailsearch_searching_pages:
  6. Searching QuerySets
  7. ===================
  8. Wagtail search is built on Django's `QuerySet API <https://docs.djangoproject.com/en/1.8/ref/models/querysets/>`_. You should be able to search any Django QuerySet provided the model and the fields being filtered on have been added to the search index.
  9. Searching Pages
  10. ---------------
  11. Wagtail provides a shortcut for searching pages: the ``.search()`` ``QuerySet`` method. You can call this on any ``PageQuerySet``. For example:
  12. .. code-block:: python
  13. # Search future EventPages
  14. >>> from wagtail.wagtailcore.models import EventPage
  15. >>> EventPage.objects.filter(date__gt=timezone.now()).search("Hello world!")
  16. All other methods of ``PageQuerySet`` can be used with ``search()``. For example:
  17. .. code-block:: python
  18. # Search all live EventPages that are under the events index
  19. >>> EventPage.objects.live().descendant_of(events_index).search("Event")
  20. [<EventPage: Event 1>, <EventPage: Event 2>]
  21. .. note::
  22. The ``search()`` method will convert your ``QuerySet`` into an instance of one of Wagtail's ``SearchResults`` classes (depending on backend). This means that you must perform filtering before calling ``search()``.
  23. .. _wagtailsearch_images_documents_custom_models:
  24. Searching Images, Documents and custom models
  25. ---------------------------------------------
  26. Wagtail's document and image models provide a ``search`` method on their QuerySets, just as pages do:
  27. .. code-block:: python
  28. >>> from wagtail.wagtailimages.models import Image
  29. >>> Image.objects.filter(uploaded_by_user=user).search("Hello")
  30. [<Image: Hello>, <Image: Hello world!>]
  31. :ref:`Custom models <wagtailsearch_indexing_models>` can be searched by using the ``search`` method on the search backend directly:
  32. .. code-block:: python
  33. >>> from myapp.models import Book
  34. >>> from wagtail.wagtailsearch.backends import get_search_backend
  35. # Search books
  36. >>> s = get_search_backend()
  37. >>> s.search("Great", Book)
  38. [<Book: Great Expectations>, <Book: The Great Gatsby>]
  39. You can also pass a QuerySet into the ``search`` method which allows you to add filters to your search results:
  40. .. code-block:: python
  41. >>> from myapp.models import Book
  42. >>> from wagtail.wagtailsearch.backends import get_search_backend
  43. # Search books
  44. >>> s = get_search_backend()
  45. >>> s.search("Great", Book.objects.filter(published_date__year__lt=1900))
  46. [<Book: Great Expectations>]
  47. .. _wagtailsearch_specifying_fields:
  48. Specifying the fields to search
  49. -------------------------------
  50. By default, Wagtail will search all fields that have been indexed using ``index.SearchField``.
  51. This can be limited to a certian set of fields by using the ``fields`` keyword argument:
  52. .. code-block:: python
  53. # Search just the title field
  54. >>> EventPage.objects.search("Event", fields=["title"])
  55. [<EventPage: Event 1>, <EventPage: Event 2>]
  56. Changing search behaviour
  57. -------------------------
  58. Search operator
  59. ^^^^^^^^^^^^^^^
  60. The search operator specifies how search should behave when the user has typed in multiple search terms. There are two possible values:
  61. - "or" - The results must match at least one term (default for Elasticsearch)
  62. - "and" - The results must match all terms (default for database search)
  63. Both operators have benefits and drawbacks. The "or" operator will return many more results but will likely contain a lot of results that aren't relevent. The "and" operator only returns results that contain all search terms, but require the user to be more precise with their query.
  64. We recommend using the "or" operator when ordering by relevance and the "and" operator when ordering by anything else (note: the database backend doesn't currently support ordering by relevance).
  65. Here's an example of using the ``operator`` keyword argument:
  66. .. code-block:: python
  67. # The database contains a "Thing" model with the following items:
  68. # - Hello world
  69. # - Hello
  70. # - World
  71. # Search with the "or" operator
  72. >>> s = get_search_backend()
  73. >>> s.search("Hello world", Things, operator="or")
  74. # All records returned as they all contain either "hello" or "world"
  75. [<Thing: Hello World>, <Thing: Hello>, <Thing: World>]
  76. # Search with the "and" operator
  77. >>> s = get_search_backend()
  78. >>> s.search("Hello world", Things, operator="and")
  79. # Only "hello world" returned as that's the only item that contains both terms
  80. [<Thing: Hello world>]
  81. For page, image and document models, the ``operator`` keyword argument is also supported on the QuerySet's ``search`` method:
  82. .. code-block:: python
  83. >>> Page.objects.search("Hello world", operator="or")
  84. # All pages containing either "hello" or "world" are returned
  85. [<Page: Hello World>, <Page: Hello>, <Page: World>]
  86. Custom ordering
  87. ^^^^^^^^^^^^^^^
  88. By default, search results are ordered by relevance, if the backend supports it. To preserve the QuerySet's existing ordering, the ``order_by_relevance`` keyword argument needs to be set to ``False`` on the ``search()`` method.
  89. For example:
  90. .. code-block:: python
  91. # Get a list of events ordered by date
  92. >>> EventPage.objects.order_by('date').search("Event", order_by_relevance=False)
  93. # Events ordered by date
  94. [<EventPage: Easter>, <EventPage: Halloween>, <EventPage: Christmas>]
  95. .. _wagtailsearch_annotating_results_with_score:
  96. Annotating results with score
  97. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  98. For each matched result, Elasticsearch calculates a "score", which is a number
  99. that represents how relevant the result is based on the user's query. The
  100. results are usually ordered based on the score.
  101. There are some cases where having access to the score is useful (such as
  102. programmatically combining two queries for different models). You can add the
  103. score to each result by calling the ``.annotate_score(field)`` method on the
  104. ``SearchQuerySet``.
  105. For example:
  106. .. code-block:: python
  107. >>> events = EventPage.objects.search("Event").annotate_score("_score")
  108. >>> for event in events:
  109. ... print(event.title, event._score)
  110. ...
  111. ("Easter", 2.5),
  112. ("Haloween", 1.7),
  113. ("Christmas", 1.5),
  114. Note that the score itself is arbitrary and it is only useful for comparison
  115. of results for the same query.
  116. .. _wagtailsearch_frontend_views:
  117. An example page search view
  118. ===========================
  119. Here's an example Django view that could be used to add a "search" page to your site:
  120. .. code-block:: python
  121. # views.py
  122. from django.shortcuts import render
  123. from wagtail.wagtailcore.models import Page
  124. from wagtail.wagtailsearch.models import Query
  125. def search(request):
  126. # Search
  127. search_query = request.GET.get('query', None)
  128. if search_query:
  129. search_results = Page.objects.live().search(search_query)
  130. # Log the query so Wagtail can suggest promoted results
  131. Query.get(search_query).add_hit()
  132. else:
  133. search_results = Page.objects.none()
  134. # Render template
  135. return render(request, 'search_results.html', {
  136. 'search_query': search_query,
  137. 'search_results': search_results,
  138. })
  139. And here's a template to go with it:
  140. .. code-block:: html
  141. {% extends "base.html" %}
  142. {% load wagtailcore_tags %}
  143. {% block title %}Search{% endblock %}
  144. {% block content %}
  145. <form action="{% url 'search' %}" method="get">
  146. <input type="text" name="query" value="{{ search_query }}">
  147. <input type="submit" value="Search">
  148. </form>
  149. {% if search_results %}
  150. <ul>
  151. {% for result in search_results %}
  152. <li>
  153. <h4><a href="{% pageurl result %}">{{ result }}</a></h4>
  154. {% if result.search_description %}
  155. {{ result.search_description|safe }}
  156. {% endif %}
  157. </li>
  158. {% endfor %}
  159. </ul>
  160. {% elif search_query %}
  161. No results found
  162. {% else %}
  163. Please type something into the search box
  164. {% endif %}
  165. {% endblock %}
  166. Promoted search results
  167. =======================
  168. "Promoted search results" allow editors to explicitly link relevant content to search terms, so results pages can contain curated content in addition to results from the search engine.
  169. This functionality is provided by the :mod:`~wagtail.contrib.wagtailsearchpromotions` contrib module.