web-dev-qa-db-fra.com

Mongoose nested query on Model by field of its referenced model

Il semble qu'il y ait beaucoup de questions/réponses sur ce sujet sur stackoverflow, mais je n'arrive pas à trouver de réponse exacte nulle part.

Ce que j'ai:

J'ai des modèles d'entreprise et de personne:

var mongoose = require('mongoose');
var PersonSchema = new mongoose.Schema{
                        name: String, 
                        lastname: String};

// company has a reference to Person
var CompanySchema = new mongoose.Schema{
                        name: String, 
                        founder: {type:Schema.ObjectId, ref:Person}};

Ce dont j'ai besoin:

Retrouver toutes les entreprises que le nom de famille "Robertson" a fondées

Ce que j'ai essayé:

Company.find({'founder.id': 'Robertson'}, function(err, companies){
    console.log(companies); // getting an empty array
});

Ensuite, j'ai pensé que Person n'était pas intégré mais référencé, j'ai donc utilisé populate pour remplir le fondateur-Person, puis j'ai essayé d'utiliser find avec le nom de famille 'Robertson'

// 1. retrieve all companies
// 2. populate their founders
// 3. find 'Robertson' lastname in populated Companies
Company.find({}).populate('founder')
       .find({'founder.lastname': 'Robertson'})
       .exec(function(err, companies) {
        console.log(companies); // getting an empty array again
    });

Je peux toujours interroger les entreprises avec l'ID de la personne sous forme de chaîne. Mais ce n'est pas exactement ce que je veux comme vous pouvez le comprendre

Company.find({'founder': '525cf76f919dc8010f00000d'}, function(err, companies){
    console.log(companies); // this works
});
22
AzaFromKaza

Vous ne pouvez pas le faire dans une seule requête car MongoDB ne prend pas en charge les jointures. Au lieu de cela, vous devez le diviser en quelques étapes:

// Get the _ids of people with the last name of Robertson.
Person.find({lastname: 'Robertson'}, {_id: 1}, function(err, docs) {

    // Map the docs into an array of just the _ids
    var ids = docs.map(function(doc) { return doc._id; });

    // Get the companies whose founders are in that set.
    Company.find({founder: {$in: ids}}, function(err, docs) {
        // docs contains your answer
    });
});
39
JohnnyHK

Je suis assez en retard pour celle-ci: p Mais je cherchais juste une réponse similaire et je pensais partager ce que j'aurais trouvé au cas où quelqu'un trouverait cela pour la même raison.

Je ne pouvais pas trouver un moyen d'y parvenir grâce à des requêtes de mangouste, mais je pense que cela fonctionne en utilisant le pipeline d'agrégation MongoDB

Pour obtenir la requête que vous recherchez, vous pouvez faire quelque chose comme ceci:

const result=await Company.aggregate([
    {$lookup: {
        from: 'persons', 
        localField: 'founder', 
        foreignField: '_id', 
        as: 'founder'}
    },
    {$unwind: {path: '$founder'}},
    {$match: {'founder.lastname', 'Robertson'}}
]);

$lookup agit comme .populate(), en remplaçant la référence par les données réelles. Il retourne un tableau car il peut être utilisé pour faire correspondre plusieurs documents.

$unwind supprime les éléments d'un tableau et, dans ce cas, transformera simplement le tableau à un élément en champ.

$match fait alors ce que cela ressemble et ne renvoie que les documents correspondant à la requête. Vous pouvez également faire une correspondance plus complexe que l'égalité stricte si vous en avez besoin.

En général, le pipeline d'agrégation fonctionne en filtrant/modifiant continuellement les documents correspondants à chaque étape du processus jusqu'à ce que vous ayez exactement ce que vous voulez.

Je n'ai pas vérifié les performances à ce sujet, mais je préfère certainement que Mongo fasse le travail plutôt que de filtrer les résultats inutiles côté serveur.

Je suppose que le seul inconvénient est que le résultat sera juste un tableau d'objets plutôt que des modèles de mangouste puisque le pipeline change généralement la forme des documents. Vous ne pourrez donc pas utiliser les méthodes du modèle sur les données renvoyées.

1
sbrass

Au cas où quelqu'un le rencontrerait plus récemment, Mongoose prend désormais en charge la fonctionnalité de jointure avec une fonctionnalité appelée Populate.

De la documentation de Mongoose:

Story.findOne({ 
    title: 'Casino Royale' 
}).populate('author').exec(function (err, story) {
    if (err) return handleError(err);
    console.log('The author is %s', story.author.name);
    // prints "The author is Ian Fleming"
});

http://mongoosejs.com/docs/populate.html

1
evanmcd