JSON 字段的 Django 全文搜索
Django Fulltext search on JSON field
我正在尝试为 Django 1.10.
实现新添加的全文搜索 Postgres 支持
我要搜索的字段之一是 JSON 字段:
from django.contrib.postgres.fields import JSONField
class Product():
attributes = JSONField(...)
如果我尝试使用 SearchVector
进行以下搜索
Product.objects.annotate(
search=SearchVector('attributes'),
).filter(search=keyword)
将提高:
django.db.utils.DataError: invalid input syntax for type json
LINE 1: ...ol1, to_tsvector(COALESCE("product"."attributes", '')) AS "s...
^
DETAIL: The input string ended unexpectedly.
CONTEXT: JSON data, line 1:
这是有道理的,像这样的原始 SQL 查询需要
select to_tsvector(attributes::text) from product;
但是如何在 Django 语法中实现该字段转换?
为什么你不应该这样做
这正是 JSONB data type and JSON/JSONB functions were introduced was to avoid this kind of search! The Django ORM provides access to most of this functionality. The Func expression 可用于双下划线等无法实现的功能的原因。
另一方面,如果您的 JSONB 列中有一个大文本字段,确实需要进行全文搜索。这表明数据库设计不是最佳的。该字段应该从 JSON 中拉出,它应该是一个独立的字段。
如果你还想这样做
Django 1.10 刚刚添加了一个 Cast 函数。
class Cast(expression, output_field)
Forces the result type of
expression to be the one from output_field.
如果您使用的是旧版本的 Django,则可以使用 RawSQL 函数。您将在上面链接的同一页面上找到一个示例。请注意,使用 RawSQL 函数与执行原始查询不同。
2018 年 1 月更新
有一种观点认为使用 RawSQL 会使您的代码不可移植,您永远不应该使用它。这个问题涉及 django.contrib.postgres 包,您的代码绝对不可移植。所以如果你想避免原始 sql,那应该是出于可移植性以外的原因。
正如@e4c5 所报告的,有 Cast function since Django 1.10(与您使用的版本相同)。
因此,如果您要将 JSON 字段作为文本进行搜索,则必须将其转换为文本:
from django.contrib.postgres.search import SearchVector
from django.db.models import TextField
from django.db.models.functions import Cast
Product.objects.annotate(
search=SearchVector(Cast('attributes', TextField())),
).filter(search=keyword)
您还可以在 SearchVector 中仅使用 JSON 字段的特定部分:
from django.contrib.postgres.search import SearchVector
from django.contrib.postgres.fields.jsonb import KeyTextTransform
Product.objects.annotate(
search=SearchVector(KeyTextTransform('key1', 'attributes')),
).filter(search=keyword)
添加了 PostgreSQL 10 Full Text Search support for JSON and JSONB。
我正在尝试为 Django 1.10.
实现新添加的全文搜索 Postgres 支持我要搜索的字段之一是 JSON 字段:
from django.contrib.postgres.fields import JSONField
class Product():
attributes = JSONField(...)
如果我尝试使用 SearchVector
进行以下搜索Product.objects.annotate(
search=SearchVector('attributes'),
).filter(search=keyword)
将提高:
django.db.utils.DataError: invalid input syntax for type json
LINE 1: ...ol1, to_tsvector(COALESCE("product"."attributes", '')) AS "s...
^
DETAIL: The input string ended unexpectedly.
CONTEXT: JSON data, line 1:
这是有道理的,像这样的原始 SQL 查询需要
select to_tsvector(attributes::text) from product;
但是如何在 Django 语法中实现该字段转换?
为什么你不应该这样做
这正是 JSONB data type and JSON/JSONB functions were introduced was to avoid this kind of search! The Django ORM provides access to most of this functionality. The Func expression 可用于双下划线等无法实现的功能的原因。
另一方面,如果您的 JSONB 列中有一个大文本字段,确实需要进行全文搜索。这表明数据库设计不是最佳的。该字段应该从 JSON 中拉出,它应该是一个独立的字段。
如果你还想这样做
Django 1.10 刚刚添加了一个 Cast 函数。
class Cast(expression, output_field)
Forces the result type of expression to be the one from output_field.
如果您使用的是旧版本的 Django,则可以使用 RawSQL 函数。您将在上面链接的同一页面上找到一个示例。请注意,使用 RawSQL 函数与执行原始查询不同。
2018 年 1 月更新
有一种观点认为使用 RawSQL 会使您的代码不可移植,您永远不应该使用它。这个问题涉及 django.contrib.postgres 包,您的代码绝对不可移植。所以如果你想避免原始 sql,那应该是出于可移植性以外的原因。
正如@e4c5 所报告的,有 Cast function since Django 1.10(与您使用的版本相同)。
因此,如果您要将 JSON 字段作为文本进行搜索,则必须将其转换为文本:
from django.contrib.postgres.search import SearchVector
from django.db.models import TextField
from django.db.models.functions import Cast
Product.objects.annotate(
search=SearchVector(Cast('attributes', TextField())),
).filter(search=keyword)
您还可以在 SearchVector 中仅使用 JSON 字段的特定部分:
from django.contrib.postgres.search import SearchVector
from django.contrib.postgres.fields.jsonb import KeyTextTransform
Product.objects.annotate(
search=SearchVector(KeyTextTransform('key1', 'attributes')),
).filter(search=keyword)
添加了 PostgreSQL 10 Full Text Search support for JSON and JSONB。