我正在使用Postgres 9.5,并且具有以下表格:

用户

  • id UUID
  • 名称TEXT

  • 图片
  • id UUID
  • 键文本
  • 宽度INTEGER
  • 高度INTEGER

  • 发布
  • id UUID
  • 标题TEXT
  • author_id UUID
  • 内容JSONB

  • 帖子的内容如下:
    [
      { "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填充它们,例如:
    {
      "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_array_elements(p.content) AS c(elem)
    
  • 在满足以下条件的情况下,将每个元素LEFT JOIN转换为images一种。键“类型”的值为“图像”:c.elem->>'type' = 'image'b。 image_id中的UUID匹配:i.id = (elem->>'image_id')::uuid请注意,content中的无效UUID会引发异常。
  • 对于图像类型,找到匹配的图像
    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 constructor一起使用。
  • 将结果无条件地LEFT JOIN LATERAL转换为posts并选择所有列,仅将p.content替换为生成的替换c.content
  • 在外部SELECT中,使用简单的jsonb将整个行转换为to_jsonb()jsonb_pretty()对于人类可读的表示形式是完全可选的。

  • All jsonb functions are documented in the manual here.

    关于sql - 如何在Postgres中加入jsonb数组元素?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40413861/

    10-13 03:22