【问题标题】:Combine trigram with ranked searching in django 1.10在 django 1.10 中将 trigram 与排名搜索相结合
【发布时间】:2016-10-18 00:23:24
【问题描述】:

我们在 django 1.10 中使用搜索,我们需要使用 trigram 搜索的用户排名搜索。

我们的代码是这样的:

def get_queryset(self):
        search = self.request.GET.get('text', '')
        vector = SearchVector('name',weight='A',
            config=settings.SEARCH_LANGS[
                settings.LANGUAGE
            ],
            ) + SearchVector(
            'content',
            weight='B',
            config=settings.SEARCH_LANGS[
                settings.LANGUAGE
            ],
            )
        query = SearchQuery(search)
        return Article.objects.annotate(
            rank=SearchRank(
                vector,
                query
                ),
            similarity=TrigramSimilarity(
                'name', search
                ) + TrigramSimilarity(
                'content', search
                ),
            ).filter(
            rank__gte=0.3
            ).filter(
            similarity__gt=0.3
            ).order_by(
            '-similarity'
            )[:20]

但是这段代码没有返回任何查询,不使用三元组我们会遇到问题,但是,将它们结合起来我们无法得到查询。

我们如何在 django 1.10 中结合 trigram 和排名搜索?

【问题讨论】:

    标签: python django postgresql


    【解决方案1】:

    我们对搜索权重的工作原理进行了更深入的调查。

    根据documents可以根据字段分配权重,甚至可以分配权重,同样我们可以使用trigrams按相似度或距离进行过滤。

    但是没有具体说明使用这两者的例子,并进一步调查它理解也没有多少权重起作用。

    一个小逻辑告诉我们,如果我们在所有中寻找一个共同的词,我们都会排名为 0,相似度的变化比范围大得多,但往往会降低该范围的值。

    现在,文本搜索,据我们所知,它是根据您要过滤的字段中包含的文本执行的,甚至比配置中放置的语言还要多。例子是放标题,使用的模型有一个标题字段和一个内容字段,其中最常见的词是how change,审查加权词(范围作为查询,所以我们可以使用valuesvalues_list来查看排名和相似度,它们是数值,我们可以查看加权词查看向量对象),我们看到如果分配了权重,但是拆分词的组合:找到'perfil'和'cambi',但是我们没有找到' cambiar' 或 'como';但是,所有模型都包含与“lorem ipsun ...”相同的文本,以及该句子的所有单词(如果它们是完整的且权重为 B);我们得出的结论是,搜索是根据字段内容完成的,以过滤比我们配置搜索时使用的语言更多的内容。

    也就是说,我们在这里展示我们用于所有内容的代码。

    首先,我们需要在启用数据库所需的范围内使用 Trigram:

    from django.db import migrations
    from django.contrib.postgres.operations import UnaccentExtension, TrigramExtension
    
    class Migration(migrations.Migration):
    
        initial = True
    
        dependencies = [
        ]
    
        operations = [
          ...
          TrigramExtension(),
          UnaccentExtension(),
    
        ]
    

    postgres 包中导入迁移操作并从任何文件迁移中运行。

    下一步是更改问题的代码,以便过滤器在第二个查询失败时返回其中一个查询:

    def get_queryset(self):
            search_query = SearchQuery(self.request.GET.get('q', ''))
    
            vector = SearchVector(
                'name',
                weight='A',
                config=settings.SEARCH_LANGS[settings.LANGUAGE_CODE],
            ) + SearchVector(
                'content',
                weight='B',
                config=settings.SEARCH_LANGS[settings.LANGUAGE_CODE],
            )
    
            if self.request.user.is_authenticated:
                queryset = Article.actives.all()
            else:
                queryset = Article.publics.all()
    
            return queryset.annotate(
              rank=SearchRank(vector, search_query)
              similarity=TrigramSimilarity(
                  'name', search_query
                ) + TrigramSimilarity(
                  'content', search_query
                ),
            ).filter(Q(rank__gte=0.3) | Q(similarity__gt=0.3)).order_by('-rank')[:20]
    

    上述代码的问题是一个又一个查询,如果选择的词没有出现在两次搜索中,问题就更大了。我们使用Q 对象通过OR 连接器进行过滤,这样如果两者中的一个未返回所需值,则将另一个发送到位。

    这样就足够了,但是欢迎他们对这些权重和三元组如何工作进行深入的说明,以充分利用最新版本的 Django 提供的这一新优势。

    【讨论】:

    • 感谢分享您找到的解决方案。
    • 如果您的用例具有可比性,您还可以按两个排名/分数值中的最大值进行排序:``` return queryset.annotate(rank=SearchRank(vector, search_query)similarity=TrigramSimilarity( 'name', search) + TrigramSimilarity('content', search), best_score=Greatest('rank', 'similarity')).filter(Q(best_score__gte=0.3)).order_by('-best_score')[:20 ] ```
    • 我使用了类似的东西,我将排名和相似性结合起来,如下所示:score=(F("rank") + F("similarity")) / 2
    【解决方案2】:

    这样的东西会为你工作......它是一个博客的搜索表单。但我不知道为什么TrigramSimilarity 只对标题起作用,而对正文不起作用?

    search_vector = SearchVector('title', weight='A') + SearchVector('body', weight='B')
    search_query = SearchQuery(query)
    rank = SearchRank(search_vector, search_query)  
    results = Post.published.annotate(rank = SearchRank(search_vector, 
              search_query)).filter(rank__gte=0.2).order_by('-rank')
    
          if results:
            results = results
          else:
            results = Post.published.annotate(similarity = TrigramSimilarity('title', 
                      query)).filter(similarity__gte=0.1).order_by('-similarity')
    

    【讨论】:

    • 我认为你应该提出一个与此相关的新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 2015-08-12
    • 2020-12-03
    • 1970-01-01
    • 2018-07-21
    • 2020-12-06
    • 1970-01-01
    相关资源
    最近更新 更多