当前位置:  开发笔记 > 后端 > 正文

如何聚合(最小/最大等)Django JSONField数据?

如何解决《如何聚合(最小/最大等)DjangoJSONField数据?》经验,为你挑选了3个好方法。

我正在使用django 1.9及其内置的JSONField和postgres 9.4.在我的模型的JSONFieldjson字段中,我使用一些值存储对象,包括数字.我需要聚合它们来找到最小/最大值.像这样的东西:

Model.objects.aggregate(min=Min('attrs__my_key'))

提取特定密钥也很有用:

Model.objects.values_list('attrs__my_key', flat=True)

上述查询失败attrs:"无法将关键字'my_key'解析为字段.不允许加入'attrs'."

有可能吗?

注意:1)我知道如何进行简单的postgres查询来完成这项工作,所以我专门搜索ORM解决方案,以便能够过滤等.2)我想这可以用(相对)新的查询表达式/查找api完成但是我还没有研究过它.



1> dinosaurwalt..:

从django 1.11(尚未出现,所以这可能会改变)你可以使用django.contrib.postgres.fields.jsonb.KeyTextTransform而不是RawSQL.

在Django 1.10,你必须复制/粘贴KeyTransform到你自己KeyTextTransform和更换->与运营商->>,并#>#>>因此它返回文本,而不是JSON对象.

Model.objects.annotate(
    val=KeyTextTransform('json_field_key', 'blah__json_field'))
).aggregate(min=Min('val')

您甚至可以KeyTextTransformSearchVectors中包含s以进行全文搜索

Model.objects.annotate(
    search=SearchVector(
        KeyTextTransform('jsonb_text_field_key', 'json_field'))
    )
).filter(search='stuff I am searching for')

请记住,您也可以在jsonb字段中编制索引,因此您应该根据您的特定工作负载来考虑这一点.


谢谢你.我花了一段时间来解码如何使用它.在我的例子中,json数据存储在模型字段'jdata'中(相当于问题中的'attrs'),json键是'createdDate',它是顶级的.min_result = Model.objects.annotate(val = KeyTextTransform('createdDate','jdata')).aggregate(min = Min('val'))

2> alTus..:

对于那些感兴趣的人,我找到了解决方案(或至少解决方法).

from django.db.models.expressions import RawSQL

Model.objects.annotate(
    val=RawSQL("((attrs->>%s)::numeric)", (json_field_key,))
).aggregate(min=Min('val')

请注意,attrs->>%s表达式将attrs->>'width'在处理后变为smth (我的意思是单引号).因此,如果您对此名称进行硬编码,则应记住插入它们,否则您将收到错误消息.

///有点offtopic ///

还有一个棘手的问题与django本身无关,但需要以某种方式处理.和attrsjson字段一样,它的键和值没有限制,你可以(取决于你的应用程序逻辑)获得一些非数字值,例如widthkey.在这种情况下,您将从DataError执行上述查询后获得postgres.同时会忽略NULL值,所以没关系.如果你能抓住错误那么没问题,你很幸运.在我的情况下,我需要忽略错误的值,这里唯一的方法是编写自定义的postgres函数来抑制错误的输出错误.

create or replace function safe_cast_to_numeric(text) returns numeric as $$
begin
    return cast($1 as numeric);
exception
    when invalid_text_representation then
        return null;
end;
$$ language plpgsql immutable;

然后使用它将文本转换为数字:

Model.objects.annotate(
    val=RawSQL("safe_cast_to_numeric(attrs->>%s)", (json_field_key,))
).aggregate(min=Min('val')

因此,我们为像json这样的动态事物得到了非常可靠的解决方案.



3> 小智..:

我知道这有点晚了(几个月),但是我在尝试执行此操作时遇到了这个帖子。设法做到这一点:

1)使用KeyTextTransform将jsonb值转换为文本

2)使用Cast将其转换为整数,以便SUM起作用:

q = myModel.objects.filter(type=9) \
.annotate(numeric_val=Cast(KeyTextTransform(sum_field, 'data'), IntegerField()))  \
.aggregate(Sum('numeric_val'))

print(q)

其中“数据”是jsonb属性,“数字_val”是我通过注释创建的变量的名称。

希望这对某人有帮助!

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有