Mongoose - 按标准查找子文档

时间:2013-05-30 20:19:59

标签: node.js mongodb mongoose

我刚刚遇到这个问题。我有两个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岁以上的孩子。有可能吗?每个答案都将不胜感激,谢谢!

3 个答案:

答案 0 :(得分:42)

您可以在最新的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将子文档中的子项信息提升到顶层。

答案 1 :(得分:18)

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

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

答案 2 :(得分:0)

A。杰西·吉鲁·戴维斯(Jesse Jiryu Davis)的回应像个咒语,但是对于更高版本的猫鼬(Mongoose 5.x),我们会收到错误消息:

猫鼬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
}

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

希望这对某人有帮助!