Mungo: tiefe Bevölkerung (bevölkere ein besiedeltes Feld)


74

Ich habe CategoryModell:

Category:
    ...
    articles: [{type:ObjectId, ref:'Article'}]

Artikelmodell enthält ref to Account model.

Article:
    ...
    account: {type:ObjectId, ref:'Account'}

Also, mit ausgefülltem articlesKategoriemodell wird sein:

{ //category
    articles: //this field is populated
     [ { account: 52386c14fbb3e9ef28000001, // I want this field to be populated
         date: Fri Sep 20 2013 00:00:00 GMT+0400 (MSK),
         title: 'Article 1' } ],
    title: 'Category 1' }

Die Frage ist: Wie wird ein Unterfeld (Konto) eines ausgefüllten Feldes ([Artikel]) ausgefüllt? So mache ich es jetzt:

globals.models.Category
    .find
        issue : req.params.id
        null
        sort:
            order: 1
    .populate("articles") # this populates only article field, article.account is not populated
    .exec (err, categories) ->
        console.log categories

Ich weiß, dass es hier diskutiert wurde: Mungo: Besiedle ein besiedeltes Feld, aber es wurde keine wirkliche Lösung gefunden


2
Wie Rroxysam sagte, .populate({path : 'userId', populate : {path : 'reviewId'}}).exec(...)scheint eine rekursive Logik zu sein, und das macht Sinn. es funktioniert!
Ewertom Moraes

Aktualisierungen von Mongoose seit dieser Frage haben dieses Problem behoben. Hier ist die Dokumentation: Auf mehreren Ebenen bevölkern
Chunky Chunk

Antworten:



136

Aktualisieren Sie zuerst Mungo 3 auf 4 und verwenden Sie dann den einfachsten Weg für eine tiefe Population in Mungo wie folgt:

Angenommen, Sie haben ein Blog-Schema mit einer Benutzer-ID als Referenz-ID und dann in Benutzer eine Überprüfung als Referenz-ID für die Schema-Überprüfung. Grundsätzlich haben Sie drei Schemata: 1. Blog 2. Benutzer 3. Überprüfung

Und Sie müssen vom Blog aus abfragen, welchem ​​Benutzer dieses Blog und die Benutzerbewertung gehören. Sie können Ihr Ergebnis also wie folgt abfragen:

BlogModel
  .find({})
  .populate({
    path : 'userId',
    populate : {
      path : 'reviewId'
    }
  })
  .exec(function (err, res) {

  })

8
Wenn ich nur Ihre Antwort gelesen und keine Stunde meiner Zeit mit den obigen Antworten verschwendet hätte!
Amin Jafari

5
Vielen Dank, dass Sie die Struktur der Abfrage angezeigt haben, anstatt nur eine Verknüpfung zu einem Dokument herzustellen. Funktioniert super!
Turner Houghton

8
Dies sollte die richtige Antwort sein. Danke @techyaura
jpincheira

Wie würden Sie die zweite Ebene füllen, wenn Sie zwei Felder füllen möchten? Es wird immer nur das letzte Feld für mich zurückgegeben, wenn ich select verwende: da ich nur bestimmte Felder im Dokument der zweiten Ebene
zurückholen möchte

Ihre Lösung funktioniert noch im Jahr 2020. Vielen Dank für das Teilen!
Niyongabo

35

Auf mehreren Ebenen bevölkern

Angenommen, Sie haben ein Benutzerschema, das die Freunde des Benutzers verfolgt.

var userSchema = new Schema({
  name: String,
  friends: [{ type: ObjectId, ref: 'User' }]
});

Mit "Auffüllen" können Sie eine Liste der Freunde eines Benutzers abrufen. Was ist jedoch, wenn Sie auch Freunde von Freunden eines Benutzers möchten? Geben Sie die Option zum Auffüllen an, um Mungo anzuweisen, das Freundes-Array aller Freunde des Benutzers zu füllen:

User.findOne({ name: 'Val' }).populate({
    path: 'friends',
    // Get friends of friends - populate the 'friends' array for every friend
    populate: { path: 'friends' }
});

Referenz: http://mongoosejs.com/docs/populate.html#deep-populate


20

Es mag etwas zu spät sein, aber ich habe ein Mungo-Plugin geschrieben , um eine tiefe Population auf beliebigen verschachtelten Ebenen durchzuführen. Wenn dieses Plugin registriert ist, können Sie die Artikel und Konten der Kategorie mit nur einer einzigen Zeile füllen:

Category.deepPopulate(categories, 'articles.account', cb)

