查询 Rails 中的 Postgres JSON 数组字段
Query on Postgres JSON array field in Rails
我正在尝试查询 Postgres 数据库中的某个值。我在 users
table 中有一个名为 groups
的字段,可以用以下任一方式表示:
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.
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}]
我对这两种表述都满意。但是,我似乎无法找到如何获得 serie 5 中的所有用户。我尝试了多个查询:
@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}]}}
保存资源之前是这样的:
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}]}>
假设:
- Postgres 9.4 或更高版本。
- "get all the users that are in serie 5" 应该是指:
"with at least one array element that contains {"serie": 5}
. There may be others."
- 使用您的第一个较短的数据格式。没有多余的 'data' 键。
简短回答: 使用 jsonb
而不是 json
,这很有效:
User.where("groups @> ?", '[{"serie": 5}]')
注意方括号使右边的操作数成为 JSON array.
为什么?
此处突出的误解:data type json
is not the same as jsonb
.
你没有声明实际的 table 定义,但你后来评论 json
并且问题中有提示:
select json_array_elements(groups -> 'data') ->> 'serie' from users;
json_array_elements()
仅适用于 json
,对于 jsonb
必须是 jsonb_array_elements()
。但是您尝试使用未为 json
:
定义的 jsonb
operator @>
groups -> 'data' @> '?'
operator ->
returns与左手输入相同的类型。但是 @>
只为 jsonb
定义,而不是 json
.
然后您尝试使用 text
的运算符 @>
作为左侧操作数。 也不可能:
groups ->> 'data' @> ?
对于各种类型(包括 Postgres 数组),运算符 @>
有变体,但对于 text
和 json
都没有。
所以,简短的回答:使用 jsonb
而不是 json
。这也允许使用非常 高效的索引 :
- Index for finding an element in a JSON array
json
对于数据类型 json
你可以使用:
SELECT *
FROM users u
WHERE EXISTS (
SELECT 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 FROM json_array_elements(users.groups) elem
WHERE elem ->> 'serie' = '5'
);
我正在尝试查询 Postgres 数据库中的某个值。我在 users
table 中有一个名为 groups
的字段,可以用以下任一方式表示:
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.
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}]
我对这两种表述都满意。但是,我似乎无法找到如何获得 serie 5 中的所有用户。我尝试了多个查询:
@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}]}}
保存资源之前是这样的:
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}]}>
假设:
- Postgres 9.4 或更高版本。
- "get all the users that are in serie 5" 应该是指:
"with at least one array element that contains{"serie": 5}
. There may be others." - 使用您的第一个较短的数据格式。没有多余的 'data' 键。
简短回答: 使用 jsonb
而不是 json
,这很有效:
User.where("groups @> ?", '[{"serie": 5}]')
注意方括号使右边的操作数成为 JSON array.
为什么?
此处突出的误解:data type json
is not the same as jsonb
.
你没有声明实际的 table 定义,但你后来评论 json
并且问题中有提示:
select json_array_elements(groups -> 'data') ->> 'serie' from users;
json_array_elements()
仅适用于 json
,对于 jsonb
必须是 jsonb_array_elements()
。但是您尝试使用未为 json
:
jsonb
operator @>
groups -> 'data' @> '?'
operator ->
returns与左手输入相同的类型。但是 @>
只为 jsonb
定义,而不是 json
.
然后您尝试使用 text
的运算符 @>
作为左侧操作数。 也不可能:
groups ->> 'data' @> ?
对于各种类型(包括 Postgres 数组),运算符 @>
有变体,但对于 text
和 json
都没有。
所以,简短的回答:使用 jsonb
而不是 json
。这也允许使用非常 高效的索引 :
- Index for finding an element in a JSON array
json
对于数据类型 json
你可以使用:
SELECT *
FROM users u
WHERE EXISTS (
SELECT 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 FROM json_array_elements(users.groups) elem
WHERE elem ->> 'serie' = '5'
);