# Django 3.2.11.dev 文档

# 全文搜索

`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'` 必须在你的 `INSTALLED_APPS`。

Changed in Django 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` 参数的解释。

Changed in Django 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 文档中有更多关于 不同排序归一化选项 的细节。

New in Django 3.1:

增加了 `normalization` 和 `cover_density` 参数。

## `SearchHeadline`

New in Django 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` 参数的解释。

## 更改搜索配置

你可以为 `SearchVector` 和 `SearchQuery` 指定 `config` 属性，以使用不同的搜索配置。这允许使用数据库定义的不同语言解析器和字典：

    >>> 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')

权重应该是下列字母之一。默认情况下，这些权重分别是指数字 `0.1`、`0.2`、`0.4` 和 `1.0`。如果你希望以不同的方式对它们进行加权，请将四个浮点数的列表传递给 `SearchRank` 作为 `weights`，顺序与上述相同：

    >>> 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>]

## 三元相似度

另一种搜索方法是三元相似度。trigram 是一组连续的三个字符。除了 `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>]