进行搜索

搜索的QuerySets

Wagtail的搜索特性,是建立在Django的 QuerySet API之上的。可搜索所有由模型所提供的Django QuerySet,以及那些已加入到搜索索引的、进行过滤的字段(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)。

对页面的搜索

Wagtail提供了搜索页面的一个捷径:.search() QuerySet方法。可在所有PageQuerySet上调用该方法。比如:

  1. # 对未来的 EventPage进行搜索
  2. >>> from wagtail.core.models import EventPage
  3. >>> EventPage.objects.filter(date__gt=timezone.now()).search("Hello world!")

所有其他PageQuerySet的方法,都可以与.search()一起使用。比如:

  1. # 搜素所有活动索引下在线显示的EventPage
  2. >>> EventPage.objects.live().descendant_of(events_index).search("Event")
  3. [<EventPage: Event 1>, <EventPage: Event 2>]

注意 方法search()将吧QuerySet转换为某个Wagtail SearchResults类(取决于后端情况)的一个实例。这意味着在调用search()方法之前进行过滤。

对图片、文档及定制模型的搜索

Wagtail的文档及图片模型,与页面模型一样,均在他们的 QuerySets 上提供了一个search方法:

  1. >>> from wagtail.images.models import Image
  2. >>> Image.objects.filter(uploaded_by_user=user).search("Hello")
  3. [<Image: Hello>, <Image: Hello world!>]

对于定制模型,可通过直接在搜索后端,使用search方法进行搜索:

  1. >>> from myapp.models import Book
  2. >>> from wagtail.search.backends import get_search_backend
  3. # 对书籍进行搜索
  4. >>> s = get_search_backend
  5. >>> s.search("Great", Book)
  6. [<Book: Great Expectations>, <Book: The Great Gatsby>]

也可将一个 QuerySet 传递进 search 方法,从而实现将过滤器添加到搜索结果(you can also pass a QuerySet into the search method which allows you to add filters to your search results):

  1. >>> from myapp.models import Book
  2. >>> from wagtail.search.backends import get_search_backend
  3. # 对书籍进行搜索
  4. >>> s = get_search_backend
  5. >>> s.search("Great", Book.objects.filter(published_date__year__lt=1900))
  6. [<Book: Great Expectations>]

指定要搜索的字段

默认Wagtail将搜索所有已使用index.SearchField进行索引了的字段。

可使用fileds关键字参数,将搜索字段限制为确切的字段集合:

  1. # 仅搜索标题字段
  2. >>> EventPage.objects.search("Event", fields=["title"])
  3. [<EventPage: Event 1>, <EventPage: Event 2>]

分面搜索

Faceted search

Wagtail带有对分面搜索的支持,分面搜索是一种基于分类字段(诸如类别或页面类型)的过滤(Wagtail supports faceted search which is kind of filtering based on a taxonomy field(such as category or page type))。

方法.facet(field_name)返回的是一个OrderedDict。字典中的键是相关对象的IDs,这些对象则是已被该字段引用到的;字典中的值,是到各个ID的引用的数目。方法结果字典,是依引用数目降序排序的。

下面是在搜索结果中找出最常用页面类型的代码:

  1. >>> Page.objects.search("Test").facet("content_type_id")
  2. # 注意:这些键是与某个 Content_Type 对象对应的ID,值则是
  3. # 那个类型下所返回页面的数目
  4. OrderedDict([
  5. ('2', 4), # 有4个页面有着 content_type_id == 2
  6. ('1', 2), # 有2个页面有着 content_type_id == 1
  7. ])

改变搜索行为

Changing search behaviour

搜索运算符

搜索运算符指明了在用户输入了多个搜索词条时,搜索应如何进行。有两个可能的取值:

  • or — 结果必须匹配至少一个词条(这是Elasticsearch默认的行为)
  • and — 结果必须匹配所有词条(这是数据库搜索的默认行为)

二者都有利有弊。or运算符将返回更多的结果,但会倾向于包含很多的不相关结果。and运算符则仅返回那些包含了所有搜索词条的结果,但要求用户的查询更为精准。

在以相关度进行排序时,推荐使用or运算符,在以所有其他线索排序时使用and运算符(注意:当前数据库后端并不支持以相关度为依据的排序)。

