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

时间:2018-05-17 12:37:46

标签: node.js postgresql express promise pg-promise

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

我有两张桌子:帖子和评论。

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));

所以我想将每条评论的结果添加到每个帖子并返回帖子。 我有这个,但不起作用:

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

有什么建议吗? PD:我认为我的问题与此类似: get JOIN table as array of results with PostgreSQL/NodeJS

解答:

选项1(最佳选择)

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

  

请参阅@ vitaly-t

的回答

OR

  

使用ajax异步获取嵌套数据。

选项2

function buildTree(t) {
        return t.map("select *, avatar from publicacion, usuarios where usuario = $1 and usuario = alias ORDER BY hora DESC LIMIT 10 OFFSET $2", [username, cantidad], posts => {
                return t.any('select * from comentario where idPublicacion = $1', 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 = $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, q])
    for (var post of posts){
        post.coments = await con.any('select * from comment where idPublictcion = $1', post.id);
    }
}catch(e){
    console.log(e);
}

3 个答案:

答案 0 :(得分:3)

我是pg-promise;)的作者

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

另请参阅此问题:get JOIN table as array of results with PostgreSQL/NodeJS

<强>更新

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

con.task(async t => {
    const posts = await t.any('SELECT *, avatar FROM post, users WHERE user = $1 AND user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [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:

答案 1 :(得分:1)

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

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

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

您可以签出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类的调用。它负责将统一结果记录构造为一个漂亮的嵌套结构。

(还要注意,它是“一个”,与我们的SQL心理模型相匹配。一个,一个或无,多个以及任何一个的DAO方法可确保将它们计入生成的顶级业务对象的数量,而不是sql表达式返回的行!)

查看repository,以了解如何在pg-promise上进行设置。 (Disclamer,我是sql-toolkit的作者。)

答案 2 :(得分:0)

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

return t.any('select *, avatar from post, users where user= $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [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;
    });

其实我建议您使用orm工具,例如书架 knex typeorm

相关问题