字段名称中的 json_object_agg 错误为 null

Tim*_*Tim 7 postgresql aggregate json postgresql-10 postgresql-11

PostgreSQL 版本:我的本地安装是 11.3,下面的小提琴是 10.0。两者的行为相同。

我有一个页面架构,每个页面都有部分,每个部分可以有不同类型的内容。当我查询某个页面时,我希望在 JSON 文档中输出有关该页面的所有信息。

我将 CTE 用于json_agg()每个部分的各种内容。最后,我将各个部分连接起来json_object_agg(),将部分标题映射到部分内容。

问题: json_object_agg()当页面没有任何部分时抛出错误。我已经通过使用没有章节标题的常规来验证有罪json_agg()。确切的错误:

error: field name must not be null
Run Code Online (Sandbox Code Playgroud)

我想要的:无论如何都不是错误。我不想在接收方进行自定义错误处理。如果查询可以返回 JSONNull来代替json_object_agg()没有部分的情况,那就更好了,但这是可选的。(欢迎其他优雅的解决方案)

文档

可能文档不完整或者我遗漏了一些东西。仅供参考。

聚合表达式上它说(强调我的):

大多数聚合函数都会忽略 null 输入,因此一个或多个表达式产生null 的行将被丢弃。除非另有说明,对于所有内置聚合,这可以 假定为 true 。

聚合函数中,json_object_agg()没有评论不处理null

将名称/值对聚合为 JSON 对象

摆弄错误的域参数。将域更改为其他选项可以使其正常工作。使用不存在的域也可以正常工作并返回 0 行。

询问

with secs as (
    select p.page_id, p.domain, s.section_id as sid, s.title as title
    from pages p
    left join sections s on p.page_id = s.page_id
    where p.domain = 'bar.com'
),
txt as (
    select
        sid,
        json_agg(
            json_build_object(
                'Pos', pos,
                'Text', content
            )
            order by pos asc
        ) as txts
    from texts
    join secs on sid = section_id
    group by sid
),
img as (
    select
        sid,
        json_agg(
            json_build_object(
                'Pos', pos,
                'Image', image
            )
            order by pos asc
        ) as imgs
    from images
    join secs on sid = section_id
    group by sid
)
select
    json_build_object(
        'ID', s.page_id,
        'Domain', domain,
        'Sections', json_object_agg ( -- Error occurs here
            s.title,
            json_build_object(
                'ID', s.sid,
                'Texts', t.txts,
                'Images', i.imgs
            )
            order by s.sid asc
        )
    )
from secs s
left join txt t on s.sid = t.sid
left join img i on s.sid = i.sid
group by s.page_id, domain;
Run Code Online (Sandbox Code Playgroud)

模式

create table pages (
    page_id serial primary key,
    domain text unique not null
);

create table sections (
    section_id serial primary key,
    title text not null,
    page_id int references pages
);

create table texts (
    section_id int references sections,
    pos int not null,
    content text not null,
    primary key (section_id, pos)
);

create table images (
    section_id int references sections,
    pos int not null,
    image text not null,
    primary key (section_id, pos)
);

-- spanac.com will have 3 sections with texts and images in each, various amounts
insert into pages (domain) values ('spanac.com');
-- foo.com has 1 empty section
insert into pages (domain) values ('foo.com');
-- bar.com has no sections
insert into pages (domain) values ('bar.com');

-- spanac.com

with s as (
    insert into sections (page_id, title) select page_id, 'first' from pages where domain = 'spanac.com' returning section_id
),
t1 as (
    insert into texts (section_id, pos, content) select section_id, 1, 'spanac one.one' from s
),
t2 as (
    insert into texts (section_id, pos, content) select section_id, 2, 'spanac one.two' from s
),
i1 as (
    insert into images (section_id, pos, image) select section_id, 1, 's11.jpg' from s
)
insert into images (section_id, pos, image) select section_id, 2, 's12.jpg' from s;

with s as (
    insert into sections (page_id, title) select page_id, 'second' from pages where domain = 'spanac.com' returning section_id
),
t1 as (
    insert into texts (section_id, pos, content) select section_id, 1, 'spanac two.one' from s
),
t2 as (
    insert into texts (section_id, pos, content) select section_id, 2, 'spanac two.two' from s
),
i1 as (
    insert into images (section_id, pos, image) select section_id, 1, 's21.jpg' from s
)
insert into images (section_id, pos, image) select section_id, 2, 's22.jpg' from s;

with s as (
    insert into sections (page_id, title) select page_id, 'third' from pages where domain = 'spanac.com' returning section_id
),
t1 as (
    insert into texts (section_id, pos, content) select section_id, 1, 'Spanac three.one' from s
)
insert into images (section_id, pos, image) select section_id, 1, 's31.jpg' from s;

-- foo.com

insert into sections (page_id, title) select page_id, 'empty' from pages where domain = 'foo.com';
Run Code Online (Sandbox Code Playgroud)

Jas*_*sen 6

听起来你发现了一个错误。

你可以在他们的主页底部报告它,并在 pgsql-bugs邮件列表上监视该错误的讨论,我不期望他们改变 postgresql 处理这些数据的方式,他们可能只会更正文档。

一个可能的解决方法是添加WHERE s.title IS NOT NULL到聚合查询中

select
    json_build_object(
        'ID', s.page_id,
        'Domain', domain,
        'Sections', json_object_agg (
            s.title,
            json_build_object(
                'ID', s.sid,
                'Texts', t.txts,
                'Images', i.imgs
            )
            order by s.sid asc
        )
    )
from secs s
left join txt t on s.sid = t.sid
left join img i on s.sid = i.sid
WHERE s.title IS NOT NULL -- prevent null title from being agregated.
group by s.page_id, domain;
Run Code Online (Sandbox Code Playgroud)