【问题标题】:Get a parents + children tree with pg-promise使用 pg-promise 获取父母 + 孩子树
【发布时间】:2016-10-06 11:08:44
【问题描述】:

我使用pg-promise 库和bluebird 进行相关查询。 我有两张表,a 和 b,看起来像这样:

|   a   |     |   b   |  
|-------|     |-------|
| a_id  |     | b_id  |
| prop1 |     | prop2 |
              |  b_a  |

其中b.b_a 是对a.a_id 的引用。我想选择与给定prop1 匹配的所有条目,结果应包含所有匹配的a-rows 以及每个a 对应的b-rows。这应该可以通过两个相关查询来实现。两个查询都可能返回多个结果。

如果表 a 只返回一行,我可以这样做:

function getResult(prop1) {
    return db.task(function (t) {
        return t.one("select * from a where prop1=$1", prop1)
            .then(function (a) {
                return t.batch([a, t.any("select * from b where b_a=$1", a.a_id)]);
            })
            .then(function (data) {
                var a = data[0];
                var bs = data[1];
                bs.forEach(function (b) {
                    b.a = a;
                });
                return bs;
            });
    });
}

我还能够为多个 a-results 获取所有匹配的 b-entries,如下所示:

function getResult(prop1) {
    return db.task(function (t) {
        return t.many("select * from a where prop1=$1", prop1)
            .then(function (as) {
                var queries = [];
                as.forEach(function (a) {
                    queries.push(t.any("select * from b where b_a=$1", a.id));
                });
                return t.batch(queries); // could concat queries with as here, but there wouldn't be a reference which b row belongs to which a row
            })
            .then(function (data) {
                // data[n] contains all matching b rows
            });
    });
}

但是如何将这两者结合起来呢?

【问题讨论】:

  • 它们的顺序相同,对吧?所以只需访问as[n]data[n]。看看here 如何访问as
  • 感谢您的链接,这可能会有所帮助。但是ab 之间存在一对多的关系,所以这里的索引没有帮助..
  • batch的返回值是多少? data 不是某种二维数组吗?
  • 刚刚看到它是多维的,在我阅读您的评论之前一秒钟。非常感谢,本来可以早点看到的!
  • where a.a_b is a reference - 表 a 中没有 a_b

标签: javascript postgresql pg-promise


【解决方案1】:

我是pg-promise的作者。


当您有 2 个表时:Parent -> Child 具有一对多关系,并且您想要获取匹配的 Parent 行数组,每行扩展属性 children 设置为表 Child 中对应行的数组 ...

有几种方法可以实现这一点,因为pg-promise 和 promises 的组合通常非常灵活。这是最短的版本:

db.task(t => {
    return t.map('SELECT * FROM Parent WHERE prop1 = $1', [prop1], parent => {
        return t.any('SELECT * FROM Child WHERE parentId = $1', parent.id)
            .then(children => {
                parent.children = children;
                return parent;
            });
    }).then(a => t.batch(a))
})
    .then(data => {
        /* data = the complete tree */
    });

这就是我们在那里所做的:

首先,我们查询Parent 项,然后我们将每一行映射到对应Child 项的查询中,然后将其行设置为Parent 并返回它。然后我们使用方法batch来解析从方法map返回的Child查询数组。

ES7 更新

这里和上面一样,但是使用 ES7 async/await 语法:

await db.task(async t => {
    const parents = await t.any('SELECT * FROM Parent WHERE prop1 = $1', [prop1]);
    for(const p of parents) {
        p.children = await t.any('SELECT * FROM Child WHERE parentId = $1', [p.id]);
    }
    return parents;
});
// the task resolves with the correct data tree

任务将使用这样的数组解决:

[
    {
        "parent1-prop1", "parent1-prop2",
        "children": [
            {"child1-prop1", "child1-prop2"},
            {"child2-prop1", "child2-prop2"}
        ]
    },
    {
        "parent2-prop1", "parent2-prop2",
        "children": [
            {"child3-prop1", "child3-prop2"},
            {"child4-prop1", "child4-prop2"}
        ]
    }    
]

API 参考:mapbatch

更新

查看更好的答案:JOIN table as array of results with PostgreSQL/NodeJS

【讨论】:

  • 非常感谢,map 在这里很有帮助 - 我自己没有找到。
  • @Krxldfx map 只是一种方便的方法,可以使您的代码更短,但不是执行任务的严格要求;)
  • @Krxldfx 更新了指向更好/更完整答案的链接;)
  • 添加了 ES7 示例。
猜你喜欢
  • 1970-01-01
  • 2015-08-30
  • 2016-09-21
  • 2021-12-02
  • 2013-06-04
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 1970-01-01
相关资源
最近更新 更多