Sie können auch festlegen , bevöl Optionen zu steuern , Dinge wie limit, select... für jeden besiedelten Pfad. Weitere Informationen finden Sie in der Plugin-Dokumentation.


9

Der einfachste Weg, dies in 3.6 zu erreichen, ist die Verwendung Model.populate.

User.findById(user.id).select('-salt -hashedPassword').populate('favorites.things').exec(function(err, user){
    if ( err ) return res.json(400, err);

    Thing.populate(user.favorites.things, {
        path: 'creator'
        , select: '-salt -hashedPassword'
    }, function(err, things){
        if ( err ) return res.json(400, err);

        user.favorites.things = things;

        res.send(user.favorites);
    });
});

1
Ich bin gespannt, wie Sie dasselbe tun würden, wenn user.favorites ein Array wäre.
Codephobie

Gleicher Weg. Werde
einfach

6

Oder Sie können Object wie folgt an die populate-Methode übergeben:

const myFilterObj = {};
const populateObj = {
                path: "parentFileds",
                populate: {
                    path: "childFileds",
                    select: "childFiledsToSelect"
                },
                select: "parentFiledsToSelect"
               };
Model.find(myFilterObj)
     .populate(populateObj).exec((err, data) => console.log(data) );

3

Es tut mir leid, dass Sie Ihre Blase platzen lassen, aber es gibt keine direkt unterstützte Lösung dafür. Die Github-Ausgabe Nr. 601 sieht düster aus. Laut den Versionshinweisen zu 3.6 scheinen die Entwickler anerkannt zu haben, dass das Problem mit der manuellen rekursiven / tiefen Population zufrieden ist.

Aus den Versionshinweisen geht hervor, dass die empfohlene Methode darin besteht, ausgefüllte Aufrufe im Rückruf zu verschachteln. exec()Verwenden Sie categories.populatesie daher in Ihrer Funktion, um sie vor dem Senden einer Antwort weiter auszufüllen.


3

Dieses Konzept ist tiefe Bevölkerung. Hier sind Kalender, Abonnement, Benutzer, Wohnung Mungo-ODM-Modelle in verschiedenen Ebenen

Calendar.find({}).populate({
      path: 'subscription_id',model: 'Subscription',
         populate: {path: 'user_id',model: 'User',
           populate: {path: 'apartment_id',model: 'Apartment',
              populate: {path: 'caterer_nonveg_id',
                          model: 'Caterer'}}}}).exec(function(err,data){ 
                          if(!err){
                             console.log('data all',data)
                           }
                           else{
                             console.log('err err err',err)
                            }
                   });

2
globals.models.Category.find()
  .where('issue', req.params.id)
  .sort('order')
  .populate('articles')
  .exec(function(err, categories) {

    globals.models.Account.populate(categories, 'articles.account', function(err, deepResults){

      // deepResult is populated with all three relations
      console.log(deepResults[0].articles[0].account);

    });
});

Das folgende Beispiel ist von der Frage @codephobia inspiriert und füllt zwei Ebenen mit vielen Beziehungen. Holen Sie zuerst a user, füllen Sie das Array der verwandten orders und schließen Sie jedes ein orderDetail.

user.model.findOne()
  .where('email', '***@****.com')
  .populate('orders')
  .exec(function(err, user) {

    orderDetail.model.populate(user, 'orders.orderDetails', function(err, results){

      // results -> user.orders[].orderDetails[] 
    });
});

Dies funktioniert gut in 3.8.8, sollte aber funktionieren 3.6.x.


0

Wenn Sie Multi Populate in Populate auswählen möchten , sollten Sie Folgendes versuchen:

Ich habe ein Buchungsschema :

let Booking = new Schema({
  ...,  // others field of collection
  experience: { type: Schema.Types.ObjectId, ref: 'Experience' },
  ...},{
    collection: 'booking'
  });

und Erfahrungsschema :

let Experience = new Schema({
  ...,
  experienceType: {type: Schema.Types.ObjectId, ref: 'ExperienceType'},
  location: {type: Schema.Types.ObjectId, ref: 'Location'},
  ...} // others field of collection
  ,{
    collection: 'experience'
  });

Erhalten Sie ExperienceType und Location of Experience, wenn Sie Buchung finden :

Booking.findOne({_id: req.params.id})
  .populate({path: 'experience',
    populate: [{path: 'experienceType', select: 'name'}, {path: 'location', select: 'name'}],
  })
  .exec((err, booking) => {
    if(err){
      console.log(err);
    }
    else {
      res.json(booking);
    }
  });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.