【问题标题】:How do I return nested array from a psql database in json format如何以 json 格式从 psql 数据库返回嵌套数组
【发布时间】:2016-12-12 01:33:28
【问题描述】:

我将从顶部开始...我有两个表,引号和 cmets。一句话“有很多” cmets。我的最终目标是在前端有两个 .map() 函数,一个将所有引号呈现到我的页面,另一个呈现位于其上方的引号的每个评论。

我正在尝试从 psql 中检索一个返回如下内容的 json:

[
{
"id": 1,
"content": "quote number 1!",
"name": null,
"num_of_flags": null,
"comments": [
  {
    "id": 1,
    "comment_content": "comment one!",
    "num_of_likes": null,
    "quote_id": 1
  },
  {
    "id": 1,
    "comment_content": "comment 2!",
    "num_of_likes": null,
    "quote_id": 1
  }
]

这样,'quotes' 是一个对象数组,属于它的 cmets 也是。

这是我真正得到的结果:

[
{
"id": 1,
"content": "quote number 1!",
"name": null,
"num_of_flags": null,
"comments": [
  {
    "id": 1,
    "comment_content": "comment 1!",
    "num_of_likes": null,
    "quote_id": 1
  }
]
},
{
"id": 2,
"content": "quote number 2!",
"name": null,
"num_of_flags": null,
"comments": [
  {
    "id": 2,
    "comment_content": "comment 2!",
    "num_of_likes": null,
    "quote_id": 2
  }
]
},
{
"id": 3,
"content": "quote number 3!",
"name": null,
"num_of_flags": null,
"comments": [
  {
    "id": 3,
    "comment_content": "comment 3",
    "num_of_likes": null,
    "quote_id": 1
  }
]
}
]

注意第三个'quote'对象。它包含“评论 3”。评论 3 的 quote_id 为 1,而 quote_id 是一个外键,应该只与引用编号 1 相关,因此它不应该出现在引用对象 3 中。此外,我的数据库实际上包含所有这些的多个 cmets引号,但我的 json 只返回一条评论,它甚至与引号没有正确的关系。

这是我的架构:

BEGIN;

DROP TABLE IF EXISTS quotes;
DROP TABLE IF EXISTS comments;

CREATE TABLE quotes (
 id SERIAL PRIMARY KEY,
 name VARCHAR,
 content VARCHAR,
 num_of_flags INTEGER
);

CREATE TABLE comments (
 id SERIAL PRIMARY KEY,
 comment_content VARCHAR,
 num_of_likes INTEGER,
 quote_id INTEGER
);

ALTER TABLE ONLY comments
 ADD CONSTRAINT quotes_id_fkey
 FOREIGN KEY (quote_id)
 REFERENCES quotes(id);

COMMIT;

我查看了这个blog 并了解了一些关于 json_agg 聚合函数的知识,并且能够拼凑一个 psql 调用,它确实将我的“cmets”变成了一个数组(正如你在我的第二个代码块中看到的那样这个帖子)。这是 psql 调用:

SELECT
      q.id,
      q.content,
      q.name,
      q.num_of_flags,
      json_agg(c.*) as comments
      FROM quotes q
      INNER JOIN comments c USING (id)
      GROUP BY q.id, q.content, q.name, q.num_of_flags

我认为关键是调整 psql 调用,但我似乎无法做出正确的调整。我还尝试应用横向连接,以及我在this Stack Overflow 帖子中找到的 array_to_json 方法,但无法将它们应用到我的项目中,可能是因为我的语法。

希望这篇文章清晰易懂!如果您碰巧遇到并可以帮助我,我真的很感激您的时间。

-比尔

【问题讨论】:

    标签: json postgresql multidimensional-array


    【解决方案1】:

    cmets 以错误的引号结尾的问题是因为您在 USING (id) 上链接了表格;相反,您应该使用ON quote.id = comment.quote_id。这也可能导致每个引用只显示一个评论,即使我可能有多个或没有。

    将一行转换为 JSON 对象的最佳方法是使用 json_build_object() (PG9.4+)。 json_agg() 将多个 JSON 对象组装成一个 JSON 数组。放在一起,你会得到这个:

    SELECT json_agg(quote_comments) AS json_object
    FROM (
        SELECT json_build_object('id', q.id,
                                 'name', q.name,
                                 'content', q.content,
                                 'num_of_flags', q.num_of_flags,
                                 'comments', json_agg(c.j)) AS quote_comments
        FROM quotes q
        LEFT JOIN (
            SELECT quote_id, json_build_object('id', id,
                                               'comment_content', comment_content,
                                               'num_of_likes', num_of_likes,
                                               'quote_id', quote_id) AS j
            FROM comments) c ON c.quote_id = q.id
        GROUP BY 1, 2, 3, 4) sub;
    

    JSON 对其对象使用层次结构,您的查询应该使用相同的层次结构;您可以使用子查询来做到这一点。在每个级别都构建一个 JSON 对象并根据需要聚合到 JSON 数组中。由于您有两个级别的聚合,因此您需要一个额外级别的子查询,因为您无法在单个(子)查询中对不同的集合进行分组。

    【讨论】:

    • 谢谢帕特里克!这似乎正是我正在寻找的。但是仍然出现错误:错误:列 c.quote_id 不存在第 14 行:来自 cmets) c ON c.quote_id = q.id 有什么想法吗?我已经运行了架构,所以 quote_id 肯定应该存在于 cmets 表中。
    • 精彩回答
    【解决方案2】:

    Postgres 9.3 版对 JSON 数据类型有很好的支持。 你可以试试这个:

    select q.*,  row_to_json(c.*) as comments from quotes q inner join comments c using(quote_id);
    

    已编辑改为使用左连接: select q., row_to_json(c.) as cmets from quotes q left join cmets c ON q.id = c.quote_id;

    【讨论】:

    • 感谢您的回复。我试了一下,得到以下错误:'USING 子句中指定的列“quote_id”在左表中不存在“。我试图将'quote_id'更改为'id'。这不会给我一个错误,但只是返回与我之前相同的东西......一个没有将 cmets 链接到正确引号的表/json。令人费解!再次感谢您的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-24
    • 2011-06-11
    • 2020-03-30
    • 2016-11-22
    • 2014-09-04
    • 1970-01-01
    • 2017-02-24
    相关资源
    最近更新 更多