搜索 — Django 文档
搜索
Web 应用程序的一项常见任务是使用用户输入在数据库中搜索一些数据。 在一个简单的情况下,这可能是按类别过滤对象列表。 更复杂的用例可能需要使用加权、分类、突出显示、多种语言等进行搜索。 本文档解释了一些可能的用例和您可以使用的工具。
我们将参考在 进行查询 中使用的相同模型。
用例
标准文本查询
基于文本的字段有一系列简单的匹配操作。 例如,您可能希望允许像这样查找作者:
>>> Author.objects.filter(name__contains='Terry')
[<Author: Terry Gilliam>, <Author: Terry Jones>]
这是一个非常脆弱的解决方案,因为它要求用户知道作者姓名的确切子字符串。 更好的方法可能是不区分大小写的匹配 (:lookup:`icontains`),但这只是稍微好一点。
数据库更高级的比较功能
如果您使用 PostgreSQL,Django 提供了 一系列数据库特定工具 以允许您利用更复杂的查询选项。 其他数据库有不同的工具选择,可能通过插件或用户定义的函数。 Django 目前不包含对它们的任何支持。 我们将使用 PostgreSQL 中的一些示例来演示数据库可能具有的功能类型。
在其他数据库中搜索
django.contrib.postgres 提供的所有搜索工具完全基于公共 API 构建,例如 custom lookups 和 数据库函数 。 根据您的数据库,您应该能够构建查询以允许类似的 API。 如果有具体的事情不能通过这种方式实现,请开票。
在上面的示例中,我们确定不区分大小写的查找会更有用。 在处理非英文名称时,进一步的改进是使用 :lookup:`无重音比较 ` :
>>> Author.objects.filter(name__unaccent__icontains='Helen')
[<Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>]
这显示了另一个问题,即我们匹配名称的不同拼写。 在这种情况下,我们有一个不对称 - 搜索 Helen
将选择 Helena
或 Hélène
,但不是相反。 另一种选择是使用 :lookup:`trigram_similar` 比较,它比较字母序列。
例如:
>>> Author.objects.filter(name__unaccent__lower__trigram_similar='Hélène')
[<Author: Helen Mirren>, <Author: Hélène Joy>]
现在我们有一个不同的问题——“Helena Bonham Carter”的较长名字没有出现,因为它更长。 Trigram 搜索考虑三个字母的所有组合,并比较在搜索字符串和源字符串中出现的数量。 对于较长的名称,源字符串中出现了更多组合,因此不再将其视为紧密匹配。
此处正确选择比较函数取决于您的特定数据集,例如使用的语言和正在搜索的文本类型。 我们看到的所有示例都是短字符串,其中用户可能会输入与源数据接近(通过不同的定义)的内容。
基于文档的搜索
当您开始考虑大块文本时,简单的数据库操作是一种过于简单的方法。 上面的例子可以被认为是对字符串的操作,而全文搜索则是查看实际的单词。 根据所使用的系统,它可能会使用以下一些想法:
- 忽略“停用词”,例如“a”、“the”、“and”。
- 词干,使“小马”和“小马”被认为是相似的。
- 根据不同的标准对单词进行加权,例如它们在文本中出现的频率,或者它们出现的字段的重要性,例如标题或关键字。
使用搜索软件有很多选择,其中最突出的是 Elastic 和 Solr。 这些是完整的基于文档的搜索解决方案。 要将它们与来自 Django 模型的数据一起使用,您需要一个层将您的数据转换为文本文档,包括对数据库 ID 的反向引用。 当使用引擎的搜索返回某个文档时,您可以在数据库中查找它。 有多种第三方库旨在帮助完成此过程。
PostgreSQL 支持
PostgreSQL 内置了自己的全文搜索实现。 虽然不如其他一些搜索引擎强大,但它的优势在于可以在您的数据库中,因此可以轻松地与其他关系查询(例如分类)结合使用。
django.contrib.postgres 模块提供了一些帮助程序来进行这些查询。 例如,一个简单的查询可能是选择所有提到“奶酪”的博客条目:
>>> Entry.objects.filter(body_text__search='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
您还可以过滤字段组合和相关模型:
>>> Entry.objects.annotate(
... search=SearchVector('blog__tagline', 'body_text'),
... ).filter(search='cheese')
[
<Entry: Cheese on Toast recipes>,
<Entry: Pizza Recipes>,
<Entry: Dairy farming in Argentina>,
]
有关完整详细信息,请参阅 contrib.postgres
全文搜索 文档。