【问题标题】:Query jsonb array for integer member查询整数成员的 jsonb 数组
【发布时间】:2018-02-22 09:20:06
【问题描述】:

背景:我们使用 PaperTrail 来保存我们不断变化的模型的历史。现在我想查询属于某个客户的商品。 PaperTrail 可选择存储object_changes,我需要查询此字段以了解何时使用此 ID 创建或更改为此 ID。

我的表格看起来像这样简化:

item_type | object_changes
----------|----------------------------------------------------------
"Item"    | {"customer_id": [null, 5], "other": [null, "change"]}
"Item"    | {"customer_id": [4, 5], "other": ["unrelated", "change"]}
"Item"    | {"customer_id": [5, 6], "other": ["asht", "asht"]}

如何查询从 ID 5 更改为或更改为 ID 5 的元素(以上所有行)?我试过了:

SELECT * FROM versions WHERE object_changes->'customer_id' ? 5;

这让我:

ERROR:  operator does not exist: jsonb ? integer
LINE 1: ...T * FROM versions WHERE object_changes->'customer_id' ? 5;
                                                                 ^
HINT:  No operator matches the given name and argument type(s).
You might need to add explicit type casts.

【问题讨论】:

  • 您可能还喜欢where_object_changes 方法。这应该是为object_changes 列创建where 子句的便捷方式。
  • 那么你有答案了吗?

标签: ruby-on-rails postgresql jsonb paper-trail-gem postgresql-9.6


【解决方案1】:

对于 jsonbcontains operator @> 可以满足您的要求:

获取数字 5 是“customer_id”数组元素的所有行:

SELECT *
FROM   versions 
WHERE  object_changes->'customer_id' @> '5';

@> 运算符需要 jsonb 作为右操作数 - 或对 jsonb 有效的字符串文字(而 ? 需要 text)。您在示例中提供的数字文字不带单引号 (5) 不能强制转换为jsonb(也不是text),它默认为integer。因此出现错误消息。相关:

这可以通过不同的索引样式来支持。对于我上面建议的查询,使用表达式索引(专业、小而快):

CREATE INDEX versions_object_changes_customer_id_gin_idx ON versions
USING gin ((object_changes->'customer_id'));

这个替代查询也有效:

SELECT * FROM versions WHERE object_changes @> '{"customer_id": [5]}';

并且可以支持通用索引(更通用、更大、更慢):

CREATE INDEX versions_object_changes_gin_idx ON versions
USING gin (object_changes jsonb_path_ops);

相关:

According to the manual,运算符 ? 搜索任何top-level key within the JSON value。测试表明数组中的字符串被视为“顶级键”,但数字不是 (毕竟键必须是字符串)。所以虽然这个查询可以工作:

SELECT * FROM versions WHERE object_changes->'other' ? 'asht';

您在数组中查找 number 的查询不会(即使您正确引用了输入字符串文字)。它只会找到(引用!)字符串"5",分类为key,而不是(未引用)数字5,分类为value

除此之外:Standard JSON only knows 4 primitivesstringnumberbooleannull。没有 integer 原语(即使我听说过软件添加),integer 只是 number 的一个子集,它被实现在 Postgres 中作为 numeric

所以你的问题标题有点误导,因为严格来说没有“整数”成员。

【讨论】:

    【解决方案2】:

    使用横向连接和jsonb_array_elements_text 函数处理每一行的object_changes

    SELECT DISTINCT v.* FROM versions v
    JOIN LATERAL jsonb_array_elements_text(v.object_changes->'customer_id') ids ON TRUE
    WHERE ids.value::int = 5;
    

    仅当您要查找的 customer_id 可能在数组中出现多次时,DISTINCT 才是必需的(如果不同的字段发生了更改,但仍然跟踪了 customer_id)。

    【讨论】:

      猜你喜欢
      • 2021-09-28
      • 2019-07-02
      • 2022-01-22
      • 1970-01-01
      • 2021-01-29
      • 2022-01-11
      • 2020-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多