查询Rails中的Postgres JSON数组字段

时间:2016-11-20 10:10:32

标签: ruby-on-rails arrays json postgresql jsonb

我正在尝试查询Postgres数据库中的某个值。我在Int表中有一个名为groups的字段,可以用以下任何一种方式表示:

1

users

2

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}]}

我对这两种表述都很好。但是,我似乎无法找到如何让所有在系列5中的用户让我们说。我尝试了多个查询:

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}]

还有许多其他尝试,有些比其他尝试更愚蠢(见上文)。我该怎么做?

我能够确定:

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

但是以下查询有效:

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

我认为我没有在列中正确传递数据。我提供的哈希是:

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

在保存资源之前,如下所示:

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}]}}

1 个答案:

答案 0 :(得分:10)

<强>假设:

  • Postgres 9.4或更高版本
  • &#34;让所有属于5&#34; 的用户理解为:
    &#34;至少有一个包含{"serie": 5}的数组元素。可能还有其他人。&#34;
  • 使用您的第一个更短的数据格式。没有多余的数据&#39;键。

简短回答:使用 jsonb 代替json,这只是有效:

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

注意方括号使右手操作数成为JSON 数组

为什么?

这里突出的误解:data type json is not the same as jsonb

您没有声明实际的表定义,但您后来评论了json并且问题中有一个提示:

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

json_array_elements()仅适用于jsonjsonb_array_elements()必须jsonb。 但是您尝试使用jsonb Operators @>json

groups -> 'data' @>  '?'

operator ->返回与左侧输入相同的类型。但@>仅为jsonb定义,而不是json

然后,您尝试将@>的运算符text用作左侧操作数。 不可能

groups ->> 'data' @>  ?

对于各种类型(包括Postgres数组),运算符@>有变体,但不适用于text而不适用于json

简而言之:使用jsonb代替json。这也允许使用非常高效索引

json

对于数据类型json,您可以使用:

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

演示

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

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 1
   FROM   json_array_elements(users.groups) elem 
   WHERE  elem ->> 'serie'  = '5'
   );