使用 pg-promise 获取父子树

Get a parents + children tree with pg-promise

我将 pg-promise 库与 bluebird 一起用于进行相关查询。 我有两个 tables,a 和 b,看起来像这样:

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

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

如果 table a 只有 return 一行,我可以这样做:

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

而且我还能够为多个 a-结果获得所有匹配的 b-条目,如下所示:

function getResult(prop1) {
    return db.task(function (t) {
        return t.many("select * from a where prop1=", prop1)
            .then(function (as) {
                var queries = [];
                as.forEach(function (a) {
                    queries.push(t.any("select * from b where b_a=", 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
            });
    });
}

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

我是 pg-promise 的作者。


当你有 2 个 table 时:Parent -> Child 具有一对多关系,并且你想获得匹配的数组 Parent行,每行扩展 属性 children 设置为来自 table Child ...

的相应行的数组

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

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

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

首先,我们查询 Parent 项,然后将每一行映射到对相应 Child 项的查询中,然后将其行设置到 Parent 和 returns它。然后我们使用方法 batch to resolve the array of Child queries returned from method map.

ES7 更新

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

await db.task(async t => {
    const parents = await t.any('SELECT * FROM Parent WHERE prop1 = ', [prop1]);
    for(const p of parents) {
        p.children = await t.any('SELECT * FROM Child WHERE parentId = ', [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 参考文献:map, batch

更新

看到一个更好的答案: