【问题标题】:Query jsonb column containing array of JSON objects查询包含 JSON 对象数组的 jsonb 列
【发布时间】:2016-12-05 08:57:57
【问题描述】:

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

我已经尝试了herehere 建议的查询。

这就是我的jsonb 数据的样子:

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

我希望返回其中一个 sql 查询:

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

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

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

我试过这个 SQL 查询:

 "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

迁移:

create_table "segments", force: :cascade do |t|

  t.jsonb    "payload",    default: "[]", null: false
  t.index ["payload"], name: "index_segments_on_payload", using: :gin

end

【问题讨论】:

  • =&gt;hstore 的有效语法,但不适用于 json(或 jsonb)。有效的 json 文字:'{"kind":"person"}' 请修正您的示例并添加您的预期结果。 "all the json" 有点模糊。还有表格行,还有数据类型json,但是什么是"json row"呢?你的意思是 JSON 数组元素?
  • 非常感谢您的帮助。是的,我的意思是 JSON 数组元素。我已经改写了这个问题,希望它更清楚。

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


【解决方案1】:

假设这个表定义:

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 数组的元素 - 并计算数组元素以及表行数(可能有多个匹配的数组元素每行)。

解决方案

要获得在列segments 中包含合格jsonb 值的行数

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

要获得所有符合条件的 JSON 数组元素(本身就是 JSON 对象)- 加上元素的总数(可能同时大于上述计数:

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

返回:

元素 -------------------------------------------------- ---------- {“种类”:“人”,“限制”:“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"}';

返回(对于具有更多条目的表):

元素 | ct_elem | ct_rows --------------+---------+--------- {"种类": "人", ... } | 4 | 3 {"种类": "人", ... } | 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"}]';

返回(对于具有更多条目的表):

过滤负载 | 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 数组”。

【讨论】:

  • 非常感谢您的详细解释和慷慨分享您的知识。
  • 我很高兴将我的数据更改为您的答案中的表单,但只是为了澄清一下,以我最初的方式存储 json 对象是否是错误的设计,类似于您推荐给我的第一个链接收件人:stackoverflow.com/questions/18404055/…,您在其中建议了此查询 SELECT * FROM tracking WHERE artist @> '[{"name": "The Dirty Heads"}]'; 。如果我将该查询调整为 "SELECT * FROM segments WHERE payload @> '[{\"kind\": \"person\"}]';" 以使用类似的原始 json 进行测试对他们来说,它返回空结果。
  • 我刚刚看了我的问题,明白了为什么你认为我嵌套在像这样 '{"a": [{ }, { }] }' 这样的键下你这样做是因为我在问题中的 json 对象之前有 a= 。因此,我已将其从我的问题中删除,因为我当前的数据与您提到的第一个链接中的数据完全相同。即 '[{ }, { }]'。我指的是我的第一条评论中的链接,该链接位于该评论上方。
  • @brg:我做了相应的简化。
  • 我通过psql直接在数据库上运行每条sql。通过返回正确的计数,第一个 count 查询按预期工作。但是所有其他查询只返回 (0 rows) 而不是您的示例中显示的预期响应。所以我运行了 SELECT * FROM segments; 并复制了数据库返回的内容并将其粘贴到此处,以便您可以看到确切的表格及其内容:gist.github.com/anonymous/1416f3fe748c792e85047a8d42d8fc3f
猜你喜欢
  • 2021-03-19
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多