.. _wagtailsearch_indexing:
========
索引
========
一个模型要提供搜索功能,需要将它加入到查询索引中。所有的页面、图片和文档已经默认加入了,所以可以方便在这些对象上搜索。
在``Page`` 或 ``Image`` 子类中定义的字段可能需要加入到索引中,以方便用户在这些字段中搜索相应的关键字,有关这方面的内容请参考 :ref:`wagtailsearch_indexing_fields`。
对于那些自定义模型,如果需要在其上面实现搜索功能,请参考 :ref:`wagtailsearch_indexing_models`。
.. _wagtailsearch_indexing_update:
更新索引
==================
如果查询索引与数据库相分离(例如使用 Elasticsearch 进行存贮),需要实现两者之间的数据同步。 有两种方式实现数据同步:使用查询信号处理句柄或定期调用 ``update_index``。
为满足速度以及可靠性,最好两种方式都使用。
信号处理句柄
---------------
``wagtailsearch`` 信号处理句柄与所有索引模型的 save/delete 信号绑定。这将自动同步增加或删除底层平台(``WAGTAILSEARCH_BACKENDS``)中的相应数据。
信号处理句柄在 ``wagtail.search`` 应用加载时进行的注册。
在某些情况下不希望进行索引的自动处理,而是调用 ``update_index`` 命令完成索引,这时可采用如下两种方法之一来实现:
禁用一个模型的自动更新信号处理句柄
`````````````````````````````````````````````````
在特定的模型类中定义 ``search_auto_update = False`` 来禁用。
禁用一个底层平台/网站的信号处理句柄
`````````````````````````````````````````````````````````````````````
将配置文件中的 ``AUTO_UPDATE`` 置为 ``False`` 来实现网站级全部禁用索引自动同步功能。
有关 ``AUTO_UPDATE`` 配置的更多文档请参考 :ref:`wagtailsearch_backends_auto_update`。
``update_index`` 命令
----------------------------
Wagtail 提供了命令行方式的指令来实现索引重建。
:code:`./manage.py update_index`
推荐每周执行一次这个命令,以及在发生以下情况时执行这个命令:
- 有页面通过脚本生成时(例如,执行了导入操作)
- 模型或搜索配置发生变化时
在执行更新索引命令时,界面执行的查询有可能返回空值,所以尽量不要在网站运行的高峰期执行命令。
.. note::
``update_index`` 命令也使用别名 ``wagtail_update_index`` 命令,主要防止与其它安装包冲突,(例如 `Haystack `_) 。
在这种情况下,其它安装在 ``INSTALLED_APPS`` 定义时应放在 ``wagtail.search`` 之前,这样就不会使用 Wagtail 的 ``update_index`` 命令了。
.. _wagtailsearch_indexing_fields:
额外字段索引
=====================
.. warning::
额外字段索引只在使用 :ref:`wagtailsearch_backends_elasticsearch` 或 :ref:`wagtailsearch_backends_postgresql` 情况下才能使用。:ref:`wagtailsearch_backends_database` 不支持额外字段索引功能。
如果项目使用数据库做为底层索引平台,通过 ``search_fields`` 定义的字段将被忽略。
基于 ``Page`` 类定义的页面模型只有定义到 ``search_fields`` 属性的字段,在搜索或过滤时才纳入搜寻范围。
可以在子类中重新定义``search_fields` 并增加 ``SearchField``/``FilterField`` 对象到其中。
实例
-------
定义 ``EventPage`` 模型,声明 ``description`` 和 ``date`` 两个字段。``description`` 用 ``SearchField`` 声明并加入索引,``date`` 用 ``FilterField`` 声明并加入索引。
.. code-block:: python
from wagtail.search import index
from django.utils import timezone
class EventPage(Page):
description = models.TextField()
date = models.DateField()
search_fields = Page.search_fields + [ # Inherit search_fields from Page
index.SearchField('description'),
index.FilterField('date'),
]
# Get future events which contain the string "Christmas" in the title or description
>>> EventPage.objects.filter(date__gt=timezone.now()).search("Christmas")
.. _wagtailsearch_index_searchfield:
``index.SearchField``
---------------------
使用全检索的方式来匹配关键词,一般用于文本字段。
选项
```````
- **partial_match-部分匹配** (``boolean``) - 设为真时,匹配到单词部分字母时即为匹配成功。例如,页面标题即采用这种形式,当用户输入 ``Hel``进行搜索时,标题为 ``Hello World!`` 的页面就匹配成功。
- **boost** (``int/float``) - 字段设置这个属性时可以定义字段匹配权重,更大的数字会表明匹配的权重高于其它字段。通过页面标题的值为 2,而其它字段的数值为 1.
- **es_extra** (``dict``) - 字段设置使用 Elasticsearch 时的字段映射属性字典,可以使用这个字段定义 Wagtail 尚未支持,但 Elasticsearch 支持的参数。
.. _wagtailsearch_index_filterfield:
``index.FilterField``
---------------------
增加不使用全文搜索的字段索引。使用过滤器来过滤查询结果集。
.. _wagtailsearch_index_relatedfields:
``index.RelatedFields``
-----------------------
允许从相关联的对象中索引字段,可以使用所有类型的类联字段,包括逆向访问。
例如,定义的图书模型有 ``ForeignKey`` 关联到作者,这时可以使用作者的 ``name`` 以及 ``date_of_birth`` 字段来做为索引字段:
.. code-block:: python
from wagtail.search import index
class Book(models.Model, index.Indexed):
...
search_fields = [
index.SearchField('title'),
index.FilterField('published_date'),
index.RelatedFields('author', [
index.SearchField('name'),
index.FilterField('date_of_birth'),
]),
]
这样可以使用作者姓名来搜索图书。
也可以反过来使用,通过出版的图书名来搜索作者:
.. code-block:: python
from wagtail.search import index
class Author(models.Model, index.Indexed):
...
search_fields = [
index.SearchField('name'),
index.FilterField('date_of_birth'),
index.RelatedFields('books', [
index.SearchField('title'),
index.FilterField('published_date'),
]),
]
.. topic::使用 ``index.RelatedFields`` 过滤
使用 ``QuerySet`` API 是不能实现通过 ``index.RelatedFields`` 内的 ``index.FilterFields`` 查询的。如果是基于 Elasticsearch 索引的,可以自定义关联查询方法。
在 Wagtail 的将来版本中考虑在 ``QuerySet`` API 中实现基于 ``index.RelatedFields`` 的过滤器。
.. _wagtailsearch_indexing_callable_fields:
索引调用方法及属性
---------------------------------------
.. note::
基于 :ref:`wagtailsearch_backends_database` 的配置,不支持如下这些功能。
Search/filter 字段不一定是 Django 模型的字段类型,它们可以是类中的任意方法或属性。
用处之一是为``get_*_display`` 方法建立索引,Django 产生字段的选项列表。
.. code-block:: python
from wagtail.search import index
class EventPage(Page):
IS_PRIVATE_CHOICES = (
(False, "Public"),
(True, "Private"),
)
is_private = models.BooleanField(choices=IS_PRIVATE_CHOICES)
search_fields = Page.search_fields + [
# Index the human-readable string for searching.
index.SearchField('get_is_private_display'),
# Index the boolean value for filtering.
index.FilterField('is_private'),
]
可调用的的方法也提供了从关联模型提供索引数据的功能。参考 :ref:`inline_panels` 的模型, 下面代码实现通过关联链接索引每本书的页面功能:
.. code-block:: python
class BookPage(Page):
# ...
def get_related_link_titles(self):
# Get list of titles and concatenate them
return '\n'.join(self.related_links.all().values_list('name', flat=True))
search_fields = Page.search_fields + [
# ...
index.SearchField('get_related_link_titles'),
]
.. _wagtailsearch_indexing_models:
索引定制模型
======================
任何 Django 模型可用来索引和搜索。
实现的方法是从 ``index.Indexed`` 继承,然后向模型中增加 ``search_fields`` 属性。
.. code-block:: python
from wagtail.search import index
class Book(index.Indexed, models.Model):
title = models.CharField(max_length=255)
genre = models.CharField(max_length=255, choices=GENRE_CHOICES)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
published_date = models.DateTimeField()
search_fields = [
index.SearchField('title', partial_match=True, boost=10),
index.SearchField('get_genre_display'),
index.FilterField('genre'),
index.FilterField('author'),
index.FilterField('published_date'),
]
# As this model doesn't have a search method in its QuerySet, we have to call search directly on the backend
>>> from wagtail.search.backends import get_search_backend
>>> s = get_search_backend()
# Run a search for a book by Roald Dahl
>>> roald_dahl = Author.objects.get(name="Roald Dahl")
>>> s.search("chocolate factory", Book.objects.filter(author=roald_dahl))
[]