【问题标题】:Use mongoose query to get only a subdocument使用猫鼬查询仅获取子文档
【发布时间】:2013-04-16 11:46:53
【问题描述】:

我有一个类似的数据结构:

var GrandGrandChild = mongoose.Schema({
    attribute: String,
    id: Number
});

var GrandChild = mongoose.Schema({
    children: [GrandGrandChild],
    id: Number,
    irrelevantAttribute: String
});

var Child = mongoose.Schema({
    children: [GrandChild],
    id: Number,
    irrelevantAttribute2: String
});

var Parent = mongoose.Schema({
    children: [Child],
    id: Number,
    irrelevantAttribute3: String
});

var GrandParent = mongoose.Schema({
    children: [Parent],
    id: Number,
    irrelevantAttribute4: String
});

这些是很多包含子文档的集合。 请注意,ID 对于它们的兄弟姐妹来说是唯一的,但并不是对于具有相同架构的所有元素都是唯一的。

所以一个祖父母可以有一个 id 为 0 的父母,另一个祖父母也可以有一个 id 为 0 的父母。但一个祖父母不能有两个 id 为 0 的父母。

唯一被保存的模式是 GrandParent 模式,mongoose/mongodb 为这个祖父母的所有数据制作了一个很好的大单文档。 (正是我要找的)

所以这是我的问题:我有一个 GrandParent ID、Parent ID、Child ID、GrandChildID 和 GrandGrandChild ID,我想以某种方式只获取所有这些 ID 指向的 GrandGrandChild 对象。

丑陋的方法是,但目前我可以开始工作的唯一方法是进行查询以获取 GrandParent 的这个大文档,并手动循环遍历所有数组以找到正确的父级,然后再次循环到找到正确的孩子,然后再次循环找到正确的孙子,然后再次循环并在这里找到我需要的孙子。

我的问题是,我将如何在猫鼬中编写一个查询,该查询要么只返回孙子文档,要么只返回包含子属性的祖父文档,并且在该子属性中,仅包含引用子对象的父对象引用孙对象引用孙对象,允许以下结果:

GRANDPARENT  PARENT      CHILD      GRANDCHILD   GRANDGRANDCHILD
grandparent.children[0].children[0].children[0].children[0].attribute;

我希望有人可以帮助我解决这个问题,据我所知是这样的:

GrandParentModel.findOne(
    {
        "id" : 0,
        "children.id" : 0,
        "children.children.id" : 0,
        "children.children.children.id" : 0,
        "children.children.children.children.id" : 0
    },
    {"children.children.children.children.$" : 1}, callback);

这个查询的问题是没有修剪掉不必要的兄弟姐妹。

希望有人能帮帮我。

海尔克·布朗

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    我问这个问题已经有一段时间了,但我想我找到了一种处理这种结构的相当优雅的方法。

    在这种情况下,我将展示它如何仅与 GrandParent、Parent 和 Child 一起使用。

    我没有在每个文档(GrandParent.children、Parent.children)中存储子文档列表,而是创建了以下结构的唯一标识符:

    Child.referenceId = {
        grandparent: "some key to the grandparent of the parent",
        parent: "some key to the parent",
        child: "key of this child"
    };
    
    Parent.referenceId = {
        grandparent: "some key to its grandparent",
        parent: "key of this parent"
    }
    
    GrandParent.referenceId = {
        grandparent: "key of this parent"
    }
    

    这将创建一个 GrandParent > Parent > Child 的层次结构。

    模型如下所示:

    var idStructure = {
        grandparent: { type: String, required: true },
        parent: { type: String, required: false },
        child: { type: String, required: false }
    };
    
    var GrandParent = mongoose.Schema({
        id: idStructure,
        irrelevantAttribute: String
    });
    
    var Parent = mongoose.Schema({
        id: idSructure,
        irrelevantAttribute: String
    });
    
    var Child = mongoose.Schema({
        id: idStructure,
        irrelevantAttribute: String
    });
    

    请注意,父级并不直接知道其父级,因为它们不存储为子文档。然而,Parent 和 Child 之间仍然通过 referenceId 建立联系。

    在搜索祖父母的整个家谱时,只需执行 3 次查询,然后正确连接它们:

    // First find all children which belong to the grandparent
    Child.find({"id.grandparent" : "some key to the grandparent"})
    .exec(function(err, children)
    {
         if(err)
             return;
    
         Parent.find({"id.grandparent" : "some key to the grandparent"})
         .exec(function(err, parents)
         {
             if(err)
                 return;
    
             // Loop through the parents and children to connect them before returning to a client
             for(var i = 0; i < parents.length; i++)
             {
                 var parent = parents[i];
                 parent.children = [];
                 // loop through the children to check if they belong to the current parent
                 for(var j = 0; j < children.length; j++)
                 {
                     var child = children[j];
                     if(parent.id.parent == child.id.parent)
                         parent.children.push(child);
                 }
             }
    
             // After filling the children into the parents, get the grandparents and do the same for the parents and grandparents as done for the children and parents.
            GrandParent.find({"id.grandparent" : "some key to the grandparent"})
           .exec(function(err, grandparents)
           {
               // TODO: the same as done above (two loops, one loops the grandparents, other loops the parents
               // Once this is finished, we have a filled grandparent
           });
    
         });
    });
    

    上面的代码将导致只有一个祖父母,其中充满了父母,而父母则充满了孩子。

    找不到更多祖父母的原因是因为祖父母的id应该是唯一的,因为祖父母的referenceId只有祖父母属性。

    我希望我的意思说清楚了,因为通过这种方法,人们可以很容易地搜索到一个特定的孩子,很容易通过参考 id 得到它的父母,也可以通过参考 id 得到它的祖父母。

    这可能有点复杂,但是一旦你自己想出了方法,一切都会很简单。

    海尔克

    【讨论】:

    【解决方案2】:

    很难让这种事情以干净的方式工作。

    我没有找到关于这个主题的干净解决方案,但也许我可以帮助你解决循环问题。 您可以使用以下方法避免循环: var doc = parent.children.id(id); Finding a sub-document

    希望对您有所帮助。 问候, 塞巴斯蒂安。

    【讨论】:

    • 但在这种情况下,我首先获取大文档,然后从该文档中选择我需要的所有字段。相反,我只得到一个小子文档。我认为使用小文档比使用大文档更快。
    【解决方案3】:

    这对我有用

          model.find({_id:args.id},{ commentList: { $elemMatch: { _id: todo.commentList[todo.commentList.length-1] } } },(err, todos) => {
             if (err) reject(err)
             else resolve(todos)
             console.log(todos);
          })
    

    $elemMatch (projection)

    【讨论】:

      猜你喜欢
      • 2015-06-26
      • 2023-04-07
      • 2017-11-24
      • 2017-06-01
      • 2019-10-19
      • 2014-03-21
      • 1970-01-01
      • 2013-10-21
      • 1970-01-01
      相关资源
      最近更新 更多