【问题标题】:Mongoose - finding subdocuments by criteriaMongoose - 按标准查找子文档
【发布时间】:2013-05-26 13:28:02
【问题描述】:

我刚刚被这个问题困住了。我有两个 Mongoose 模式:

var childrenSchema = mongoose.Schema({
    name: {
        type: String
    },
    age: {
        type: Number,
        min: 0
    }
});

var parentSchema = mongoose.Schema({
    name : {
        type: String
    },
    children: [childrenSchema]
});

问题是,如何从每个父文档中获取所有子文档(在本例中为childrenSchema 对象)?假设我有一些数据:

var parents = [
    { name: "John Smith",
    children: [
        { name: "Peter", age: 2 }, { name: "Margaret", age: 20 }
    ]},
    { name: "Another Smith",
    children: [
        { name: "Martha", age: 10 }, { name: "John", age: 22 }
    ]}
];

我想在一个查询中检索所有 18 岁以上的儿童。可以吗?将不胜感激每一个答案,谢谢!

【问题讨论】:

  • 您是否希望它仅在孩子超过 18 岁时返回父级,或者您是否希望它仅填充每个父级中超过 18 岁的孩子?
  • 如果我能得到“孩子”的集合就太好了……

标签: node.js mongodb mongoose


【解决方案1】:

您可以在最新的 MongoDB 版本中使用 $elemMatch 作为查询投影运算符。从 mongo shell:

db.parents.find(
    {'children.age': {$gte: 18}},
    {children:{$elemMatch:{age: {$gte: 18}}}})

这会将年幼孩子的文档从children 数组中过滤出来:

{ "_id" : ..., "children" : [ { "name" : "Margaret", "age" : 20 } ] }
{ "_id" : ..., "children" : [ { "name" : "John", "age" : 22 } ] }

如您所见,子文档仍分组在其父文档中。 MongoDB 查询从集合中返回文档。您可以使用聚合框架的$unwind 方法将它们拆分为单独的文档:

> db.parents.aggregate({
    $match: {'children.age': {$gte: 18}}
}, {
    $unwind: '$children'
}, {
    $match: {'children.age': {$gte: 18}}
}, {
    $project: {
        name: '$children.name',
        age:'$children.age'
    }
})
{
    "result" : [
        {
            "_id" : ObjectId("51a7bf04dacca8ba98434eb5"),
            "name" : "Margaret",
            "age" : 20
        },
        {
            "_id" : ObjectId("51a7bf04dacca8ba98434eb6"),
            "name" : "John",
            "age" : 22
        }
    ],
    "ok" : 1
}

我重复$match 子句的性能:第一次通过它消除了没有孩子至少18 岁的父母,所以$unwind 只考虑有用的文件。第二个$match 删除不匹配的$unwind 输出,$project 将子文档中的儿童信息提升到顶层。

【讨论】:

  • 有效! :) 感谢帮助! :) 我在哪里可以获得一些关于高级 MongoDB 操作的文档?它的文档就足够了吗?我希望尽可能多地了解 MongoDB :)
  • 参加 10gen 的在线 MongoDB For Developers 课程,阅读 Rick Copeland 的 MongoDB Design Patterns 一书。
  • 我知道,我来晚了,但是如果父母有多个 18 岁以上的孩子怎么办?我在 mongo shell 上尝试了第一个查询,但它只为每个父项检索一个匹配项。我说的对吗?
  • 谢谢!@A.JesseJiryuDavis 我还有一个问题,我看到结果只包含父对象的“_id”,如果我想保留所有父对象的数据并且只包含孩子我该怎么办18 岁以上,例如:{ "_id" : ..., "name": "John Smith", "children" : [ { "name" : "Margaret", "age" : 20 } ] }
  • 对于那些最终来到这里并像我一样对这个答案感到沮丧的人......只需使用 find 即可完成。这是一个示例:mongoplayground.net/p/qfAskjZcNGz 我是从这个问题中得到的,尽管它特别提到了嵌套。 stackoverflow.com/questions/36229123/…
【解决方案2】:

在 Mongoose 中,您还可以像这样使用优雅的 .populate() 函数:

parents
.find({})
.populate({
  path: 'children',
  match: { age: { $gte: 18 }},
  select: 'name age -_id'
})
.exec()

【讨论】:

  • populate 在引用其他集合中的文档时起作用。 OP 正在使用子文档。
  • 我已经很久没有做出这个答案了 :) & 我现在不能尝试,但我认为它也应该适用于嵌入式文档!
  • 如果不匹配返回null
  • @AbdelHady 它不适用于嵌入文档,populate 仅适用于引用文档
【解决方案3】:

A. Jesse Jiryu Davis 的响应就像一个魅力,但是对于更高版本的 Mongoose (Mongoose 5.x),我们得到了错误:

Mongoose 5.x 不允许将一系列运算符传递给Model.aggregate()。而不是Model.aggregate({ $match }, { $skip }),而是Model.aggregate([{ $match }, { $skip }])

所以现在的代码就是:

> db.parents.aggregate([{
    $match: {'children.age': {$gte: 18}}
}, {
    $unwind: '$children'
}, {
    $match: {'children.age': {$gte: 18}}
}, {
    $project: {
        name: '$children.name',
        age:'$children.age'
    }
}])
{
    "result" : [
        {
            "_id" : ObjectId("51a7bf04dacca8ba98434eb5"),
            "name" : "Margaret",
            "age" : 20
        },
        {
            "_id" : ObjectId("51a7bf04dacca8ba98434eb6"),
            "name" : "John",
            "age" : 22
        }
    ],
    "ok" : 1
}

(注意查询周围的数组括号)

希望这对某人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    • 2014-11-27
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2015-04-27
    相关资源
    最近更新 更多