全文搜索

django.contrib.postgres.search 模块中的数据库函数方便了 PostgreSQL 的 全文搜索引擎 的使用。

在本文档的例子中,我们将使用 执行查询 中定义的模型。

参见

有关搜索的高级概述,请参见 主题文档

search 查找

常见的使用全文搜索的方式是对数据库中的单个列搜索单个术语。例如:

  1. >>> Entry.objects.filter(body_text__search="Cheese")
  2. [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]

这将使用默认的数据库搜索配置,从 body_text 字段在数据库中创建一个 to_tsvector,从搜索词 'Cheese' 中创建一个 plainto_tsquery。通过匹配查询和向量得到结果。

要使用 search 查找,'django.contrib.postgres' 必须在你的 INSTALLED_APPS

SearchVector

class SearchVector(*expressions, config=None, weight=None)

对单个字段进行搜索非常有用,但也有一定的限制。我们要搜索的 Entry 实例属于一个 Blog,该 Blog 有一个 tagline 字段。要对这两个字段进行查询,可以使用 SearchVector

  1. >>> from django.contrib.postgres.search import SearchVector
  2. >>> Entry.objects.annotate(
  3. ... search=SearchVector("body_text", "blog__tagline"),
  4. ... ).filter(search="Cheese")
  5. [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]

SearchVector 的参数可以是任何 Expression 或字段名。多个参数将使用空格连接在一起,这样搜索文档就会包含所有参数。

SearchVector 对象可以组合在一起,允许您重复使用它们。例如:

  1. >>> Entry.objects.annotate(
  2. ... search=SearchVector("body_text") + SearchVector("blog__tagline"),
  3. ... ).filter(search="Cheese")
  4. [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]

关于 configweight 参数的解释,请参见 更改搜索配置加权查询

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 的 全文搜索文档 来了解两者的区别和语法。举例说明。

  1. >>> from django.contrib.postgres.search import SearchQuery
  2. >>> SearchQuery('red tomato') # two keywords
  3. >>> SearchQuery('tomato red') # same results as above
  4. >>> SearchQuery('red tomato', search_type='phrase') # a phrase
  5. >>> SearchQuery('tomato red', search_type='phrase') # a different phrase
  6. >>> SearchQuery("'tomato' & ('red' | 'green')", search_type='raw') # boolean operators
  7. >>> SearchQuery("'tomato' ('red' OR 'green')", search_type='websearch') # websearch operators

SearchQuery 术语可以逻辑组合,以提供更大的灵活性:

  1. >>> from django.contrib.postgres.search import SearchQuery
  2. >>> SearchQuery("meat") & SearchQuery("cheese") # AND
  3. >>> SearchQuery("meat") | SearchQuery("cheese") # OR
  4. >>> ~SearchQuery("meat") # NOT

参见 更改搜索配置config 参数的解释。

SearchRank

class SearchRank(vector, query, weights=None, normalization=None, cover_density=False)

到目前为止,我们已经返回了向量和查询之间可能存在任何匹配的结果。您可能希望按某种相关性对结果进行排序。PostgreSQL 提供了一个排名函数,该函数考虑了查询术语在文档中出现的频率、术语在文档中的紧密程度以及它们出现的文档部分的重要性。匹配越好,排名的值就越高。要按相关性排序:

  1. >>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
  2. >>> vector = SearchVector("body_text")
  3. >>> query = SearchQuery("cheese")
  4. >>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by("-rank")
  5. [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]

参见 加权查询 关于 weights 参数的解释。

cover_density 参数设置为 True,启用覆盖密度排序,即考虑匹配的查询词的接近程度。

提供一个整数给 normalization 参数来控制排名的归一化。这个整数是一个位掩码,所以您可以组合多种行为:

  1. >>> from django.db.models import Value
  2. >>> Entry.objects.annotate(
  3. ... rank=SearchRank(
  4. ... vector,
  5. ... query,
  6. ... normalization=Value(2).bitor(Value(4)),
  7. ... )
  8. ... )

