将嵌套循环查询组合到父数组结果 - pg-promise

Combine nested loop queries to parent array result - pg-promise

我是 node(express) 和 pg-promise 的新手,一直无法弄清楚如何将每个嵌套查询(循环)的结果添加到主 json 数组结果查询中.

我有两个 table:帖子和评论。

CREATE TABLE post(
id serial,
content text not null,
linkExterno text,
usuario VARCHAR(50) NOT NULL REFERENCES usuarios(alias) ON UPDATE cascade ON DELETE cascade,
multimedia text,
ubicacation VARCHAR(100),
likes integer default 0,
time VARCHAR default now(),
reported boolean default false,
PRIMARY KEY (id)  );

CREATE TABLE comment(
id serial,
idPost integer NOT NULL REFERENCES post(id) ON UPDATE cascade ON DELETE cascade,
acount VARCHAR(50) NOT NULL REFERENCES users(alias) ON UPDATE cascade ON DELETE cascade,
content text NOT NULL,
date date default now(),
PRIMARY KEY (id));

所以我想将每个评论的结果添加到 post 和 return 中的每个 post。 我有这个,但不起作用:

con.task(t => {
    return t.any('select *, avatar from post, users where user=  and user = alias ORDER BY time DESC LIMIT 10 OFFSET ', [username, pos])
    .then(posts => {
        if(posts.length > 0){
            for (var post of posts){
                post.coments = t.any('select * from comment where idPost =  ', post.id);
            }
        }
    });
}).then(posts => {
    res.send(posts);
}).catch(error => {
    console.log(error);
});

有什么建议吗? PD:我想我的问题有点类似于这个: 获取 JOIN table 作为结果数组 PostgreSQL/NodeJS

答案:

选项 1(最佳选择)

通过 JSON 对 psql 进行单个查询(JSON 查询

See answer by @vitaly-t

Getting the nested data asynchronously using ajax.

选项 2:

function buildTree(t) {
        return t.map("select *, avatar from publicacion, usuarios where usuario =  and usuario = alias ORDER BY hora DESC LIMIT 10 OFFSET ", [username, cantidad], posts => {
                return t.any('select * from comentario where idPublicacion = ', posts.id)
                    .then(coments => {
                        posts.coments = coments;
                        console.log(posts.coments);
                        return posts;
                    });
        }).then(t.batch); // settles the array of generated promises
    }

    router.get('/publicaciones', function (req, res) {
        cantidad = req.query.cantidad || 0; //num de publicaciones que hay
        username = req.session.user.alias;

        con.task(buildTree)
        .then(data => {
            res.send(data);
        })
        .catch(error => {
            console.log(error);
        });
    });

选项 3(异步)

try{
    var posts = await con.any('select *, avatar from post, users where user =  and user = alias ORDER BY time DESC LIMIT 10 OFFSET ', [username, q])
    for (var post of posts){
        post.coments = await con.any('select * from comment where idPublictcion = ', post.id);
    }
}catch(e){
    console.log(e);
}

您可以使用 await,但它会同步工作。

return t.any('select *, avatar from post, users where user=  and user = alias ORDER BY time DESC LIMIT 10 OFFSET ', [username, pos])
    .then(posts => {
        if(posts.length > 0){
            for (var post of posts){
                post.coments = await t.any('select * from comment where idPost = ', post.id);
            }
        }
        return posts;
    });

实际上,我建议您使用诸如 bookshelfknextypeorm[ 之类的 orm 工具=12=]

我是 pg-promise 的作者 ;)


con.task(t => {
    const a = post => t.any('SELECT * FROM comment WHERE idPost = ', post.id)
        .then(comments => {
            post.comments = comments;
            return post;
        });
    return t.map('SELECT *, avatar FROM post, users WHERE user =  AND user = alias ORDER BY time DESC LIMIT 10 OFFSET ', [username, pos], a)
        .then(t.batch);
})
    .then(posts => {
        res.send(posts);
    })
    .catch(error => {
        console.log(error);
    });

另请参阅此问题:

更新

如果您不想一直使用 JSON 查询方法,那么下面的方法将比原始解决方案更好地扩展,因为我们连接所有子查询,然后执行它们一个查询:

con.task(async t => {
    const posts = await t.any('SELECT *, avatar FROM post, users WHERE user =  AND user = alias ORDER BY time DESC LIMIT 10 OFFSET ', [username, pos]);
    const a = post => ({query: 'SELECT * FROM comment WHERE idPost = ${id}', values: post});
    const queries = pgp.helpers.concat(posts.map(a));
    await t.multi(queries)
        .then(comments => {
            posts.forEach((p, index) => {
                p.comments = comments[index];
            });
        });
    return posts;
})
    .then(posts => {
        res.send(posts);
    })
    .catch(error => {
        console.log(error);
    });

见API:

如果您想要结构化(嵌套)数据,而不必

A) 使用 json 函数重写您的 sql,或者将其拆分为多个任务查询,或者

B) 重构您的代码以使用重型 ORMAPI

您可以查看 sql-toolkit。它是为 pg-promise 构建的节点库,允许您编写常规原生 SQL 并接收回结构正确(嵌套)的纯业务对象。它严格来说是在 pg-promise 之上的增强工具包,并没有试图抽象出 pg-promise(你仍然设置 pg-promise 并可以直接使用它)。

例如:

class Article extends BaseDAO {
  getBySlug(slug) {
    const query = `
      SELECT
        ${Article.getSQLSelectClause()},
        ${Person.getSQLSelectClause()},
        ${ArticleTag.getSQLSelectClause()},
        ${Tag.getSQLSelectClause()}
      FROM article
      JOIN person
        ON article.author_id = person.id
      LEFT JOIN article_tags
        ON article.id = article_tags.article_id
      LEFT JOIN tag
        ON article_tags.tag_id = tag.id
      WHERE article.slug = $(slug);
  `;
  return this.one(query, { slug });
  // OUTPUT: Article {person: Person, tags: Tags[Tag, Tag, Tag]}
}

select 子句使用业务对象 "getSQLSelectClause" 方法来节省输入列的单调乏味,并确保没有名称冲突(没有什么神奇的事情发生,可以直接写出来相反)。

this.one 是对 sql-toolkit 基础 DAO class 的调用。它负责将平面结果记录构建成一个漂亮的嵌套结构。

(另请注意,"one" 与我们对 SQL 的心智模型相匹配。用于 one、oneOrNone、many 和 any 的 DAO 方法确保它们对生成的顶部数量的计数级别业务对象 - 不是 sql 表达式 returns!)

的行数

查看 repository 以了解有关如何在 pg-promise 之上进行设置的详细信息。 (免责声明,我是 sql-toolkit 的作者。)