全文搜索 — Django 文档
全文检索
django.contrib.postgres.search
模块中的数据库函数简化了PostgreSQL的全文搜索引擎的使用。
对于本文档中的示例,我们将使用 进行查询 中定义的模型。
search 查找
使用全文搜索的一种常见方法是针对数据库中的单个列搜索单个术语。 例如:
>>> Entry.objects.filter(body_text__search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
这将在数据库中的 body_text
字段中创建一个 to_tsvector
,并从搜索词 'Cheese'
中创建一个 plainto_tsquery
,两者都使用默认的数据库搜索配置。 通过匹配查询和向量获得结果。
要使用 search
查找,'django.contrib.postgres'
必须在您的 :setting:`INSTALLED_APPS` 中。
3.1 版更改: 添加了对查询表达式的支持。
SearchVector
- class SearchVector(*expressions, config=None, weight=None)
针对单个字段进行搜索很好,但也很有限。 我们正在搜索的 Entry
实例属于 Blog
,它有一个 tagline
字段。 要查询这两个字段,请使用 SearchVector
:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector('body_text', 'blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
SearchVector
的参数可以是任何 Expression 或字段的名称。 多个参数将使用空格连接在一起,以便搜索文档将它们全部包含在内。
SearchVector
对象可以组合在一起,允许您重复使用它们。 例如:
>>> Entry.objects.annotate(
... search=SearchVector('body_text') + SearchVector('blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
有关 config
和 weight
参数的说明,请参阅 更改搜索配置 和 加权查询 。
SearchQuery
- class SearchQuery(value, config=None, search_type='plain')
SearchQuery
将用户提供的术语转换为搜索查询对象,数据库将其与搜索向量进行比较。 默认情况下,用户提供的所有单词都会通过词干算法,然后它会查找所有结果术语的匹配项。
如果 search_type
是 'plain'
(这是默认值),则将这些术语视为单独的关键字。 如果 search_type
是 'phrase'
,则将这些术语视为单个短语。 如果 search_type
是 'raw'
,那么您可以提供带有术语和运算符的格式化搜索查询。 如果 search_type
是 'websearch'
,那么您可以提供格式化的搜索查询,类似于网络搜索引擎使用的查询。 'websearch'
需要 PostgreSQL ≥ 11。 阅读 PostgreSQL 的 全文搜索文档 以了解差异和语法。 例子:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery('red tomato') # two keywords
>>> SearchQuery('tomato red') # same results as above
>>> SearchQuery('red tomato', search_type='phrase') # a phrase
>>> SearchQuery('tomato red', search_type='phrase') # a different phrase
>>> SearchQuery("'tomato' & ('red' | 'green')", search_type='raw') # boolean operators
>>> SearchQuery("'tomato' ('red' OR 'green')", search_type='websearch') # websearch operators
SearchQuery
项可以逻辑组合以提供更大的灵活性:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery('meat') & SearchQuery('cheese') # AND
>>> SearchQuery('meat') | SearchQuery('cheese') # OR
>>> ~SearchQuery('meat') # NOT
有关 config
参数的说明,请参阅 更改搜索配置 。
3.1 版更改: 增加了对 SearchQuery.value
中的 'websearch'
搜索类型和查询表达式的支持。
SearchRank
- class SearchRank(vector, query, weights=None, normalization=None, cover_density=False)
到目前为止,我们已经返回了向量和查询之间可能存在任何匹配的结果。 您可能希望按某种相关性对结果进行排序。 PostgreSQL 提供了一个排序功能,它考虑了查询词在文档中出现的频率、词在文档中的接近程度以及文档中出现的部分的重要性。 匹配越好,排名的值就越高。 按相关性排序:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector('body_text')
>>> query = SearchQuery('cheese')
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by('-rank')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
有关 weights
参数的说明,请参阅 加权查询 。
将 cover_density
参数设置为 True
以启用覆盖密度排名,这意味着将匹配查询词的邻近性考虑在内。
为 normalization
参数提供一个整数以控制等级归一化。 这个整数是一个位掩码,因此您可以组合多种行为:
>>> from django.db.models import Value
>>> Entry.objects.annotate(
... rank=SearchRank(
... vector,
... query,
... normalization=Value(2).bitor(Value(4)),
... )
... )
PostgreSQL 文档有更多关于 不同等级规范化选项 的详细信息。
3.1 新功能: 增加了 normalization
和 cover_density
参数。
SearchHeadline
3.1 版中的新功能。
- class SearchHeadline(expression, query, config=None, start_sel=None, stop_sel=None, max_words=None, min_words=None, short_word=None, highlight_all=None, max_fragments=None, fragment_delimiter=None)
接受单个文本字段或表达式、查询、配置和一组选项。 返回突出显示的搜索结果。
将 start_sel
和 stop_sel
参数设置为用于在文档中包装突出显示的查询词的字符串值。 PostgreSQL 的默认值是 <b>
和 </b>
。
为 max_words
和 min_words
参数提供整数值以确定最长和最短的标题。 PostgreSQL 的默认值是 35 和 15。
为 short_word
参数提供一个整数值以丢弃每个标题中此长度或更少的单词。 PostgreSQL 的默认值为 3。
将 highlight_all
参数设置为 True
以使用整个文档代替片段并忽略 max_words
、min_words
和 short_word
参数. 这在 PostgreSQL 中默认是禁用的。
为 max_fragments
提供一个非零整数值以设置要显示的最大片段数。 这在 PostgreSQL 中默认是禁用的。
设置 fragment_delimiter
字符串参数以配置分片之间的分隔符。 PostgreSQL 的默认值是 " ... "
。
PostgreSQL 文档有更多关于 突出显示搜索结果 的细节。
用法示例:
>>> from django.contrib.postgres.search import SearchHeadline, SearchQuery
>>> query = SearchQuery('red tomato')
>>> entry = Entry.objects.annotate(
... headline=SearchHeadline(
... 'body_text',
... query,
... start_sel='<span>',
... stop_sel='</span>',
... ),
... ).get()
>>> print(entry.headline)
Sandwich with <span>tomato</span> and <span>red</span> cheese.
有关 config
参数的说明,请参阅 更改搜索配置 。
更改搜索配置
您可以将 config
属性指定给 SearchVector 和 SearchQuery 以使用不同的搜索配置。 这允许使用数据库定义的不同语言解析器和字典:
>>> from django.contrib.postgres.search import SearchQuery, SearchVector
>>> Entry.objects.annotate(
... search=SearchVector('body_text', config='french'),
... ).filter(search=SearchQuery('œuf', config='french'))
[<Entry: Pain perdu>]
config
的值也可以存储在另一列中:
>>> from django.db.models import F
>>> Entry.objects.annotate(
... search=SearchVector('body_text', config=F('blog__language')),
... ).filter(search=SearchQuery('œuf', config=F('blog__language')))
[<Entry: Pain perdu>]
加权查询
每个字段在查询中可能不具有相同的相关性,因此您可以在组合它们之前设置各种向量的权重:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector('body_text', weight='A') + SearchVector('blog__tagline', weight='B')
>>> query = SearchQuery('cheese')
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by('rank')
重量应为以下字母之一:D、C、B、A。 默认情况下,这些权重分别指代数字 0.1
、0.2
、0.4
和 1.0
。 如果您希望对它们进行不同的加权,请按照上述相同的顺序将四个浮点数的列表作为 weights
传递给 SearchRank:
>>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
>>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by('-rank')
性能
使用这些功能中的任何一个都不需要特殊的数据库配置,但是,如果您搜索的记录超过几百条,则可能会遇到性能问题。 例如,全文搜索是一个比比较整数大小更密集的过程。
如果您查询的所有字段都包含在一个特定模型中,您可以创建一个与您希望使用的搜索向量相匹配的功能索引。 PostgreSQL 文档详细介绍了 为全文搜索创建索引 。
SearchVectorField
- class SearchVectorField
如果这种方法变得太慢,您可以向模型添加 SearchVectorField
。 例如,您需要使用触发器填充它,如 PostgreSQL 文档 中所述。 然后,您可以查询该字段,就像它是带注释的 SearchVector
:
>>> Entry.objects.update(search_vector=SearchVector('body_text'))
>>> Entry.objects.filter(search_vector='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
三元组相似度
另一种搜索方法是三元组相似性。 三元组是一组三个连续的字符。 除了 :lookup:`trigram_similar` 查找之外,您还可以使用其他几个表达式。
要使用它们,您需要在 PostgreSQL 上激活 pg_trgm 扩展 。 您可以使用 TrigramExtension 迁移操作安装它。
TrigramSimilarity
- class TrigramSimilarity(expression, string, **extra)
接受字段名称或表达式,以及字符串或表达式。 返回两个参数之间的三元组相似度。
用法示例:
>>> from django.contrib.postgres.search import TrigramSimilarity
>>> Author.objects.create(name='Katy Stevens')
>>> Author.objects.create(name='Stephen Keats')
>>> test = 'Katie Stephens'
>>> Author.objects.annotate(
... similarity=TrigramSimilarity('name', test),
... ).filter(similarity__gt=0.3).order_by('-similarity')
[<Author: Katy Stevens>, <Author: Stephen Keats>]
TrigramDistance
- class TrigramDistance(expression, string, **extra)
接受字段名称或表达式,以及字符串或表达式。 返回两个参数之间的三元组距离。
用法示例:
>>> from django.contrib.postgres.search import TrigramDistance
>>> Author.objects.create(name='Katy Stevens')
>>> Author.objects.create(name='Stephen Keats')
>>> test = 'Katie Stephens'
>>> Author.objects.annotate(
... distance=TrigramDistance('name', test),
... ).filter(distance__lte=0.7).order_by('distance')
[<Author: Katy Stevens>, <Author: Stephen Keats>]