PostgreSQL 文档中有更多关于 不同排序归一化选项 的细节。

SearchHeadline

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_selstop_sel 参数设置为字符串值,用于在文档中高亮显示查询词。PostgreSQL 的默认值是 <b></b>

max_wordsmin_words 参数提供整数值,以确定最长和最短的标题。PostgreSQL 的默认值是 35 和 15。

short_word 参数提供一个整数值,以便在每个标题中丢弃这个长度或更少的字。PostgreSQL 的默认值是 3。

highlight_all 参数设置为 True,以使用整个文档来代替片段,并忽略 max_wordsmin_wordsshort_word 参数。这在 PostgreSQL 中是默认禁用的。

max_fragments 提供一个非零的整数值,以设置要显示的最大片段数。在 PostgreSQL 中默认是禁用的。

设置 fragment_delimiter 字符串参数来配置片段之间的定界符。PostgreSQL 的默认值是 " ... "

PostgreSQL 文档中有更多关于 高亮搜索结果 的细节。

用法示例:

  1. >>> from django.contrib.postgres.search import SearchHeadline, SearchQuery
  2. >>> query = SearchQuery("red tomato")
  3. >>> entry = Entry.objects.annotate(
  4. ... headline=SearchHeadline(
  5. ... "body_text",
  6. ... query,
  7. ... start_sel="<span>",
  8. ... stop_sel="</span>",
  9. ... ),
  10. ... ).get()
  11. >>> print(entry.headline)
  12. Sandwich with <span>tomato</span> and <span>red</span> cheese.

参见 更改搜索配置config 参数的解释。

更改搜索配置

您可以为 SearchVectorSearchQuery 指定 config 属性,以使用不同的搜索配置。这允许使用数据库定义的不同语言解析器和字典:

  1. >>> from django.contrib.postgres.search import SearchQuery, SearchVector
  2. >>> Entry.objects.annotate(
  3. ... search=SearchVector("body_text", config="french"),
  4. ... ).filter(search=SearchQuery("œuf", config="french"))
  5. [<Entry: Pain perdu>]

config 的值也可以存储在另一个列中:

  1. >>> from django.db.models import F
  2. >>> Entry.objects.annotate(
  3. ... search=SearchVector("body_text", config=F("blog__language")),
  4. ... ).filter(search=SearchQuery("œuf", config=F("blog__language")))
  5. [<Entry: Pain perdu>]

加权查询

不同字段在查询中的相关性可能不同,因此您可以在将它们组合之前设置各个向量的权重:

  1. >>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
  2. >>> vector = SearchVector("body_text", weight="A") + SearchVector(
  3. ... "blog__tagline", weight="B"
  4. ... )
  5. >>> query = SearchQuery("cheese")
  6. >>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by(
  7. ... "rank"
  8. ... )

权重应该是以下字母之一:D、C、B、A。默认情况下,这些权重分别对应数字 0.10.20.41.0。如果您希望以不同的方式设置权重,可以将一个包含四个浮点数的列表传递给 SearchRank,按照上述相同的顺序设置权重:

  1. >>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
  2. >>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by("-rank")

性能

使用这些函数都不需要特殊的数据库配置,但是,如果你搜索的记录超过几百条,你很可能会遇到性能问题。例如,全文搜索是一个比比较整数大小更密集的过程。

如果您查询的所有字段都包含在一个特定的模型中,您可以创建一个与您希望使用的搜索向量匹配的功能性 GINGiST 索引。例如:

  1. GinIndex(
  2. SearchVector("body_text", "headline", config="english"),
  3. name="search_vector_idx",
  4. )

PostgreSQL 文档详细介绍了如何为全文搜索创建索引,您可以查看 创建全文搜索索引的 PostgreSQL 文档 获取更多信息。

SearchVectorField

class SearchVectorField

