关于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 相关文章推荐
PostgreSQL存储过程实用脚本(二):创建函数入门
Apr 05 PostgreSQL
Centos环境下Postgresql 安装配置及环境变量配置技巧
May 18 PostgreSQL
通过Qt连接OpenGauss数据库的详细教程
Jun 23 PostgreSQL
浅谈PostgreSQL表分区的三种方式
Jun 29 PostgreSQL
postgresql 删除重复数据案例详解
Aug 02 PostgreSQL
PostgreSQL自动更新时间戳实例代码
Nov 27 PostgreSQL
PostgreSQL事务回卷实战案例详析
Mar 25 PostgreSQL
PostgreSQL并行计算算法及参数强制并行度设置方法
Apr 06 PostgreSQL
PostgreSQL数据库创建并使用视图以及子查询
Apr 11 PostgreSQL
postgreSQL数据库基础知识介绍
Apr 12 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
Terran剧情介绍
2020/03/14 星际争霸
php+ajax登录跳转登录实现思路
2016/07/31 PHP
php使用 readfile() 函数设置文件大小大小的方法
2017/08/11 PHP
使用Laravel中的查询构造器实现增删改查功能
2019/09/03 PHP
tp5框架的增删改查操作示例
2019/10/31 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
将CKfinder整合进CKEditor3.0的新方法
2010/01/10 Javascript
js修改地址栏URL参数解决url参数问题
2012/12/15 Javascript
js的flv视频播放器插件使用方法
2015/06/23 Javascript
JavaScript数组去重的3种方法和代码实例
2015/07/01 Javascript
基于jQuery实现复选框是否选中进行答题提示
2015/12/10 Javascript
js实现根据身份证号自动生成出生日期
2015/12/15 Javascript
AngularJS身份验证的方法
2016/02/17 Javascript
js 上传文件预览的简单实例
2016/08/16 Javascript
JavaScript中利用构造器函数模拟类的方法
2017/02/16 Javascript
详解Angular.js数据绑定时自动转义html标签及内容
2017/03/30 Javascript
基于js 字符串indexof与search方法的区别(详解)
2017/12/04 Javascript
通过cordova将vue项目打包为webapp的方法
2019/02/02 Javascript
Angular.JS读取数据库数据调用完整实例
2019/07/02 Javascript
layui添加动态菜单与选项卡
2019/07/26 Javascript
layui 关闭open弹出框 刷新table表格页面的方法
2019/09/16 Javascript
利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化)
2021/02/24 Javascript
[03:03]2014DOTA2国际邀请赛 EG战队专访
2014/07/12 DOTA
[01:02:55]CHAOS vs Mineski 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
Django验证码的生成与使用示例
2017/05/20 Python
pandas实现DataFrame显示最大行列,不省略显示实例
2019/12/26 Python
Python3爬虫关于代理池的维护详解
2020/07/30 Python
校园文化建设方案
2014/02/03 职场文书
教师开学感言
2014/02/14 职场文书
新农村建设汇报材料
2014/08/15 职场文书
四风问题对照检查整改措施思想报告
2014/10/05 职场文书
2015年财务个人工作总结范文
2015/05/22 职场文书
实习证明格式范文
2015/06/16 职场文书
导游词之河北野三坡
2019/12/11 职场文书
vue-cropper插件实现图片截取上传组件封装
2021/05/27 Vue.js
win10键盘驱动怎么修复?Win10键盘驱动修复小技巧
2022/04/06 数码科技