python django全文搜索taggit

dgenwo3n  于 2023-06-04  发布在  Python
关注(0)|答案(1)|浏览(125)

我的应用-基础知识

我有一个简单的django应用程序,它允许存储有关某些项目的信息,我正在尝试实现搜索视图/功能。
我使用django-taggit来标记项目的功能/特性。

我想实现什么

我想实现一个全文搜索,允许搜索所有领域的项目,包括他们的标签。

问题

1.在结果视图中,标记的项目显示多次(每个标记出现一次)
1.当我在搜索字段中指定 * 只有一个 * 标签时,排名是正确的,但当我指定 * 多个 * 标签名称时,我会得到意想不到的排名结果。
我怀疑SearchVector()没有像我期望的那样解析标签关系。在这种情况下,标签应该被视为单词列表。

示例代码

models.py

from django.db import models
from taggit.managers import TaggableManager

class Item(models.Model):
    identifier = models.SlugField('ID', unique=True, editable=False)
    short_text = models.CharField('Short Text', max_length=100, blank=True)
    serial_number = models.CharField('Serial Number', max_length=30, blank=True)
    revision = models.CharField('Revision/Version', max_length=30, blank=True)
    part_number = models.CharField('Part Number', max_length=30, blank=True)
    manufacturer = models.CharField('Manufacturer', max_length=30, blank=True)
    description = models.TextField('Description', blank=True)
    tags = TaggableManager('Tags', blank=True)
    is_active = models.BooleanField('Active', default=True)

forms.py

from django import forms

class SearchForm(forms.Form):
    search = forms.CharField(max_length=200, required=False)
    active_only = forms.BooleanField(initial=True, label='Show active items only', required=False)

views.py

from django.views.generic.list import ListView
from django.contrib.postgres.search import SearchQuery, SearchVector, SearchRank

from . import models
from . import forms

class ItemListView(ListView):
    form_class = forms.SearchForm
    model = models.Item
    fields = ['serial_number', 'part_number', 'manufacturer', 'tags', 'is_active']
    template_name_suffix = '_list'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = self.form_class(self.request.GET)
        return context

    def get_queryset(self):
        queryset = super().get_queryset()
        form = self.form_class(self.request.GET)
        if form.is_valid():
            if form.cleaned_data['active_only']:
                queryset = queryset.filter(is_active=True)

            if not form.cleaned_data['search']:
                return super().get_queryset()

            search_vector = SearchVector('identifier', 'short_text', 'serial_number', 'revision', 'part_number',
                                         'manufacturer', 'description', 'tags')
            search_query = SearchQuery(form.cleaned_data['search'], search_type='websearch')
            return (
                queryset.annotate(
                    search=search_vector, rank=SearchRank(search_vector, search_query)
                )
                # .filter(search=search_query)
                .order_by("-rank").distinct()
            ) #.filter(search__icontains=form.cleaned_data['search'],)
        return super().get_queryset()
lp0sw83n

lp0sw83n1#

您的问题是将tags字段直接添加到SearchVector
让我们将Django的StringAgg标签连接成一个字符串,然后在SearchVector中使用该字符串
首先我们导入StringAgg

from django.contrib.postgres.aggregates import StringAgg

然后这就是你如何改变你的get_queryset函数

def get_queryset(self):
    queryset = super().get_queryset()
    form = self.form_class(self.request.GET)
    if form.is_valid():
        if form.cleaned_data['active_only']:
            queryset = queryset.filter(is_active=True)

        if not form.cleaned_data['search']:
            return queryset

        queryset = queryset.annotate(tags_str=StringAgg('tags__name', delimiter=' '))
        search_vector = SearchVector('identifier', 'short_text', 'serial_number', 'revision', 'part_number',
                                     'manufacturer', 'description', 'tags_str')
        search_query = SearchQuery(form.cleaned_data['search'], search_type='websearch')
        return (
            queryset.annotate(
                search=search_vector, rank=SearchRank(search_vector, search_query)
            )
            .order_by("-rank").distinct()
        )
    return queryset

相关问题