.. _wagtailsearch_searching: ========= 搜索 ========= .. _wagtailsearch_searching_pages: 查询结果集 =================== Wagtail 搜索基于 Django 的 `QuerySet API `_ 。在开发过程中可以对加入查询索引的字段采用类似于 Django 模型及字段过滤的操作。 搜索页面 --------------- Wagtail 提供页面搜索的快捷方法:``.search()`` 和 ``QuerySet``。这两个方法可以任何 ``PageQuerySet`` 对象上使用。例如: .. code-block:: python # Search future EventPages 在未来事件页中搜索 >>> from wagtail.core.models import EventPage >>> EventPage.objects.filter(date__gt=timezone.now()).search("Hello world!") ``PageQuerySet`` 其它方法也可以结合 ``search()`` 使用。例如: .. code-block:: python # Search all live EventPages that are under the events index 在事件索引页下属页面中搜索 >>> EventPage.objects.live().descendant_of(events_index).search("Event") [, ] .. note:: ``search()`` 方法将原来 ``QuerySet`` 转换成 Wagtail 的 ``SearchResults`` 类实例 (依赖于底层实现平台)。这意味着过滤操作必须在 ``search()`` 执行之前。 .. _wagtailsearch_images_documents_custom_models: 搜索图片、文档及定制的模型 --------------------------------------------- Wagtail 的文档和图片各自模型 QuerySets 中定义了 ``search``,和页面类似: .. code-block:: python >>> from wagtail.images.models import Image >>> Image.objects.filter(uploaded_by_user=user).search("Hello") [, ] :ref:`自定义模型 ` 可以通过底层平台 ``search`` 方法来调用: .. code-block:: python >>> from myapp.models import Book >>> from wagtail.search.backends import get_search_backend # Search books >>> s = get_search_backend() >>> s.search("Great", Book) [, ] 调用 ``search`` 方法时可以传递一个 QuerySet 参数对搜索结果进行过滤: .. code-block:: python >>> from myapp.models import Book >>> from wagtail.search.backends import get_search_backend # Search books >>> s = get_search_backend() >>> s.search("Great", Book.objects.filter(published_date__year__lt=1900)) [] .. _wagtailsearch_specifying_fields: 只搜索特定字段 ------------------------------- 缺省情况下,搜索会匹配 ``index.SearchField`` 中定义的所有字段。 如果只想对其中个别字段进行搜索,可使用 ``fields`` 参数定义所要搜索的字段集合: .. code-block:: python # Search just the title field >>> EventPage.objects.search("Event", fields=["title"]) [, ] .. _wagtailsearch_faceted_search: 分面检索(Faceted search) -------------------------- Wagtail 支持分面检索,分面检索基于可分类字段(如分类或页面类型)。 ``.facet(field_name)`` 方法返加一个 ``OrderedDict`` 对象。键是给定字段相关联对象的 ID,值是每个 ID 引用的次数。结果按引用次数降序排序。 例如,按页面类型查看搜索结果中每种类型有多少页面: .. code-block:: python >>> Page.objects.search("Test").facet("content_type_id") # Note: The keys correspond to the ID of a ContentType object; the values are the # number of pages returned for that type OrderedDict([ ('2', 4), # 4 pages have content_type_id == 2 ('1', 2), # 2 pages have content_type_id == 1 ]) 改变搜索方式 ------------------------- 查询操作符 ^^^^^^^^^^^^^^^ 查询操作符定义多个术语查找过程中的搜索方式。有两个取值: - "or"(或) - 查询结果至少匹配所提供术语中的一个 (这是 Elasticsearch 搜索的缺省方式) - "and" (与)- 查询结果要匹配所有提供术语 (这是数据库搜索的缺省方式) 这两种方式有各自的优缺点。 "or" 操作会返回尽可能多的集合或忽略了所有术语间的相关性。而 "and" 操作只返回严格匹配所有术语的记录,会此要求太严格而返回过少的记录集。 我们推荐在需要按相关性进行排序时使用 "or" 操作符。而按其它形式排序时使用 "and" 操作符(注意:底层平台为数据库时不支持按相关性进行排序)。 下面是一个使用 ``operator`` 关键字参数的例子: .. code-block:: python # The database contains a "Thing" model with the following items: # - Hello world # - Hello # - World # Search with the "or" operator >>> s = get_search_backend() >>> s.search("Hello world", Things, operator="or") # All records returned as they all contain either "hello" or "world" [, , ] # Search with the "and" operator >>> s = get_search_backend() >>> s.search("Hello world", Things, operator="and") # Only "hello world" returned as that's the only item that contains both terms [] 对于页面、图片和文档模型,在 QuerySet's ``search`` 方法中也支持 ``operator`` 参数: .. code-block:: python >>> Page.objects.search("Hello world", operator="or") # All pages containing either "hello" or "world" are returned [, , ] 词组搜索(Phrase) ^^^^^^^^^^^^^^^^^^^^ 词组或短语搜索是将查找的术语集当作词组来查找。这样搜索出的结果应符合这些术语出现的顺序。 例如: .. code-block:: python >>> from wagtail.search.query import Phrase >>> Page.objects.search(Phrase("Hello world")) [] >>> Page.objects.search(Phrase("World hello")) [] 要了解更多使用术语双引号语法进行词组搜索的方法,请参考 :ref:`wagtailsearch_query_string_parsing`。 .. _wagtailsearch_complex_queries: 搜索中复杂查询 ^^^^^^^^^^^^^^^^^^^^^^ 通过使用查询类,Wagtail 还支持构造复杂 Python 对象查询方法。参考下面例子: ``PlainText(query_string, operator=None, boost=1.0)`` 这个类将查询条件字符串分隔成几个术语。这是没使用查询类的缺省参数形式。 查询类包含查询字符串、分隔符及提升权重值(boost). 例如: .. code-block:: python >>> from wagtail.search.query import PlainText >>> Page.objects.search(PlainText("Hello world")) # Multiple plain text queries can be combined. This example will match both "hello world" and "Hello earth" >>> Page.objects.search(PlainText("Hello") & (PlainText("world") | PlainText("earth"))) ``Phrase(query_string)`` 这个类将字符串转换成词组。参考上一节的描述。 例如: .. code-block:: python # This example will match both the phrases "hello world" and "Hello earth" >>> Page.objects.search(Phrase("Hello world") | Phrase("Hello earth")) ``Boost(query, boost)`` 这个类提供查询的权重值。 例如: .. code-block:: python >>> from wagtail.search.query import PlainText, Boost # This example will match both the phrases "hello world" and "Hello earth" but matches for "hello world" will be ranked higher >>> Page.objects.search(Boost(Phrase("Hello world"), 10.0) | Phrase("Hello earth")) 注意,底层平台使用 PostgreSQL 或数据库时不支持。 .. _wagtailsearch_query_string_parsing: 查询字符串的解析 ^^^^^^^^^^^^^^^^^^^^ 前面章节展示了程度中构造词组查询的方式,许多搜索引擎 (包括 Wagtail 台管理界面) 支持双引号让最终用户定义短语词组的搜索方式。 除了词组搜索的这种形式之外,还允许用户使用冒号形式的增加过滤条件。 这两个查询字符串的解析方法在实现时可通过调用 ``parse_query_string`` 工具函数实现,函数使用用户输入字符串做为参数,返回查询对象和过滤器的字典: 例如: .. code-block:: python >>> from wagtail.search.utils import parse_query_string >>> filters, query = parse_query_string('my query string "this is a phrase" this-is-a:filter', operator='and') >>> filters { 'this-is-a': 'filter', } >>> query And([ PlainText("my query string", operator='and'), Phrase("this is a phrase"), ]) 下面是在搜索视图中使用此函数的示例: .. code-block:: python from wagtail.search.utils import parse_query_string def search(request): query_string = request.GET['query'] # Parse query filters, query = parse_query_string(query_string, operator='and') # Published filter # An example filter that accepts either `published:yes` or `published:no` and filters the pages accordingly published_filter = filters.get('published') published_filter = published_filter and published_filter.lower() if published_filter in ['yes', 'true']: pages = pages.filter(live=True) elif published_filter in ['no', 'false']: pages = pages.filter(live=False) # Search pages = pages.search(query) return render(request, 'search_results.html', {'pages': pages}) 设置顺序 ^^^^^^^^^^^^^^^ 缺省情况下,如果底层平台支持查询结果将按相关性的顺序进行排序。 如果不使用这种排序方法,需要将 ``search()`` 方法的 ``order_by_relevance`` 关键字参数设为 ``False``。 例如: .. code-block:: python # Get a list of events ordered by date >>> EventPage.objects.order_by('date').search("Event", order_by_relevance=False) # Events ordered by date [, , ] .. _wagtailsearch_annotating_results_with_score: 使用分数标注查询结果 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 对于每个匹配到的查询结果,Elasticsearch 计算一个 "分值",这个数值代表一个查询结果记录与用户查询相关度。查询结果就是这个分值进行排序的。 在一个情况下,程序中访问这个分值是有意义的(例如程序中同时搜索两个不同的模型)。使用 ``SearchQuerySet`` 的 ``.annotate_score(field)`` 方法可以给每个搜索结果增加分值。 例如: .. code-block:: python >>> events = EventPage.objects.search("Event").annotate_score("_score") >>> for event in events: ... print(event.title, event._score) ... ("Easter", 2.5), ("Haloween", 1.7), ("Christmas", 1.5), 注意,分值是在查询过程中判定出来的,只有相同的查询,分值的大小才有意义。 .. _wagtailsearch_frontend_views: 页面搜索视图示例 =========================== 下面是一个 Django 视图可用来在网站上增加"搜索"页面: .. code-block:: python # views.py from django.shortcuts import render from wagtail.core.models import Page from wagtail.search.models import Query def search(request): # Search search_query = request.GET.get('query', None) if search_query: search_results = Page.objects.live().search(search_query) # Log the query so Wagtail can suggest promoted results Query.get(search_query).add_hit() else: search_results = Page.objects.none() # Render template return render(request, 'search_results.html', { 'search_query': search_query, 'search_results': search_results, }) 下面是相关的模板: .. code-block:: html {% extends "base.html" %} {% load wagtailcore_tags %} {% block title %}Search{% endblock %} {% block content %}
{% if search_results %}
    {% for result in search_results %}
  • {{ result }}

    {% if result.search_description %} {{ result.search_description|safe }} {% endif %}
  • {% endfor %}
{% elif search_query %} No results found {% else %} Please type something into the search box {% endif %} {% endblock %} 提供查询结果 ======================= "提供查询结果" 目的是让编辑增加一些有助于搜索精确度的内容。这些功能可参考 :mod:`~wagtail.contrib.search_promotions` 模块说明。