hstore或json数据的Django数字比较?

Posted

技术标签:

【中文标题】hstore或json数据的Django数字比较?【英文标题】:Django numeric comparison of hstore or json data? 【发布时间】:2016-08-15 09:19:16 【问题描述】:

是否可以通过将 hstore 值转换为 intfloat 来过滤查询集?

我遇到了一个问题,我们需要向现有数据模型添加更强大的查询。数据模型使用HStoreField 来存储大部分建筑数据,我们需要能够对它们进行查询/过滤,其中一些值需要被视为数值。

但是,由于这些值被视为字符串,因此它们会逐个字符地进行比较,从而导致查询不正确。例如,'700' > '1000'

因此,如果我想查询 sqft 值在 700 到 1000 之间的所有项目,我得到的结果为零,即使我可以清楚地看到有数百个项目的值在该范围内。如果我只查询 sqft 值 >= 700 的项目,我只会得到 sqft 值以 7、8 或 9 开头的结果。

我还尝试使用来自 django-pgjson 的 JsonField 进行测试(因为我们还没有使用 Django 1.9),但它似乎有同样的问题。

设置

Django==1.8.9
django-pgjson==0.3.1 (for jsonfield functionality)
Postgres==9.4.7

models.py

from django.contrib.postgres.fields import HStoreField
from django.db import models

class Building (models.Model):
    address1 = models.CharField(max_length=50)
    address2 = models.CharField(max_length=20, default='', blank=True)
    city = models.CharField(max_length=50)
    state = models.CharField(max_length=2)
    zipcode = models.CharField(max_length=10)
    data = HStoreField(blank=True, null=True)

示例数据

这是 hstore 字段中某些数据的样子的示例。

address1: ...
address2: ...
city: ...
state: ...
zipcode: ...
data: 
    'year_built': '1995',
    'building_type': 'residential',
    'building_subtype': 'single-family',
    'bedrooms': '2',
    'bathrooms': '1',
    'total_sqft': '958',

返回错误结果的示例查询

queryset = Building.objects.filter(data__total_sqft__gte=700)

我尝试过使用annotate 功能,看看我是否可以强制它转换为数值,但我没有任何运气让它工作。我总是收到一条错误消息,说我要查询的字段不存在。这是我在其他地方找到的一个似乎不起作用的示例。

queryset = Building.objects.all().annotate(
    sqft=RawSQL("((data->>total_sqft)::numeric)")
).filter(sqft__gte=700)

导致此错误的原因:

FieldError:无法将关键字“sqft”解析为字段。选项有:address1、address2、city、state、zipcode、data

让这个设置更复杂一点的一点是,我们正在动态构建查询并使用Q() 对象来和/或一起使用它们。

所以,在给定键、值和运算符类型(gtelteiexact)的情况下,尝试做类似这样的事情:

queryset.annotate(**key: RawSQL("((%data->>%s)::numeric)", (key,))
queries.append(Q(**'__'.format(key, operator): value)
queries.filter(reduce(operator.and_, queries)

但是,即使只是让第一个查询工作而无需动态构建它们,我也会很高兴。

我考虑过必须为明确定义字段的建筑数据创建单独模型的可能性,但是data hstore 中有超过 600 个键值对。似乎将其更改为具体的数据模型将是设置和维护的一场噩梦。

【问题讨论】:

我已经能够通过升级 Django 并使用 1.9 中的内置 JSONField 而不是 django-pgjson 来使查询工作,因为它使用 jsonb 而不是 json 数据库字段.但是,使用标准 json 字段似乎仍然可以做到这一点。 total_sqft 周围的单引号?如: RawSQL("((data->>'total_sqft')::numeric)")。也许也没有内部括号:RawSQL("(data->>'total_sqft'::numeric)")。我不使用 HStore,但这就是 PostgreSQL 将它用于 JSON 的方式。 【参考方案1】:

所以我遇到了一个非常相似的问题,最终将Cast Function (Django > 1.10) 与KeyTextTransform 一起使用。

my_query =.query.annotate(as_numeric=Cast(KeyTextTransform('my_json_fieldname', 'metadata'), output_field=DecimalField(max_digits=6, decimal_places=2))).filter(as_numeric=2)

【讨论】:

以上是关于hstore或json数据的Django数字比较?的主要内容,如果未能解决你的问题,请参考以下文章

Django HStore:如何覆盖键值模型字段的 __getattr__ 和 __setattr__

PostgreSQL 9.2 - 将 TEXT json 字符串转换为 json/hstore 类型

sql 一些函数将数组/ hstore转换为json :)

如何将 Postgres Hstore 数据类型转换为雪花对象或变体

PostgreSQL hstore 列性能提升一例

Postgresql:如何在不使用中间 hstore 的情况下将键值表转换为 json