【问题标题】:Django SearchVector on choices field选择字段上的 Django SearchVector
【发布时间】:2019-05-21 07:02:35
【问题描述】:

如果我有一个简单的 Django 模型,例如:

from model_utils import Choices

class Art(models.Model):
    CATEGORIES = Choices(
        (1, 'DIGITAL_ART', 'Digital Art'),
        (2, 'MULTIMEDIA', 'Multimedia'),
    )
    title = models.TextField()
    category = models.PositiveSmallIntegerField(
        db_index=True, choices=CATEGORIES, blank=True, null=True
    )

如何在 postgres 中使用 SearchVector 来允许在标题和类别字段上进行搜索?例如。所以“Some Book Digital Art”会同时查询标题和类别字段。

问题在于,在数据库级别,选择字段存储为整数,而不是文本。

有没有办法在创建搜索向量时将整数映射到相应的文本值?

【问题讨论】:

  • 是在设计模型还是已经有表格和数据?标题和类别对您的搜索具有相同的值?

标签: django postgresql django-models full-text-search django-queryset


【解决方案1】:

我能想到的第一个解决方案是使用Conditional Expression 将类别文本添加到SearchVector

from django.db.models import Case, CharField, Value, When
from django.contrib.postgres.search import SearchVector


Art.objects.annotate(
    category_text=Case(
        When(category=1, then=Value('Digital Art')),
        When(category=2, then=Value('Multimedia')),
        default=Value(''),
        output_field=CharField()
    ),
    search=SearchVector('title') + SearchVector('category_text')
).filter(
    search='Some Book Digital Art'
).values_list('title', flat=True)

此查询的结果将类似于:

<QuerySet ['Some Book']>

为 PostgreSQL 生成的 SQL 如下:

SELECT "arts_art"."title"
FROM "arts_art"
WHERE (
  to_tsvector(COALESCE("arts_art"."title", '')) ||
  to_tsvector(COALESCE(
    CASE
      WHEN ("arts_art"."category" = 1) THEN 'Digital Art'
      WHEN ("arts_art"."category" = 2) THEN 'Multimedia'
      ELSE ''
    END, '')
  )
) @@ (plainto_tsquery('Some Book Digital Art')) = TRUE

更新

您可以从您的选择列表中自动构建您的查询:

from django.db.models import Case, CharField, Value, When
from django.contrib.postgres.search import SearchVector

CATEGORIES = (
    (1, 'Digital Art'),
    (2, 'Multimedia'),
)

Art.objects.annotate(
    category_text=Case(
        *[When(category=c, then=Value(v)) for c, v in CATEGORIES],
        default=Value(''),
        output_field=CharField()
    ),
    search=SearchVector('title') + SearchVector('category_text')
).filter(
    search='Some Book Digital Art'
).values_list('title', flat=True)

【讨论】:

  • 谢谢。很好的解决方案(在更新时我认为您不小心错过了Value,即then=Value(v)
  • 不客气。我很高兴您发现我的解决方案很有用。感谢您的建议,我已经更新了代码示例。
猜你喜欢
  • 2019-05-01
  • 2014-08-15
  • 2010-11-19
  • 2018-09-26
  • 2011-11-11
  • 2013-11-02
  • 2023-04-08
  • 2018-12-27
  • 1970-01-01
相关资源
最近更新 更多