【问题标题】:Update specific object in array of objects Postgres jsonb更新对象数组中的特定对象 Postgres jsonb
【发布时间】:2021-02-16 03:56:05
【问题描述】:

我正在尝试更新表 Books 上的 jsonb 列 pagesRead,其中包含对象数组。它的结构看起来像这样:

[{
    "book": "Moby Dick",
    "pagesRead": [
    "1",
    "2",
    "3",
    "4"
    ]
},
{
    "book": "Book Thief",
    "pagesRead": [
    "1",
    "2"
    ]
}]

我要做的是在阅读书的特定页面或有人开始新书时更新 pagesRead,在其中添加一个额外的条目。

我可以检索特定图书的详细信息,但我不确定如何更新它。

编辑:所以我不得不使用 S-Man 的 Update 查询来添加图书条目,但我使用了 Barbaros Özhan 的 Insert 查询来处理页面更新

【问题讨论】:

    标签: sql json postgresql jsonb


    【解决方案1】:

    之前的一些想法:

    1. 您不应该将结构化数据原样存储在一列中。这会产生更新、索引(因此,搜索/性能)、过滤等所有问题。请将所有内容规范化为适当的表格和列
    2. 永远不要存储数组。规范化它。
    3. 不要使用类型文本来存储整数(页)
    4. "pagesRead" 是您的过滤器元素的兄弟 ("book")。这使得引用它比引用它作为一个孩子要复杂得多。因此,请考虑将书名(或更好的:id)作为{"my_id_for_book_thief": {"name" : "Book Thief", "pagesRead": [...]}} 之类的键。在这种情况下,您可以使用路径来引用它。否则,我们需要提取数组,查看每个 book 属性并引用其兄弟

    demo:db<>fiddle

    添加book 非常简单(假设您使用jsonb 类型而不是json 类型):

    SELECT mydata || '{"book": "Lord Of The Rings", "pagesRead": []}'
    FROM mytable
    

    更新:

    UPDATE mytable
    SET mycolumn = mycolumn || '{"book": "Lord Of The Rings", "pagesRead": []}'
    

    添加pagesRead 值:

    SELECT 
        jsonb_agg(                                                         -- 4
            jsonb_build_object(                                            -- 3
                'book', elem -> 'book',
                'pagesRead', CASE WHEN elem ->> 'book' = 'Moby Dick' THEN  -- 2
                                 elem -> 'pagesRead' || '"42"'
                             ELSE elem -> 'pagesRead' END
            )
        ) as new_array
    FROM mytable,
        jsonb_array_elements(mydata) as elem                               -- 1
    
    1. 将数组提取为每个元素一条记录
    2. 如果元素包含正确的书,则添加一个页面
    3. 重建对象
    4. 重新聚合您的数组。

    更新将是:

    UPDATE mytable
    SET mycolumn = s.new_array
    FROM (
        -- <query above>
    ) s
    

    【讨论】:

      【解决方案2】:

      假设你想为第二本书添加一个新页面(Book Thief),那么使用JSONB_INSERT()函数和下面的更新语句就足够了

      UPDATE books
         SET pagesRead = JSONB_INSERT(pagesRead,'{1,pagesRead,1}','"3"'::JSONB,true)
      

      但是,为了使其成为动态解决方案,在不知道书在主数组中的位置的情况下,将新的页码添加到所需书的pagesRead数组的末尾,确定位置,以及子查询中相关数组的长度为

      WITH b AS
      (
       SELECT idx-1 AS pos1, 
              JSONB_ARRAY_LENGTH( (j ->> 'pagesRead')::JSONB )-1 AS pos2
         FROM books 
        CROSS JOIN JSONB_ARRAY_ELEMENTS(pagesRead) 
         WITH ORDINALITY arr(j,idx)
        WHERE j ->> 'book' = 'Book Thief'
      )
      UPDATE books
         SET pagesRead = 
              JSONB_INSERT(
                           pagesRead,
                           ('{'||pos1||',pagesRead,'||pos2||'}')::TEXT[], 
                          --# pos1 stands for the position within the main array
                          --# pos2 stands for the position within the related pagesRead array
                           '"3"'::JSONB, --# an arbitrary page number
                           true --# the new page value will be inserted after the target path
                           ) 
        FROM b 
      

      Demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-29
        • 2018-11-15
        • 2020-12-09
        • 1970-01-01
        相关资源
        最近更新 更多