查询包含JSON对象数组的jsonb列

时间:2016-07-30 22:49:49

标签: sql ruby-on-rails postgresql rails-activerecord jsonb

我使用 PostgreSQL 9.5 和Rails 5.我想查询下面显示的jsonb列,其中包含一组JSON对象,以返回包含{{1}的所有JSON数组元素并且还执行计数 我使用的 SQL 显示在json数据下面。运行查询只返回一个空数组。

我已尝试过建议的问题herehere

这就是我的{"kind":"person"}数据:

jsonb

我想要一个sql查询返回:

   '[
        {"kind":"person", "filter_term":"56","selected_attr":"customer"},
        {"kind":"email", "filter_term":"marketer","selected_attr":"job_title"}
      ]'

和另一个返回数组的查询,以便我可以在我的应用程序中调用count并在其上循环以创建表单:

                             data
----------------------------------------------------------------------
 '{"kind":"person", "filter_term":"56","selected_attr":"customer"}'
(1 row)

我尝试了这个SQL查询:

 data
----------------------------------------------------------------------
 '[{"kind":"person", "filter_term":"56","selected_attr":"customer"}]'
 (1 row)

我也试过这个问题:

 "SELECT * FROM \"segments\" WHERE (payload @> '[{\"kind\":\"person\"}]')"

这是第三个查询:

  "SELECT payload FROM segments WHERE payload @> '[{\"kind\":\"person\"}]'::jsonb;"

模特:

 "SELECT * FROM segments s WHERE s.payload->'\"#{a}\"' @> '[{\"kind\":\"person\"}]';"

迁移:

class Segment < ApplicationRecord
 store_accessor :payload,:kind, :filter_term, :selected_model_name, :selected_attr, :limit, :selected_operator
end

1 个答案:

答案 0 :(得分:10)

假设这个表定义:

CREATE TABLE segments (segments_id serial PRIMARY KEY, payload jsonb);

使用这样的JSON值:

INSERT INTO segments (payload)
VALUES ('[
            {
                "kind": "person",
                "limit": "1",
                "filter_term": "56",
                "selected_attr": "customer",
                "selected_operator": "less_than"
            },
            {
                "kind": "email",
                "filter_term": "marketer",
                "selected_attr": "job_title",
                "selected_operator": "equals"
            }
        ]'
   );
  • 您想要返回包含键/值对"kind":"person"(不是嵌套的JSON对象{"kind":"person"})的JSON数组的元素 - 并计算数组元素和表行(可能有每行多个匹配的数组元素。)

解决方案

要获取列jsonb中包含符合条件的segments值的 行数

SELECT count(*)
FROM   segments s
WHERE  s.payload @> '[{"kind":"person"}]';

获取 所有符合条件的JSON数组元素 (自身为JSON对象) - 加上元素的总数(可能同时大于上面的数量:< / p>

SELECT j.*
FROM   segments s
JOIN   LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}'
WHERE  s.payload @> '[{"kind":"person"}]';

返回:

elem
------------------------------------------------------------
{"kind": "person", "limit": "1", "filter_term": "56", ... }

要一次

SELECT j.*, count(*) OVER () AS ct_elem, s.ct_rows
FROM  (
   SELECT payload, count(*) OVER () AS ct_rows
   FROM   segments
   WHERE  payload @> '[{"kind":"person"}]'
   ) s
JOIN   LATERAL jsonb_array_elements(s.payload) j(elem) ON j.elem @> '{"kind":"person"}';

返回(对于包含更多条目的表):

elem                      | ct_elem | ct_rows
--------------------------+---------+---------
{"kind": "person",  ... } | 4       | 3
{"kind": "person",  ... } | 4       | 3
...

但是 我认为你真的想要这个

SELECT a.*
     , sum(ct_elem_row) OVER () AS ct_elem_total
     , count(*)         OVER () AS ct_rows
FROM   segments s
JOIN   LATERAL (
   SELECT json_agg(j.elem) AS filtered_payload, count(*) AS ct_elem_row
   FROM   jsonb_array_elements(s.payload) j(elem)
   WHERE  j.elem @> '{"kind":"person"}'
   ) a ON ct_elem_row > 0
WHERE  s.payload @> '[{"kind":"person"}]';

返回(对于包含更多条目的表):

filtered_payload                                     | ct_elem_row | ct_elem_total | ct_rows
-----------------------------------------------------+-------------+---------------+---------
[{"kind": "person", ... }]                           | 1           | 4             | 3
[{"kind": "person", ... }]                           | 1           | 4             | 3
[{"kind": "person", ... }, {"kind": "person", ... }] | 2           | 4             | 3

这标识匹配的行,然后选择匹配的数组元素,并且每行只构建一个数组。加上重要。

为了获得最佳性能,您将获得jsonb_path_ops GIN索引,如:

CREATE INDEX segments_path_ops_gin_idx ON segments 
USING  gin (payload jsonb_path_ops);

(但是,提供更多不同查询的更通用的索引可能是更好的选择。)

相关:

<强>术语

我们正在处理一个包含 JSON数组的JSON对象,保存为Postgres jsonb数据类型 - 简称为“JSON数组”,但不是一个“JSON数组”。