更新对象数组中的特定对象 Postgres jsonb
Update specific object in array of objects Postgres jsonb
我正在尝试更新 table Books
上的 jsonb 列 pagesRead
,其中包含一个对象数组。它的结构类似于:
[{
"book": "Moby Dick",
"pagesRead": [
"1",
"2",
"3",
"4"
]
},
{
"book": "Book Thief",
"pagesRead": [
"1",
"2"
]
}]
我想做的是在阅读本书的特定页面时更新 pagesRead,或者如果有人开始阅读新书,则在其中添加一个额外的条目。
我可以检索到具体的图书详细信息,但我不确定如何更新它。
编辑:所以我不得不使用 S-Man 的更新查询来添加图书条目,但我使用 Barbaros Özhan 的插入查询来处理更新页面
之前的一些想法:
- 切勿将结构化数据按原样存储在一列中。这会产生更新、索引(因此,searching/performance)、过滤等所有问题。请将所有内容规范化为适当的表格和列
- 永远不要存储数组。规范化它。
- 不要使用文字类型来存储整数(页数)
"pagesRead"
是您的过滤器元素 ("book"
) 的兄弟元素。这使得引用它比引用它作为 child 复杂得多。因此,将书名(或更好:id)视为 {"my_id_for_book_thief": {"name" : "Book Thief", "pagesRead": [...]}}
之类的键。在这种情况下,您可以使用路径来引用它。否则,我们需要提取数组,查看每个 book
属性并引用其兄弟
添加 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
- 将数组提取为每个元素一条记录
- 如果元素包含正确的书籍,则添加一个页面
- 重建object
- 重新聚合数组。
更新为:
UPDATE mytable
SET mycolumn = s.new_array
FROM (
-- <query above>
) s
假设你想为第二本书(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
我正在尝试更新 table Books
上的 jsonb 列 pagesRead
,其中包含一个对象数组。它的结构类似于:
[{
"book": "Moby Dick",
"pagesRead": [
"1",
"2",
"3",
"4"
]
},
{
"book": "Book Thief",
"pagesRead": [
"1",
"2"
]
}]
我想做的是在阅读本书的特定页面时更新 pagesRead,或者如果有人开始阅读新书,则在其中添加一个额外的条目。
我可以检索到具体的图书详细信息,但我不确定如何更新它。
编辑:所以我不得不使用 S-Man 的更新查询来添加图书条目,但我使用 Barbaros Özhan 的插入查询来处理更新页面
之前的一些想法:
- 切勿将结构化数据按原样存储在一列中。这会产生更新、索引(因此,searching/performance)、过滤等所有问题。请将所有内容规范化为适当的表格和列
- 永远不要存储数组。规范化它。
- 不要使用文字类型来存储整数(页数)
"pagesRead"
是您的过滤器元素 ("book"
) 的兄弟元素。这使得引用它比引用它作为 child 复杂得多。因此,将书名(或更好:id)视为{"my_id_for_book_thief": {"name" : "Book Thief", "pagesRead": [...]}}
之类的键。在这种情况下,您可以使用路径来引用它。否则,我们需要提取数组,查看每个book
属性并引用其兄弟
添加 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
- 将数组提取为每个元素一条记录
- 如果元素包含正确的书籍,则添加一个页面
- 重建object
- 重新聚合数组。
更新为:
UPDATE mytable
SET mycolumn = s.new_array
FROM (
-- <query above>
) s
假设你想为第二本书(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