搜索¶
Web 应用程序的一个常见任务是根据用户输入搜索数据库中的某些数据。在简单的情况下,这可能是按类别筛选对象列表。更复杂的使用案例可能需要使用加权、分类、高亮显示、多种语言等进行搜索。本文档解释了一些可能的使用案例以及您可以使用的工具。
我们将参考执行查询中使用的相同模型。
使用案例¶
标准文本查询¶
文本字段有多种匹配操作。例如,您可能希望允许如下查找作者
>>> Author.objects.filter(name__contains="Terry")
[<Author: Terry Gilliam>, <Author: Terry Jones>]
这是一个非常脆弱的解决方案,因为它要求用户知道作者姓名的确切子字符串。更好的方法可能是大小写不敏感匹配(icontains
),但这只是稍微好一点。
数据库更高级的比较函数¶
如果您使用的是 PostgreSQL,Django 提供了一系列特定于数据库的工具,使您可以利用更复杂的查询选项。其他数据库有不同的工具选择,可能通过插件或用户自定义函数实现。Django 目前不包含对它们的任何支持。我们将使用 PostgreSQL 的一些示例来演示数据库可能具有的功能。
在其他数据库中搜索
django.contrib.postgres
提供的全部搜索工具完全构建在公共 API 上,例如自定义查找和数据库函数。根据您的数据库,您应该能够构建查询以允许类似的 API。如果某些特定内容无法通过这种方式实现,请提交工单。
在上面的示例中,我们确定大小写不敏感查找更有用。在处理非英语姓名时,进一步的改进是使用unaccented comparison
>>> Author.objects.filter(name__unaccent__icontains="Helen")
[<Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>]
这显示了另一个问题,我们在其中与姓名的不同拼写匹配。在这种情况下,我们有一个不对称性——搜索Helen
将找到Helena
或Hélène
,但反之则不然。另一种选择是使用trigram_similar
比较,它比较字母序列。
例如
>>> Author.objects.filter(name__unaccent__lower__trigram_similar="Hélène")
[<Author: Helen Mirren>, <Author: Hélène Joy>]
现在我们遇到了一个不同的问题——“Helena Bonham Carter”这个较长的名字没有显示出来,因为它长得多。三元组搜索考虑所有三个字母的组合,并比较搜索字符串和源字符串中出现的组合数量。对于较长的名称,源字符串中没有出现更多组合,因此它不再被认为是近似匹配。
此处比较函数的正确选择取决于您的特定数据集,例如使用的语言和被搜索文本的类型。我们看到的全部示例都是关于用户可能输入与源数据接近(根据不同的定义)的短字符串。
基于文档的搜索¶
当您开始考虑大块文本时,标准数据库操作不再是一种有用的方法。上面的示例可以被认为是对字符字符串的操作,全文搜索则查看实际的单词。根据所使用的系统,它可能会使用以下一些想法
忽略“停用词”,例如“a”、“the”、“and”。
词干化,使“pony”和“ponies”被认为是相似的。
根据不同的标准对单词进行加权,例如它们在文本中出现的频率,或它们出现的字段(例如标题或关键字)的重要性。
使用搜索软件有很多替代方案,其中最突出的是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
全文搜索文档。