查询Rails中的Postgres JSON数组字段

Luc*_*rna 6 arrays postgresql json ruby-on-rails jsonb

我试图在Postgres数据库中查询某个值.我有一个groupsusers表中命名的字段,可以用以下任何一种方式表示:

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}]}
Run Code Online (Sandbox Code Playgroud)

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}]
Run Code Online (Sandbox Code Playgroud)

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

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

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

我已经能够确定:

select groups -> 'data' ->> 'serie' from users;  
ERROR: cannot extract field from a non-object.
Run Code Online (Sandbox Code Playgroud)

但是以下查询有效:

select json_array_elements(groups -> 'data') ->> 'serie' from users;
Run Code Online (Sandbox Code Playgroud)

我想我没有在列中正确传递数据.我提供的哈希是:

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}]}}
Run Code Online (Sandbox Code Playgroud)

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

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}]}>
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 12

假设:

  • Postgres 9.4或更高版本.
  • "让所有在serie 5中的用户"应该是这样的:
    "至少有一个包含的数组元素{"serie": 5}.可能还有其他的."
  • 使用您的第一个更短的数据格式.没有冗余的"数据"键.

简短的回答:使用jsonb而不是,json这只是工作:

User.where("groups @> ?", '[{"serie": 5}]')
Run Code Online (Sandbox Code Playgroud)

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

为什么?

这里突出的误区:数据类型json不一样 jsonb.

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

select json_array_elements(groups -> 'data') ->> 'serie' from users;
Run Code Online (Sandbox Code Playgroud)

json_array_elements()只适用于json,必须jsonb_array_elements()适用jsonb.
但是你试图使用jsonb运算符 @>,这是不可能的json:

groups -> 'data' @>  '?'
Run Code Online (Sandbox Code Playgroud)

所述操作者->将返回相同类型的左手输入.但@>只是定义jsonb,而不是json.

然后,您尝试使用运营商@>对于text为左侧操作数.也不可能:

groups ->> 'data' @>  ?
Run Code Online (Sandbox Code Playgroud)

@>各种类型(包括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'
   );
Run Code Online (Sandbox Code Playgroud)

演示

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}]';
Run Code Online (Sandbox Code Playgroud)

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'
   );
Run Code Online (Sandbox Code Playgroud)