简体   繁体   English

在 Rails 中查询 Postgres JSON 数组字段

[英]Query on Postgres JSON array field in Rails

I am trying to query a certain value in a Postgres database.我正在尝试查询 Postgres 数据库中的某个值。 I have a field named groups in the users table that can be represented in either of these ways:我在users表中有一个名为groups的字段,可以用以下任何一种方式表示:

1. 1.

groups: {"data"=>[{"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}, {"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}]}

2. 2.

groups: [{"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}, {"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}]

I am fine with either of this representations.我对这两种表示都很好。 However I just can't seem to find out how to get all the users that are in serie 5 let's say.但是,我似乎无法找到如何获得意甲 5 中的所有用户的方法。 I tried multiple queries along the lines of:我尝试了多个查询:

@users = User.where("groups ->> 'data' @>  ?", {serie: 5})
@users = User.where("groups -> 'data' @>  '?'", {serie: 5})
@users = User.where("groups ->> 'data' ->> 'serie' = ?", 5)

And many other attempts, some more stupid than others (see above).还有许多其他尝试,有些比其他尝试更愚蠢(见上文)。 How would I do it?我该怎么做?

I have been able to determine that:我已经能够确定:

select groups -> 'data' ->> 'serie' from users;  
ERROR: cannot extract field from a non-object.

However the following query works:但是,以下查询有效:

select json_array_elements(groups -> 'data') ->> 'serie' from users;

I think I am not properly delivering the data in the column.我认为我没有正确传递列中的数据。 The hash I am providing to create is:我要创建的 hash 是:

pry(#<Overrides::RegistrationsController>)> @response['data']['user']
=> {"last_name"=>"Doe1",
 "first_name"=>"John1",
 "email"=>"c0f45@example.com",
 "groups"=>
  {"data"=>
    [{"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}, {"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}]}}

Before saving the resource looks like this:在保存资源之前是这样的:

pry(#<Overrides::RegistrationsController>)> @resource
=> #<User id: nil, provider: "email", uid: "", first_name: "John1", last_name: "Doe1", email: "c0f45@example.com", role: "Student", created_at: nil, updated_at: nil, groups: {"data"=>[{"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}, {"serie"=>5, "year"=>3, "specialization"=>"Matematica", "management_id"=>1, "group_number"=>2}]}>

Assumptions:假设:

  • Postgres 9.4 or later . Postgres 9.4 或更高版本
  • "get all the users that are in serie 5" is supposed to mean: “获取所有在 5 级联赛中的用户”应该是指:
    "with at least one array element that contains {"serie": 5} . There may be others." “至少有一个包含{"serie": 5}数组元素。可能还有其他元素。”
  • Working with your first, shorter data format.使用您的第一个较短的数据格式。 No redundant 'data' key.没有多余的“数据”键。

Short answer: Use jsonb instead of json and this just works:简短的回答:使用jsonb而不是json这只是有效:

User.where("groups @> ?", '[{"serie": 5}]')

Note the square brackets to make the right-hand operand a JSON array .请注意使右侧操作数成为 JSON数组的方括号。

Why?为什么?

The prominent misunderstanding here: data type json is not the same as jsonb .这里有一个突出的误解:数据类型jsonjsonb

You didn't declare the actual table definition, but you later commented json and there is a hint in the question:您没有声明实际的表定义,但您后来评论了json并且问题中有一个提示:

 select json_array_elements(groups -> 'data') ->> 'serie' from users;

json_array_elements() only works for json , would have to be jsonb_array_elements() for jsonb . json_array_elements()仅适用于json ,对于jsonb必须是jsonb_array_elements() But you try to use the jsonb operator @> which is not defined for json :但是您尝试使用未为json定义的jsonb运算符@>

 groups -> 'data' @> '?'

The operator -> returns the same type as the left-hand input. 运算符->返回与左侧输入相同的类型。 But @> is only defined for jsonb , not for json .但是@>只为jsonb定义,而不是为json定义。

Then you try to use the operator @> for text as left-hand operand.然后您尝试将运算符@>用于text作为左侧操作数。 Not possible either :也不可能

 groups ->> 'data' @> ?

There are variants of the operator @> for various types (incl. Postgres arrays), but not for text and not for json .运算符@>有多种类型(包括 Postgres 数组)的变体,但不适用于textjson

So, the short answer: Use jsonb instead of json .所以,简短的回答:使用jsonb而不是json This allows to use very efficient indexes , too:这也允许使用非常有效的索引

json

For data type json you could use:对于数据类型json您可以使用:

SELECT *
FROM   users u
WHERE  EXISTS (
   SELECT FROM json_array_elements(u.groups) elem 
   WHERE  elem ->> 'serie' = '5'
   );

Demos演示

jsonb : jsonb :

SELECT *
FROM  (
   VALUES (1, jsonb '[{"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
        , (2,       '[{"serie":7, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":8, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
        , (3,       '[{"serie":9, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
   ) users(id, groups)
WHERE  groups @> '[{"serie": 5}]';

json : json

SELECT *
FROM  (
   VALUES (1, json  '[{"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
        , (2,       '[{"serie":7, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":8, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
        , (3,       '[{"serie":9, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}
                    , {"serie":5, "year":3, "specialization":"Matematica", "management_id":1, "group_number":2}]')
   ) users(id, groups)
WHERE  EXISTS (
   SELECT FROM json_array_elements(users.groups) elem 
   WHERE  elem ->> 'serie'  = '5'
   );

JSON filtering in Rails在 Rails 中过滤 JSON

Event.create( payload: [{ "name": 'Jack', "age": 12 },
                                 { "name": 'John', "age": 13 },
                                 { "name": 'Dohn', "age": 24 }]

Event.where('payload @> ?', '[{"age": 12}]')
#You can also filter by name key
Event.where('payload @> ?', '[{"name": "John"}]')
#You can also filter by {"name":"Jack", "age":12}
Event.where('payload @> ?', {"name":"Jack", "age":12}.to_json)

You can find more about this here您可以在此处找到更多相关信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM