关于PostgreSQL JSONB的匹配和交集问题


Posted in PostgreSQL onSeptember 14, 2021

PostgreSQL 自从支持 JSONB 到现在,已经有十余年,这十多年来,社区为 JSONB 提供了很多强大的功能。就我个人而言,其实最常用的还是匹配操作 @> 。

把JSON数据看作一个抽象语法树(AST)的话,这个操作符判断右参数是不是左参数的子图。

这里本来应该有个图示, 但是周末的时候临时有个数据集在处理,所以没有时间去找合适的工具了。简单举几个例子,下面这个例子得到true,这应该很好理解:

select '{"a": 1, "b": 2, "c": 3}'::jsonb @> '{"b":2}' ;
--------------
t

而它也可以匹配更复杂的情况,下面这个例子也是 true:

select '{"a": 1, "b": 2, "c": {"value": 3}}'::jsonb @> '{"c":{"value": 3}}';
 ?column?
----------
 t
(1 row)

下面这个例子可能新用户会有点儿迷惑,但是其实也很好的契合了这个规则:

select '{"a": 1, "b": 2, "c": {"value": 3}}'::jsonb @> '{"c":{}}';
 ?column?
----------
 t
(1 row)

但是应该注意的是,下面这个例子结果是 false:

select '{"a": 1, "b": 2, "c": {"value": 3}}'::jsonb @> '{"c":[]}';
 ?column?
----------
 f
(1 row)

这也不难理解,{} 和 [] 不相等。

下面这个例子比较有意思:

select '{"a": 1, "b": 2, "c": {"value": [1, 2, 3]}}'::jsonb @> '{"c":{"value": [2]}}';
 ?column?
----------
 t
(1 row)

这里要注意的是,比较一个 JSON 数组是否匹配另一个时,它并不要求两个数组的顺序相等,只要右边是左边的真子集就可以:

select '{"a": 1, "b": 2, "c": {"value": [1, 2, 3]}}'::jsonb @> '{"c":{"value": [2]}}';
 ?column?
----------
 t
(1 row)
 
select '{"a": 1, "b": 2, "c": {"value": [1, 2, 3]}}'::jsonb @> '{"c":{"value": [5, 2]}}';
 ?column?
----------
 f
(1 row)
 
select '{"a": 1, "b": 2, "c": {"value": [1, 2, 3]}}'::jsonb @> '{"c":{"value": [3, 2]}}';
 ?column?
----------
 t
(1 row)

这个规则契合了PostgreSQL的倒排索引,PostgreSQL的gin索引,JSONB 字段类型和匹配操作 @> 成为了一个非常有力的组合。在过去几年里,我习惯为一些重要的业务表加上一个类型为 JSONB 的meta 字段,并对其建立 gin 索引

create index idx_xxx_meta on xxx using(gin);

需要注意的是指定索引类型时的 create index 语法。

这样的设计可以解决很多传统上难以解决的问题,例如我可以给每个条目打上一个 tag 列表,取带有某几个 tag 的条目就是一个简单的匹配查询:

select xxx from data_table where meta @> '{"tags": ["tag1", "tagx", "tagy"]}'

因为有gin索引的帮助,这个搜索的性能足够常规的互联网应用所需。

甚至我的在 CSDN NLP 组的同事还挖掘出了新的用法。我们在一个存储树节点的表里,保存了一个 meta 字段,其中有一个 path 列表,存储当前字段在树中的路径,它的每一项都是 {"id": node_id, "title": something}这样的结构,而我们搜索某一个节点下面的所有子节点,包括其隔代的子节点时,仅需要执行这样一个查询:

select xxx from tree_node where meta @> '{"path": [{"id": node_id}]}'

当然这个匹配操作也有它的限制,它在右边是左边的真子图的情况下才会匹配成功。例如我希望查找 tags 列表中包含我搜索项中的任何一个(即两者存在非空交集)的情况,用这种方法就不行了。此时我们需要另一个运算符 ?|

select '["tag1", "tag2", "tag3"]'::jsonb ?| '{tag2, tag3}';
 ?column?
----------
 t
(1 row)
 
select '["tag1", "tag2", "tag3"]'::jsonb ?| '{tag2, tag3, tag5}';
 ?column?
----------
 t
(1 row)
 
select '["tag1", "tag2", "tag3"]'::jsonb ?| '{tag5}';
 ?column?
----------
 f
(1 row)

注意这几个例子,首先右边的运算符不再是jsonb,而必须是 text[],其次它其实是检查 key 值——也就是可以通过 gin 索引存储的值:

select '{"tag1":1, "tag2":2, "tag3":3}'::jsonb ?| '{tag5}';
 ?column?
----------
 f
(1 row)
 
select '{"tag1":1, "tag2":2, "tag3":3}'::jsonb ?| '{tag3}';
 ?column?
----------
 t
(1 row)
 
select '{"tag1":1, "tag2":2, "tag3":3}'::jsonb ?| '{tag3, tag1}';
 ?column?
----------
 t
(1 row)

PostgreSQL 支持 JSON 和 JSONB 已经有十余年,每一个版本都在积极的增强其 JSON 数据处理能力,即使我近十年来的积极探索和学习,也没有全面的了解。这个交集运算也是近期在 NLP 组的工作过程中才注意到的。

到此这篇关于PostgreSQL JSONB的匹配和交集的文章就介绍到这了,更多相关PostgreSQL JSONB内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PostgreSQL 相关文章推荐
Centos环境下Postgresql 安装配置及环境变量配置技巧
May 18 PostgreSQL
如何使用PostgreSQL进行中文全文检索
May 27 PostgreSQL
PostgreSQL13基于流复制搭建后备服务器的方法
Jan 18 PostgreSQL
PostGIS的安装与入门使用指南
Jan 18 PostgreSQL
使用PostGIS完成两点间的河流轨迹及流经长度的计算(推荐)
Jan 18 PostgreSQL
PostgreSQL事务回卷实战案例详析
Mar 25 PostgreSQL
PostgreSQL 插入INSERT、删除DELETE、更新UPDATE、事务transaction
Apr 12 PostgreSQL
PostgreSQL数据库去除重复数据和运算符的基本查询操作
Apr 12 PostgreSQL
PostgreSQL怎么创建分区表详解
Jun 25 PostgreSQL
PostgreSQL逻辑复制解密原理解析
Sep 23 PostgreSQL
postgresql 删除重复数据案例详解
Aug 02 #PostgreSQL
PostgreSQL解析URL的方法
Aug 02 #PostgreSQL
postgresql使用filter进行多维度聚合的解决方法
Jul 16 #PostgreSQL
浅谈PostgreSQL表分区的三种方式
通过Qt连接OpenGauss数据库的详细教程
postgres之jsonb属性的使用操作
Jun 23 #PostgreSQL
postgresql无序uuid性能测试及对数据库的影响
Jun 11 #PostgreSQL
You might like
php读取文件内容的方法汇总
2015/01/24 PHP
PHP输出九九乘法表代码实例
2015/03/27 PHP
WordPress开发中用于获取近期文章的PHP函数使用解析
2016/01/05 PHP
php处理单文件、多文件上传代码分享
2016/08/24 PHP
PHP二进制与字符串之间的相互转换教程
2016/10/14 PHP
PHP检查URL包含特定字符串实例方法
2019/02/11 PHP
easyui datagrid 键盘上下控制选中行示例
2014/03/31 Javascript
TypeScript 学习笔记之基本类型
2015/06/19 Javascript
JS实现的通用表单验证插件完整实例
2015/08/20 Javascript
HTML5游戏引擎LTweenLite实现的超帅动画效果(附demo源码下载)
2016/01/26 Javascript
js拼接html字符串的注意事项
2016/10/13 Javascript
详解js树形控件—zTree使用总结
2016/12/28 Javascript
JS闭包用法实例分析
2017/03/27 Javascript
纯js实现的积木(div层)拖动功能示例
2017/07/19 Javascript
WdatePicker.js时间日期插件的使用方法
2017/07/26 Javascript
详解Nuxt.js Vue服务端渲染摸索
2018/02/08 Javascript
vue中使用ueditor富文本编辑器
2018/02/08 Javascript
vue中如何实现pdf文件预览的方法
2018/07/12 Javascript
JavaScript 格式化数字、金额、千分位、保留几位小数、舍入舍去
2019/07/23 Javascript
深入浅析Vue中mixin和extend的区别和使用场景
2019/08/01 Javascript
JS前后端实现身份证号验证代码解析
2020/07/23 Javascript
解决vue单页面多个组件嵌套监听浏览器窗口变化问题
2020/07/30 Javascript
[01:25]DOTA2自定义游戏灵园鬼域等你踏足
2015/10/30 DOTA
解析Python中的二进制位运算符
2015/05/13 Python
Python 数值区间处理_对interval 库的快速入门详解
2018/11/16 Python
Python标准库使用OrderedDict类的实例讲解
2019/02/14 Python
Python实现Linux监控的方法
2019/05/16 Python
VSCode中自动为Python文件添加头部注释
2019/11/14 Python
css3 transform及原生js实现鼠标拖动3D立方体旋转
2016/06/20 HTML / CSS
菲律宾领先的在线时尚商店:Zalora菲律宾
2018/02/08 全球购物
菲律宾最大的网上花店和礼品店:PhilFlower.com
2018/02/09 全球购物
介绍一下Java中的Class类
2015/04/10 面试题
会计电算化专业毕业生推荐信
2013/12/24 职场文书
幼儿园教师自我鉴定
2014/03/20 职场文书
Nginx已编译的nginx-添加新模块
2021/04/01 Servers
Java实现HTML转为Word的示例代码
2022/06/28 Java/Android