搜索 ( Searching )
• 最后修改:2024-05-04 • 阅读量:44
搜索 QuerySets
Wagtail 搜索基于 Django 的 QuerySet API 构建。如果模型和要过滤的字段已添加到搜索索引中,您应该能够搜索任何 Django 查询集。
搜索 Pages
Wagtail 提供了搜索页面的快捷方式: .search()
QuerySet
方法。您可以在任何 PageQuerySet
上调用此方法。例如:
# Search future EventPages
>>> from wagtail.models import EventPage
>>> EventPage.objects.filter(date__gt=timezone.now()).search("Hello world!")
PageQuerySet
的所有其他方法都可以与 search()
一起使用。例如:
# 搜索事件索引下的所有实时事件页面
>>> EventPage.objects.live().descendant_of(events_index).search("Event")
[<EventPage: Event 1>, <EventPage: Event 2>]
注意:
search()
方法会将您的 QuerySet
转换为 Wagtail 的 SearchResults
类之一的实例(取决于后端)。这意味着您必须在调用 search()
之前执行过滤。 {class="text-success"}
search
方法的标准行为是只返回完整单词的匹配;例如,搜索“hel”将不会返回包含单词“hello”的结果。例外情况是回调数据库搜索后端,它在数据库没有可用的全文搜索扩展,并且没有指定备选后端时使用。这将执行基本的子字符串匹配,并将返回包含忽略所有单词边界的搜索项的结果。
自动完成搜索
Wagtail 提供了一个单独的方法,对特定的自动完成字段执行部分匹配。这主要用于在用户输入查询时实时向他们推荐页面——不建议用于普通搜索,因为自动补全往往会在搜索的特定术语之外添加不需要的结果。
>>> EventPage.objects.live().autocomplete("Eve")
[<EventPage: Event 1>, <EventPage: Event 2>]
搜索图像、文档和自定义模型
Wagtail 的文档和图像模型在其 QuerySet 上提供了 search
方法,就像页面一样:
>>> from wagtail.images.models import Image
>>> Image.objects.filter(uploaded_by_user=user).search("Hello")
[<Image: Hello>, <Image: Hello world!>]
自定义模型 可以直接在搜索后端使用 search
方法进行搜索:
>>> from myapp.models import Book
>>> from wagtail.search.backends import get_search_backend
# Search books
>>> s = get_search_backend()
>>> s.search("Great", Book)
[<Book: Great Expectations>, <Book: The Great Gatsby>]
您还可以将 QuerySet 传递到 search
方法中,该方法允许您向搜索结果添加过滤器:
>>> 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))
[<Book: Great Expectations>]
指定要搜索的字段
默认情况下, Wagtail 将搜索已使用 index.SearchField
建立索引的所有字段。
可以使用 fields
关键字参数将其限制为特定的字段集:
# 仅搜索标题字段
>>> EventPage.objects.search("Event", fields=["title"])
[<EventPage: Event 1>, <EventPage: Event 2>]
Faceted search
Wagtail 支持分面搜索,这是一种基于分类字段(例如类别或页面类型)的过滤。
.facet(field_name)
方法返回 OrderedDict
。键是指定字段已引用的相关对象的 ID,值是找到每个 ID 的引用数。结果按参考文献数量降序排列。
例如,要在搜索结果中查找最常见的页面类型:
>>> Page.objects.search("Test").facet("content_type_id")
# 注意:键对应于ContentType对象的ID;值是
# 该类型返回的页数
OrderedDict([
('2', 4), # 4 个页面的 content_type_id == 2
('1', 2), # 2 个页面的 content_type_id == 1
])
改变搜索行为
搜索运算符
搜索运算符指定当用户输入多个搜索词时搜索应如何表现。有两个可能的值:
- “or” - 结果必须至少匹配一个术语(Elasticsearch 的默认值)
- “and” - 结果必须匹配所有术语(数据库搜索的默认值)
两种运营商都有优点和缺点。“或”运算符将返回更多结果,但可能包含许多不相关的结果。“and”运算符仅返回包含所有搜索词的结果,但要求用户更精确地查询。
我们建议在按相关性排序时使用“或”运算符,在按其他内容排序时使用“and”运算符(注意:数据库后端当前不支持按相关性排序)。
以下是使用 operator
关键字参数的示例:
# 数据库包含一个 "Thing" 型号,其中包含以下项目:
# - 你好世界
# - 你好
# - 世界
# 使用 "or" 运算符搜索
>>> s = get_search_backend()
>>> s.search("Hello world", Things, operator="or")
# 返回的所有记录都包含 "hello" 或 "world"
[<Thing: Hello World>, <Thing: Hello>, <Thing: World>]
# 使用 "and" 运算符搜索
>>> s = get_search_backend()
>>> s.search("Hello world", Things, operator="and")
# 仅返回 "hello world" ,因为这是唯一包含这两个术语的项目
[<Thing: Hello world>]
对于页面、图像和文档模型,QuerySet 的 search
方法也支持 operator
关键字参数:
>>> Page.objects.search("Hello world", operator="or")
# All pages containing either "hello" or "world" are returned
[<Page: Hello World>, <Page: Hello>, <Page: World>]
短语搜索
短语搜索用于查找整个句子或短语而不是单个术语。这些术语必须一起出现并以相同的顺序出现。
For example:
>>> from wagtail.search.query import Phrase
>>> Page.objects.search(Phrase("Hello world"))
[<Page: Hello World>]
>>> Page.objects.search(Phrase("World hello"))
[<Page: World Hello day>]
如果您希望使用双引号语法实现短语查询,请参阅 查询字符串解析 。
模糊匹配
模糊匹配将返回包含与搜索词相似的词的文档(由 Levenshtein edit distance 测量)。
对于三到五个字母的术语最多允许进行一次编辑(换位、插入或删除字符),对于较长的术语允许进行两次编辑,较短的术语必须完全匹配。
For example:
>>> from wagtail.search.query import Fuzzy
>>> Page.objects.search(Fuzzy("Hallo"))
[<Page: Hello World>]
仅 Elasticsearch 搜索后端支持模糊匹配。
复杂的搜索查询
通过使用搜索查询类, Wagtail 还支持将搜索查询构建为 Python 对象,该对象可以由其他搜索查询包装并与其他搜索查询组合。提供以下课程:
PlainText(query_string, operator=None, boost=1.0)
此类包装了一串单独的术语。这与没有查询类的搜索相同。
它需要一个查询字符串、运算符和提升。
For example:
>>> from wagtail.search.query import PlainText
>>> Page.objects.search(PlainText("Hello world"))
# 可以组合多个纯文本查询。此示例将匹配 "hello world" 和 "Hello earth"
>>> Page.objects.search(PlainText("Hello") & (PlainText("world") | PlainText("earth")))
Phrase(query_string)
此类包装一个包含短语的字符串。请参阅上一节了解其工作原理。
For example:
# 此示例将匹配短语 "hello world" 和 "Hello earth"
>>> Page.objects.search(Phrase("Hello world") | Phrase("Hello earth"))
Boost(query, boost)
该类提高了另一个查询的分数。
For example:
>>> from wagtail.search.query import PlainText, Boost
# 此示例将匹配短语 "hello world" 和 "Hello earth" ,但 "hello world" 的匹配排名更高
>>> Page.objects.search(Boost(Phrase("Hello world"), 10.0) | Phrase("Hello earth"))
请注意,PostgreSQL 或数据库搜索后端不支持此功能。
查询字符串解析
前面的部分展示了如何手动构建短语搜索查询,但是很多搜索引擎(包括 Wagtail admin,尝试一下!)支持通过用双引号括起短语来编写短语查询。除了短语之外,您可能还希望允许用户使用冒号语法 ( hello world published:yes
) 将过滤器添加到查询中。
这两个功能可以使用 parse_query_string
实用函数来实现。该函数接受用户输入的查询字符串并返回查询对象和过滤器 QueryDict:
示例:
>>> from wagtail.search.utils import parse_query_string
>>> filters, query = parse_query_string('my query string "this is a phrase" this_is_a:filter key:value1 key:value2', operator='and')
# Alternatively..
# filters, query = parse_query_string("my query string 'this is a phrase' this_is_a:filter key:test1 key:test2", operator='and')
>>> filters
<QueryDict: {'this_is_a': ['filter'], 'key': ['value1', 'value2']}>>
# Get a list of values associated to a particular key using getlist method
>>> filters.getlist('key')
['value1', 'value2']
# Get a dict representation using dict method
# NOTE: dict method will reduce multiple values for a particular key to the last assigned value
>>> filters.dict()
{
'this_is_a': 'filter',
'key': 'value2'
}
>>> query
And([
PlainText("my query string", operator='and'),
Phrase("this is a phrase"),
])
以下是如何在搜索视图中使用此函数的示例:
from wagtail.search.utils import parse_query_string
def search(request):
query_string = request.GET['query']
# 解析查询
filters, query = parse_query_string(query_string, operator='and')
# 已发布的过滤器
# 接受 `published:yes` 或 `published:no` 并相应地过滤页面的过滤器示例
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})
Custom ordering
默认情况下,搜索结果按相关性排序(如果后端支持)。为了保留 QuerySet 的现有排序,需要在 search()
方法上将 order_by_relevance
关键字参数设置为 False
。
示例:
# 获取按日期排序的事件列表
>>> EventPage.objects.order_by('date').search("Event", order_by_relevance=False)
# 按日期排序的事件
[<EventPage: Easter>, <EventPage: Halloween>, <EventPage: Christmas>]
用评分注释结果
对于每个匹配的结果,Elasticsearch 都会计算一个“评分”,该数字表示结果与用户查询的相关程度。结果通常根据分数排序。
在某些情况下,访问分数很有用(例如以编程方式组合不同模型的两个查询)。您可以通过调用 SearchQuerySet
上的 .annotate_score(field)
方法将分数添加到每个结果中。
示例:
>>> events = EventPage.objects.search("Event").annotate_score("_score")
>>> for event in events:
... print(event.title, event._score)
...
("Easter", 2.5),
("Halloween", 1.7),
("Christmas", 1.5),
请注意,分数本身是任意的,它仅适用于比较同一查询的结果。
页面搜索视图示例
下面是一个 Django 视图示例,可用于向您的站点添加“搜索”页面:
# views.py
from django.shortcuts import render
from wagtail.models import Page
from wagtail.contrib.search_promotions.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)
# 记录查询,以便 Wagtail 可以建议升级结果
Query.get(search_query).add_hit()
else:
search_results = Page.objects.none()
# 渲染模板
return render(request, 'search_results.html', {
'search_query': search_query,
'search_results': search_results,
})
这是一个与之配套的模板:
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block title %}Search{% endblock %}
{% block content %}
<form action="{% url 'search' %}" method="get">
<input type="text" name="query" value="{{ search_query }}">
<input type="submit" value="Search">
</form>
{% if search_results %}
<ul>
{% for result in search_results %}
<li>
<h4><a href="{% pageurl result %}">{{ result }}</a></h4>
{% if result.search_description %}
{{ result.search_description|safe }}
{% endif %}
</li>
{% endfor %}
</ul>
{% elif search_query %}
No results found
{% else %}
Please type something into the search box
{% endif %}
{% endblock %}
推荐的搜索结果
“推广搜索结果”允许编辑者将相关内容明确链接到搜索词,因此结果页面除了搜索引擎的结果之外还可以包含精选内容。
此功能由 [search_promotions
] contrib 模块提供。