下面是一个使用operator关键字参数的示例:

  1. # 数据库中包含了一个都有以下条目的“Thing”的模型:
  2. # - Hello world
  3. # - Hello
  4. # - World
  5. # 使用 “or” 运算符的搜索
  6. >>> s = get_search_backend()
  7. >>> s.search("Hello world", Things, operator="or")
  8. # 所有记录都被返回,因为他们都保护了“hello”或“world”
  9. [<Thing: Hello World>, <Thing: Hello>, <Thing: World>]
  10. # 以“and”运算符进行搜索
  11. >>> s = get_search_backend()
  12. >>> s.search("Hello world", Things, operator="and")
  13. # 仅返回“hello world”, 因为那是仅有的包含了“hello”与“world”两个词条的数据库条目
  14. [<Thing: Hello world>]

对于页面、图片及文档模型,在QuerySet的search方法上,也是支持operator关键字参数的:

  1. >>> Page.objects.search("Hello world", operator="or")
  2. # 所有包含了“hello”或“world”的页面都被返回
  3. [<Page: Hello World>, <Page: Hello>, <Page: World>]

对排序进行定制

默认在后端支持的情况下,搜索结果是以想高度进行排序的。要保留QuerySet的既有排序,就要将search()方法上的order_by_relevance关键字参数设置为False

比如:

  1. # 获取一个依日期排序的活动清单
  2. >>> EventPage.objects.order_by('date').search("Event", order_by_relevance=False)
  3. # 这就是依日期排序的活动了
  4. [<EventPage: Easter>, <EventPage: Halloween>, <EventPage: Christmas>]

使用分值来对结果进行批注

Annotating results with score

对每项匹配结果,Elasticsearch会计算出一个“分值”,所谓分值,就是根据用户的查询,用于表示该项结果相关度的一个数字。结果通常是基于分值进行排序的。

在某些场合,对分值的访问是很有用的(诸如程序实现的对不同模型的两个查询结合起来)。通过调用SearchQuerySet上的.annotate_score(field)方法,可将分支加入到每次查询。

比如:

  1. >>> events = EventPage.objects.search('Event').annotate_score('_score')
  2. >>> for event in events:
  3. ... print(event.title, event._score)
  4. ...
  5. ("Easter", 2.5),
  6. ("Haloween", 1.7),
  7. ("Christmas", 1.5),

请注意分值本身是随意的,且仅在对同样查询下的结果比较才有用处(note that the score itself is arbitrary and it is only useful for comparison of results for the same query)。

页面搜索视图的示例

下面是一个可用于将“搜索”页面加入到站点的Django视图的示例:

  1. # views.py
  2. from django.shortcuts import render
  3. from wagtail.core.models import Page
  4. from wagtail.search.models import Query
  5. def search(request):
  6. # 搜索
  7. search_query = request.GET.get('query', None)
  8. if search_query:
  9. search_results = Page.objects.live().search(search_query)
  10. # 对该查询进行记录,从而令到Wagtail可以给出改进的结果建议
  11. Query.get(search_query).add_hit()
  12. else:
  13. search_results = Page.objects.none()
  14. # 对模板进行渲染
  15. return render(request, 'search_results.html', {
  16. 'search_query': search_query,
  17. 'search_results': search_results,
  18. })

以下是一个与该试图一同工作的模板:

{% raw %} {% extends “base.html” %} {% load wagtailcore_tags %}

  1. {% block title %}搜索{% endblock %}
  2. {% block content %}
  3. <form action="{% url 'search' %}" method="get">
  4. <input type="text" name="query" value="{{ search_query }}" />
  5. <input type="submit" value="搜索" />
  6. </form>
  7. {% if search_results %}
  8. <ul>
  9. {% for result in search_results %}
  10. <li>
  11. <h4><a href="{% pageurl result %}">{{ result }}</a></h4>
  12. {% if result.search_description %}
  13. {{ result.search_description|safe }}
  14. {% endif %}
  15. </li>
  16. {% endfor %}
  17. </ul>
  18. {% elif search_query %}
  19. 未找到结果
  20. {% else %}
  21. 请将搜索词条输入到搜索框中
  22. {% endif %}
  23. {% endblock %}

{% endraw %}

提升后的搜索结果

Promoted search results

“提升了的搜索结果”特性,允许网站编辑显式地将相关内容,链接到搜索条目,如此结果页面就能包含干预后的内容,作为搜索引擎结果的补充。

此功能是有社区贡献模块search_promotions所提供了。