web-dev-qa-db-fra.com

Mongoose - recherche de sous-documents par critères

Je suis juste coincé avec ce problème. J'ai deux schémas Mongoose:

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

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

La question est, comment récupérer tous les sous-documents (dans ce cas, childrenSchema objets) à partir de chaque document parent? Supposons que j'ai des données:

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 }
    ]}
];

Je voudrais récupérer - en une seule requête - tous les enfants de plus de 18 ans. Est-ce possible? Chaque réponse sera appréciée, merci!

30
Kuba T

Vous pouvez utiliser $elemMatch comme opérateur de projection de requête dans les versions les plus récentes de MongoDB. Du mongo Shell:

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

Cela filtre les documents des jeunes enfants du tableau children:

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

Comme vous pouvez le voir, les enfants sont toujours regroupés dans leurs documents parents. Les requêtes MongoDB renvoient des documents provenant de collections. Vous pouvez utiliser le cadre d'agrégation $unwind méthode pour les diviser en documents séparés:

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

Je répète le $match clause de performance: la première fois, elle élimine les parents avec non enfants d'au moins 18 ans, donc le $unwind ne considère que les documents utiles. La deuxième $match supprime $unwind sortie qui ne correspond pas, et le $project hisse les informations des enfants des sous-documents au niveau supérieur.

43

Dans Mongoose, vous pouvez également utiliser la fonction élégante .populate() comme ceci:

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

A. La réponse de Jesse Jiryu Davis fonctionne comme un charme, mais pour les versions ultérieures de Mongoose (Mongoose 5.x), nous obtenons l'erreur:

Mongoose 5.x interdit de passer une série d'opérateurs à Model.aggregate(). Au lieu de Model.aggregate({ $match }, { $skip }), faites Model.aggregate([{ $match }, { $skip }])

Ainsi, le code serait simplement:

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

(notez les crochets du tableau autour des requêtes)

J'espère que cela aide quelqu'un!

0
cjwiseman11