.. _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)) []