Web应用程序的常见任务是使用用户输入来搜索数据库中的一些数据。 在一个简单的情况下,这可能是按类别过滤对象列表。 更复杂的用例可能需要通过加权,分类,突出显示,多种语言等进行搜索。 本文档介绍了一些可能的用例和可以使用的工具。
我们将参考Making queries中使用的相同模型。
基于文本的字段具有简单匹配操作的选择。 例如,您可能希望允许查询作者如下:
>>> Author.objects.filter(name__contains='Terry')
[, ]
这是一个非常脆弱的解决方案,因为它需要用户知道作者姓名的确切子字符串。 一个更好的方法可以是不区分大小写的匹配(icontains
),但这只是稍微好一点。
如果您使用PostgreSQL,Django提供a selection of database specific tools,以允许您利用更复杂的查询选项。 其他数据库可能通过插件或用户定义的功能选择不同的工具。 Django目前不包括任何对它们的支持。 我们将使用PostgreSQL中的一些示例来演示数据库可能具有的功能。
搜索其他数据库
由django.contrib.postgres
提供的所有搜索工具完全在公共API(例如custom lookups和database functions)上构建。 根据您的数据库,您应该能够构造查询以允许类似的API。 如果有这样的事情不能实现,请打开一张票。
在上面的例子中,我们确定不区分大小写的查找将更有用。 在处理非英文名称时,进一步的改进是使用unaccented comparison
:
>>> Author.objects.filter(name__unaccent__icontains='Helen')
[, , ]
这显示了另一个问题,我们在匹配不同的名称拼写。 在这种情况下,我们有不对称性,但是搜索Helen
将会接收Helena
或Hélène
,但不是相反的。 另一个选择是使用比较序列的trigram_similar
比较。
像这样:
>>> Author.objects.filter(name__unaccent__lower__trigram_similar='Hélène')
[, ]
现在我们有一个不同的问题 - “Helena Bonham Carter”的名字不再显示出来,因为它长得多。 Trigram搜索考虑三个字母的所有组合,并比较搜索和源字符串中出现的数量。 对于较长的名称,有更多的组合出现在源字符串中,因此它不再被认为是紧密匹配的。
这里的比较功能的正确选择取决于您的特定数据集,例如使用的语言和正在搜索的文本的类型。 我们看到的所有示例都是短字符串,用户可能会通过不同的定义输入源数据。
当您开始考虑大块文本时,简单的数据库操作太简单了。 而上面的例子可以被认为是一串字符的操作,全文搜索查看实际的单词。 根据所使用的系统,可能会使用以下一些想法:
使用搜索软件有许多替代方案,其中一些最突出的是Elastic和Solr。 这些是完整的基于文档的搜索解决方案。 要将它们与来自Django模型的数据一起使用,您需要一个将数据转换为文本文档的层,包括对数据库ID的反向引用。 当使用引擎搜索返回某个文档时,可以在数据库中查找。 有各种第三方库旨在帮助此过程。
PostgreSQL有自己的全文搜索实现内置。 虽然并不像其他搜索引擎一样强大,但它的优点在于您的数据库内可以轻松地与其他关系查询(如分类)相结合。
django.contrib.postgres
模块提供了一些帮助来进行这些查询。 例如,一个简单的查询可能是选择所有提到“奶酪”的博客条目:
>>> Entry.objects.filter(body_text__search='cheese')
[, ]
您还可以对字段和相关模型的组合进行过滤:
>>> Entry.objects.annotate(
... search=SearchVector('blog__tagline', 'body_text'),
... ).filter(search='cheese')
[
,
,
,
]
有关完整的详细信息,请参阅contrib.postgres
Full text search文档。
2017年9月6日