如果这种方法变得太慢,您可以在模型中添加一个 SearchVectorField。您需要使用触发器保持它的数据更新,例如,可以参考 PostgreSQL 文档 中的描述。然后,您可以查询该字段,就像它是一个已注释的 SearchVector 一样:

  1. >>> Entry.objects.update(search_vector=SearchVector("body_text"))
  2. >>> Entry.objects.filter(search_vector="cheese")
  3. [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]

三元相似度

另一种搜索的方法是三字母相似度。三字母是指三个连续的字符组合。除了 trigram_similartrigram_word_similartrigram_strict_word_similar 查找之外,您还可以使用一些其他表达式。

要使用它们,你需要激活 PostgreSQL 上的 pg_trgm 扩展 。你可以使用 TrigramExtension 迁移操作来安装它。

TrigramSimilarity

class TrigramSimilarity(expression, string, **extra)

接受一个字段名或表达式,以及一个字符串或表达式。返回两个参数之间的三元相似度。

用法示例:

  1. >>> from django.contrib.postgres.search import TrigramSimilarity
  2. >>> Author.objects.create(name="Katy Stevens")
  3. >>> Author.objects.create(name="Stephen Keats")
  4. >>> test = "Katie Stephens"
  5. >>> Author.objects.annotate(
  6. ... similarity=TrigramSimilarity("name", test),
  7. ... ).filter(
  8. ... similarity__gt=0.3
  9. ... ).order_by("-similarity")
  10. [<Author: Katy Stevens>, <Author: Stephen Keats>]

TrigramWordSimilarity

class TrigramWordSimilarity(string, expression, **extra)

接受一个字符串或表达式,以及一个字段名或表达式。返回两个参数之间的三元相似度。

用法示例:

  1. >>> from django.contrib.postgres.search import TrigramWordSimilarity
  2. >>> Author.objects.create(name="Katy Stevens")
  3. >>> Author.objects.create(name="Stephen Keats")
  4. >>> test = "Kat"
  5. >>> Author.objects.annotate(
  6. ... similarity=TrigramWordSimilarity(test, "name"),
  7. ... ).filter(
  8. ... similarity__gt=0.3
  9. ... ).order_by("-similarity")
  10. [<Author: Katy Stevens>]

TrigramStrictWordSimilarity

class TrigramStrictWordSimilarity(string, expression, **extra)

New in Django 4.2.

接受一个字符串或表达式,以及一个字段名或表达式。返回两个参数之间的三字母严格单词相似度。类似于 TrigramWordSimilarity(),但它强制范围边界与单词边界匹配。

TrigramDistance

class TrigramDistance(expression, string, **extra)

接受一个字段名或表达式,以及一个字符串或表达式。返回两个参数之间的三元距离。

用法示例:

  1. >>> from django.contrib.postgres.search import TrigramDistance
  2. >>> Author.objects.create(name="Katy Stevens")
  3. >>> Author.objects.create(name="Stephen Keats")
  4. >>> test = "Katie Stephens"
  5. >>> Author.objects.annotate(
  6. ... distance=TrigramDistance("name", test),
  7. ... ).filter(
  8. ... distance__lte=0.7
  9. ... ).order_by("distance")
  10. [<Author: Katy Stevens>, <Author: Stephen Keats>]

TrigramWordDistance

class TrigramWordDistance(string, expression, **extra)

接受一个字符串或表达式,以及一个字段名或表达式。返回两个参数之间的三元字距离。

用法示例:

  1. >>> from django.contrib.postgres.search import TrigramWordDistance
  2. >>> Author.objects.create(name="Katy Stevens")
  3. >>> Author.objects.create(name="Stephen Keats")
  4. >>> test = "Kat"
  5. >>> Author.objects.annotate(
  6. ... distance=TrigramWordDistance(test, "name"),
  7. ... ).filter(
  8. ... distance__lte=0.7
  9. ... ).order_by("distance")
  10. [<Author: Katy Stevens>]

TrigramStrictWordDistance

class TrigramStrictWordDistance(string, expression, **extra)

New in Django 4.2.

接受一个字符串或表达式,以及一个字段名或表达式。返回两个参数之间的三字母严格单词距离。