全文搜索 - Django 的__search与__icontains的区别



我最近才开始学习 Django,所以如果这对你来说太新手了,请原谅我。

我有一个在Django中实现的搜索功能。我在MySQL DB全文索引中创建,然后在我的Book模型中有一个字段:search_keywords,我基本上存储了所有单词,包括书名和内容。然后我的搜索函数做类似的事情:

search_keyword = self.request.GET.get('keywords')
    if search_keyword:
        # '*' is added for cases where user enters only part of the keyword
        qs = qs.filter(keywords__search=search_keyword + '*')

但是,如果我的搜索查询是"介绍",我知道有一个像"test.introduction"这样的单词/术语,那么上述方法就不起作用(如果该术语只是"介绍",那么它可以正常工作)。

然后当我尝试这个时:

        qs = qs.filter(keywords__icontains=search_keyword)

它找到"测试.介绍"等。

所以我开始想知道为什么会这样。"__search"仅适用于全字搜索吗?我知道"__icontains"的大小写是有意义的(不区分大小写并且包含单词的一部分),但是为什么要麻烦在数据库中创建全文索引,以便我们可以在 Django 中使用"__search"呢?这是速度优势吗(例如,在要搜索大量文本的情况下?还是我在这里完全错过了一些东西?

区别在于要在数据库上执行的结果 SQL 查询...我个人更喜欢"__icontains",因为所有数据库都支持"__search",而"仅适用于mysql(作为django文档)(也支持Django≥1.10中的PostgreSQL - 参见文档)。

查看每个方法的查询:

使用__search

>>> str(Contact.objects.filter(first_name__search='john').query)
'SELECT `contact_contact`.`id`, `contact_contact`.`title`, `contact_contact`.`first_name`, `contact_contact`.`last_name`, `contact_contact`.`user_id`, `contact_contact`.`role_id`, `contact_contact`.`organization_id`, `contact_contact`.`dob`, `contact_contact`.`email`, `contact_contact`.`notes`, `contact_contact`.`create_date` FROM `contact_contact` WHERE MATCH (`contact_contact`.`first_name`) AGAINST (john IN BOOLEAN MODE)'

使用__icontains

>>> str(Contact.objects.filter(first_name__icontains='john').query)
'SELECT `contact_contact`.`id`, `contact_contact`.`title`, `contact_contact`.`first_name`, `contact_contact`.`last_name`, `contact_contact`.`user_id`, `contact_contact`.`role_id`, `contact_contact`.`organization_id`, `contact_contact`.`dob`, `contact_contact`.`email`, `contact_contact`.`notes`, `contact_contact`.`create_date` FROM `contact_contact` WHERE `contact_contact`.`first_name` LIKE %john% '
正如

我尝试的那样,__search仅适用于多个单词中的整个单词(令人难以置信!),例如,当我的Book模型包含带有title='The little prance'的对象时,而

Country.objects.filter(name__contais='itt')

返回一个对象,

Country.objects.filter(name__search='itt')

什么都不回来!

搜索的优点是允许搜索多个字段:

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

最新更新