用于获取集合中的子项和子项的Mongoose查询

时间:2017-03-16 11:10:39

标签: javascript node.js mongodb mongoose

我有什么:

我在mongo中有一个有很多文档的集合。该集合称为Users。出于示例目的,我已经显示了7条记录。

{
    _id: _______________________,
    name: Mary    
},
{
    _id: _______________________,
    name: John,
    manager: objectId("id of Mary");
},
{
    _id: _______________________,
    name: Tim,
    manager: objectId("id of John");
},
{
    _id: _______________________,
    name: Tom,
    manager: objectId("id of John");
},
{
    _id: _______________________,
    name: Henry,
    manager: objectId("id of Tim");
},
{
    _id: _______________________,
    name: Mark,
    manager: objectId("id of Henry");
},
{
    _id: _______________________,
    name: Todd,
    manager: objectId("id of Mary");
}

通过查看上述数据,您可以看到如下关系:

                                 Mary
                                   |
                  -------------------------------------
                  |                                   |
                John                                Todd
                  |
      --------------------------
      |                         |
     Tim                       Tom
      |
    Henry
      |
    Mark

我想要的是什么:

我想要一个mongoose查询,它会返回包含其子项和子项的所有记录。所以,例如,如果我想让John下的所有用户都包括John,那么我的输出应该是这样的:

{
    _id: _______________________,
    name: John,
    manager: objectId("id of Mary");
},
{
    _id: _______________________,
    name: Tim,
    manager: objectId("id of John");
},
{
    _id: _______________________,
    name: Tom,
    manager: objectId("id of John");
},
{
    _id: _______________________,
    name: Henry,
    manager: objectId("id of Tim");
},
{
    _id: _______________________,
    name: Mark,
    manager: objectId("id of Henry");
}

我不想要的内容:

我知道数据是关系型的,所以有些人可能会建议我应该使用关系数据库。但是现在我正在尝试学习mongodb和Node.js.所以,我想坚持使用mongodb。

我也知道可以拥有一个包含所有数据的集合:

var ManagerSchema = new Schema({
   manager_name: String,
   users: [users]
}

var UserSchema = new Schema({
   user_name: String
})

但我不想要上面提到的东西。

我只想拥有一个集合,数据将是关系型的。

1 个答案:

答案 0 :(得分:3)

您需要分3个阶段完成:

  1. 首先通过查询_id
  2. 来获取John
  3. 通过搜索以John的身份作为经理的用户
  4. 来获取John的“孩子”
  5. 通过查询所有任何 John的孩子作为他们的经理的用户来获取John的'孙子'。 MongoDB $in运算符在这里很有用
  6. 代码:

    const idOfjohn = '1234567890abcdef';
    let john, children, grandchildren; // initialise variables
    
    Users.findById(idOfjohn) // find John
        .then(user => {
            john = user; // assign to variable with wider scope
    
            return Users.find({ manager: idOfjohn }); // find users whose manager is John
        })
        .then(users => {
            children = users;
            const childrenIDs = users.map(user => user._id); // make array of IDs
    
            return Users.find({ manager: { $in: childrenIDs } }); // find users whose managers have John as a manager
        })
        .then(users => {
            grandchildren = users;
    
            const all = [john]
                .concat(children)
                .concat(grandchildren); // create a single array
    
            console.log(all); // or do whatever
        });
    

    修改

    OP澄清他需要n级后代。

    您可以使用co库的wrap函数执行此操作,如下所示:

    const { wrap } = require('co');
    
    
    const findWithDescendants = wrap(function*(topUserId, generations) {
        const topUser = yield Users.findById(topUserId); // get John himself
    
        let users = [topUser]; // will hold all users
        let ids = [topUserId]; // will hold IDs to search in
    
        for (let i = 0; i < generations; i++) {
            const thisLevelUsers = yield Users.find({ manager: { $in: ids } });
    
            ids = thisLevelUsers.map(user => user._id); // replace with new IDs
            users = users.concat(thisLevelUsers); // add new users
        }
    
        return users;
    });
    
    findWithDescendants(idOfjohn, 3)
        .then(users => {
            console.log(users); // or do whatever
        });
    

    注意:为了确保没有重复项,您可以使用Set而不是users的数组,您可以将每个用户添加到{{ 1}}逐个检查每一个users数组中是否存在它们。