我正在使用Postgres 9.5,并且具有以下表格:
Users
id UUID
帖子的内容如下:
[ { "type": "text", "text": "learning pg" }, { "type": "image", "image_id": "8f4422b4-3936-49f5-ab02-50aea5e6755f" }, { "type": "image", "image_id": "57efc97c-b9b4-4cd5-b1e1-3539f5853835" }, { "type": "text", "text": "pg is awesome" } ]
现在,我想加入内容的图像类型,并用填充它们image_id,例如:
image_id
{ "id": "cb1267ca-b1ac-4daa-8c7e-72d4c000e9fa", "title": "Learning join jsonb in Postgres", "author_id": "deba01b7-ec58-4cc2-b3ae-7dc42e582767", "content": [ { "type": "text", "text": "learning pg" }, { "type": "image", "image": { "id": "8f4422b4-3936-49f5-ab02-50aea5e6755f", "key": "/upload/test1.jpg", "width": 800, "height": 600 } }, { "type": "image", "image": { "id": "57efc97c-b9b4-4cd5-b1e1-3539f5853835", "key": "/upload/test2.jpg", "width": 1280, "height": 720 } }, { "type": "text", "text": "pg is awesome" } ] }
这是我的测试sql文件:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; DROP TABLE IF EXISTS Users; DROP TABLE IF EXISTS Images; DROP TABLE IF EXISTS Posts; CREATE TABLE Users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name text NOT NULL ); CREATE TABLE Images ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), key TEXT, width INTEGER, height INTEGER, creator_id UUID ); CREATE TABLE Posts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), title TEXT, author_id UUID, content JSONB ); DO $$ DECLARE user_id UUID; DECLARE image1_id UUID; DECLARE image2_id UUID; BEGIN INSERT INTO Users (name) VALUES ('test user') RETURNING id INTO user_id; INSERT INTO Images (key, width, height, creator_id) VALUES ('upload/test1.jpg', 800, 600, user_id) RETURNING id INTO image1_id; INSERT INTO Images (key, width, height, creator_id) VALUES ('upload/test2.jpg', 600, 400, user_id) RETURNING id INTO image2_id; INSERT INTO Posts (title, author_id, content) VALUES ( 'test post', user_id, ('[ { "type": "text", "text": "learning pg" }, { "type": "image", "image_id": "' || image1_id || '" }, { "type": "image", "image_id": "' || image2_id || '" }, { "type": "text", "text": "pg is awesome" } ]') :: JSONB ); END $$;
有什么办法可以实现这一要求?
假设至少为Postgres 9.5,它将完成以下工作:
SELECT jsonb_pretty(to_jsonb(p)) AS post_row_as_json FROM ( SELECT id, title, author_id, c.content FROM posts p LEFT JOIN LATERAL ( SELECT jsonb_agg( CASE WHEN c.elem->>'type' = 'image' AND i.id IS NOT NULL THEN elem - 'image_id' || jsonb_build_object('image', i) ELSE c.elem END) AS content FROM jsonb_array_elements(p.content) AS c(elem) LEFT JOIN images i ON c.elem->>'type' = 'image' AND i.id = (elem->>'image_id')::uuid ) c ON true ) p;
取消嵌套jsonb数组,每个数组元素产生1行:
jsonb
jsonb_array_elements(p.content) AS c(elem)
对于每个元素LEFT JOIN到images在条件 一。的 关键 “类型”具有 值 “图像”:c.elem->>'type' = 'image' 湾 中的UUIDimage_id匹配:i.id = (elem->>'image_id')::uuid 请注意,无效的UUIDcontent将引发异常。
LEFT JOIN
images
c.elem->>'type' = 'image'
i.id = (elem->>'image_id')::uuid
content
对于图像类型,找到匹配的图像
c.elem->>'type' = 'image' AND i.id IS NOT NULL
删除键’image_id’并将相关图像行添加为jsonb值:
elem - 'image_id' || jsonb_build_object('image', i)
否则保留原始元素。
使用将新修改的元素重新聚合到一个新content列中jsonb_agg()。 也可以与普通的ARRAY构造函数一起使用。
jsonb_agg()
无条件地LEFT JOIN LATERAL将结果复制到posts并选择所有列,仅替换p.content为生成的替换项c.content
LEFT JOIN LATERAL
posts
p.content
c.content
在外部SELECT,jsonb用简单的将整个行转换为to_jsonb()。 jsonb_pretty()对于人类可读的表示形式是完全可选的。
SELECT
to_jsonb()
jsonb_pretty()
所有jsonb功能均在此处的手册中进行了说明。