在视图中使用全文搜索+ GIN(Django 1.11)

时间:2017-11-19 14:27:12

标签: postgresql django-models django-views django-queryset gin

我需要一些帮助,在django视图中构建适当的查询,以便使用GIN索引进行全文搜索。我有一个很大的数据库(~400k行),需要在3个字段上进行全文搜索。试图使用django docs search,这是代码之前的GIN代码。它可以工作,但需要6秒多才能搜索所有字段。接下来,我尝试实现GIN索引以加快搜索速度。已经有很多问题如何构建它。但我的问题是 - 在使用GIN索引进行搜索时,视图查询如何变化?我应该搜索哪些字段?

  GIN之前:

models.py

class Product(TimeStampedModel):
    product_id = models.AutoField(primary_key=True, )
    shop = models.ForeignKey('Shop', to_field='shop_name')
    brand = models.ForeignKey('Brand', to_field='brand_name')
    title = models.TextField(blank=False, null=False)
    description = models.TextField(blank=True, null=True)

views.py

def get_cosmetic(request):
if request.method == "GET":
    pass
else:
    search_words = request.POST.get('search')
    search_vectors = SearchVector('title', weight='B')+ SearchVector('description', weight='C') + SearchVector('brand__brand_name', weight='A')

    products = Product.objects.annotate(search = search_vectors, rank=SearchRank(search_vectors, search))\
        .filter(search=search_words ).order_by('-rank')

    return render(request, 'example.html', {"products": products})

GIN之后:
models.py

class ProductManager(models.Manager):
def with_documents(self):
    vector = pg_search.SearchVector('brand__brand_name', weight='A') +\
            pg_search.SearchVector('title', weight='A')+\
            pg_search.SearchVector('description', weight='C')
    return self.get_queryset().annotate(document=vector)


class Product(TimeStampedModel):
    product_id = models.AutoField(primary_key=True, )
    shop = models.ForeignKey('Shop', to_field='shop_name')
    brand = models.ForeignKey('Brand', to_field='brand_name')
    title = models.TextField(blank=False, null=False)
    description = models.TextField(blank=True, null=True)

search_vector = pg_search.SearchVectorField(null=True)

objects = ProductManager()

class Meta:
    indexes = [
        indexes.GinIndex(fields=['search_vector'], name='title_index')
    ]

#update search_vector every time the entry updates
def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    if 'update_fields' not in kwargs or 'search_vector' not in kwargs['update_fields']:
        instance = self._meta.default_manager.with_documents().get(pk=self.pk)
        instance.search_vector = instance.document
        instance.save(update_fields=['search_vector'])

views.py

def get_cosmetic(request):
if request.method == "GET":
    pass

else:
    search_words = request.POST.get('search')    
    products = ?????????
    return render(request, 'example.html', {"products": products})

1 个答案:

答案 0 :(得分:2)

回答我自己的问题:

products = Product.objects.annotate(rank=SearchRank(F('search_vector'), search_words))
                          .filter(search_vector=search_words)
                          .order_by('-rank')


这意味着您应该搜索索引字段 - 在我的案例search_vector字段中 此外,我在ProductManager()类中更改了我的代码,所以现在我可以使用

products = Product.objects.with_documents(search_words)

其中with_documents()是自定义ProductManager()的自定义函数。此更改的方法是here (page 30)

这些代码的作用是什么:
1)创建具有分数到字段的search_vector,具有更高分数的字段 - 在结果排序中获得更高的位置 2)通过ORM Django为全文搜索创建GIN索引 3)每次更改模型实例时更新GIN索引

这段代码不做什么:
1)它不按查询的子字符串的相关性排序。 Possible solution.

希望这能帮助Django中有点复杂的全文搜索。