Mungo bevölkern nach dem Speichern

95

Ich kann das Erstellerfeld nicht manuell oder automatisch in ein neu gespeichertes Objekt einfügen. Die einzige Möglichkeit, die ich finden kann, besteht darin, die bereits vorhandenen Objekte erneut abzufragen, was ich gerne tun würde.

Dies ist das Setup:

var userSchema = new mongoose.Schema({   
  name: String,
});
var User = db.model('User', userSchema);

var bookSchema = new mongoose.Schema({
  _creator: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  description: String,
});
var Book = db.model('Book', bookSchema);

Hier ziehe ich an meinen Haaren

var user = new User();
user.save(function(err) {
    var book = new Book({
        _creator: user,
    });
    book.save(function(err){
        console.log(book._creator); // is just an object id
        book._creator = user; // still only attaches the object id due to Mongoose magic
        console.log(book._creator); // Again: is just an object id
        // I really want book._creator to be a user without having to go back to the db ... any suggestions?
    });
});

BEARBEITEN: Der neueste Mungo hat dieses Problem behoben und die Funktion zum Auffüllen hinzugefügt. Siehe die neu akzeptierte Antwort.

Pykler
quelle

Antworten:

135

Sie sollten in der Lage sein, die Populate-Funktion des Modells zu verwenden, um dies zu tun: http://mongoosejs.com/docs/api.html#model_Model.populate Im Save-Handler für Buch anstelle von:

book._creator = user;

Sie würden so etwas tun wie:

Book.populate(book, {path:"_creator"}, function(err, book) { ... });

Wahrscheinlich eine zu späte Antwort, um Ihnen zu helfen, aber ich habe mich kürzlich damit beschäftigt, und es könnte für andere nützlich sein.

user1417684
quelle
Es wäre schön, wenn dies mit virtuellen Attributen funktionieren würde. likecreator.profile
chovy
Wenn der Benutzer einige virtuelle Attribute hat, sind diese nicht enthalten.
Chovy
Dies funktionierte für mich, obwohl ich einige Probleme hatte, als ich versuchte, den Verweis kurz vor dem Speichern auf null zu setzen. Nach dem Speichern hat das Aufrufen von Book.populate das Feld mit dem vorherigen Referenzwert falsch gefüllt, und ich kann nicht herausfinden, warum. Die Datenbank enthält erfolgreich die Null.
Daniel Flippance
2
Und dies wird die Datenbank nicht erneut abfragen?!
Nepoxx
9
Dies ist eine erneute Abfrage der Datenbank
bubakazouba
36

Für den Fall, dass noch jemand danach sucht.

Mongoose 3.6 hat viele coole Funktionen zum Auffüllen eingeführt:

book.populate('_creator', function(err) {
 console.log(book._creator);
});

oder:

Book.populate(book, '_creator', function(err) {
 console.log(book._creator);
});

Weitere Informationen finden Sie unter : https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Auf diese Weise würden Sie den Benutzer jedoch weiterhin abfragen.

Ein kleiner Trick, um dies ohne zusätzliche Fragen zu erreichen, wäre:

book = book.toObject();
book._creator = user;
Eric Saboia
quelle
zu tun , book._creator = user;nachdem die save()die einzig richtige Antwort unter allen aktuellen Antworten ist, benötigen alle anderen Antworten eine zusätzliche Abfrage.
Mr5o1
22

Die Lösung für mich war execPopulate, so zu verwenden

const t = new MyModel(value)
return t.save().then(t => t.populate('my-path').execPopulate())
François Romain
quelle
2
Vielen Dank @Francois, Du hast mein Leben gerettet, ich habe versucht, eine Lösung dafür zu finden. Endlich verstanden.
Rupesh
18

Lösung, die ein Versprechen zurückgibt (keine Rückrufe):

Verwenden Sie Document # populate

book.populate('creator').execPopulate();

// summary
doc.populate(options);               // not executed
doc.populate(options).execPopulate() // executed, returns promise

Mögliche Implementierung

var populatedDoc = doc.populate(options).execPopulate();
var populatedDoc.then(doc => {
   ... 
});

Lesen Sie mehr über Dokument Bevölkerung hier .

Govind Rai
quelle
Gutes Zeug. Danke
Joel H
11

Nur um es näher zu erläutern und ein weiteres Beispiel zu geben, das mir geholfen hat. Dies kann denjenigen helfen, die teilweise ausgefüllte Objekte nach dem Speichern abrufen möchten. Die Methode ist auch etwas anders. Verbrachte mehr als ein oder zwei Stunden damit, nach dem richtigen Weg zu suchen.

  post.save(function(err) {
    if (err) {
      return res.json(500, {
        error: 'Cannot save the post'
      });
    }
    post.populate('group', 'name').populate({
      path: 'wallUser',
      select: 'name picture'
    }, function(err, doc) {
      res.json(doc);
    });
  });
Pratik Bothra
quelle
9

Ich dachte, ich würde das ergänzen, um die Dinge für komplette Noobs wie mich zu klären.

Was massiv verwirrend ist, wenn Sie nicht aufpassen, ist, dass es drei sehr unterschiedliche Methoden zum Auffüllen gibt. Sie sind Methoden für verschiedene Objekte (Modell vs. Dokument), nehmen unterschiedliche Eingaben entgegen und geben unterschiedliche Ausgaben (Dokument vs. Versprechen).

Hier sind sie für diejenigen, die verblüfft sind:

Document.prototype.populate ()

Siehe vollständige Dokumente.

Dieser bearbeitet Dokumente und gibt ein Dokument zurück. Im ursprünglichen Beispiel würde es so aussehen:

book.save(function(err, book) {
    book.populate('_creator', function(err, book) {
        // Do something
    })
});

Da es Dokumente bearbeitet und ein Dokument zurückgibt, können Sie sie wie folgt verketten:

book.save(function(err, book) {
    book
    .populate('_creator')
    .populate('/* Some other ObjectID field */', function(err, book) {
        // Do something
    })
});

Aber sei nicht albern wie ich und versuche das:

book.save(function(err, book) {
    book
    .populate('_creator')
    .populate('/* Some other ObjectID field */')
    .then(function(book) {
        // Do something
    })
});

Denken Sie daran: Document.prototype.populate () gibt ein Dokument zurück, das ist also Unsinn. Wenn Sie ein Versprechen wollen, brauchen Sie ...

Document.prototype.execPopulate ()

Siehe vollständige Dokumente.

Dieser bearbeitet Dokumente, ABER er gibt ein Versprechen zurück, das in das Dokument aufgelöst wird. Mit anderen Worten, Sie können es folgendermaßen verwenden:

book.save(function(err, book) {
    book
    .populate('_creator')
    .populate('/* Some other ObjectID field */')
    .execPopulate()
    .then(function(book) {
        // Do something
    })
});

Das ist besser. Endlich gibt es ...

Model.populate ()

Siehe vollständige Dokumente.

Dieser arbeitet an Modellen und gibt ein Versprechen zurück. Es wird daher etwas anders verwendet:

book.save(function(err, book) {
    Book // Book not book
    .populate(book, { path: '_creator'})
    .then(function(book) {
        // Do something
    })
});

Hoffe, das hat einigen anderen Newcomern geholfen.

curzmg
quelle
1

Leider ist dies ein langjähriges Problem mit Mungos, von dem ich glaube, dass es noch nicht gelöst ist:

https://github.com/LearnBoost/mongoose/issues/570

Was Sie tun können, ist, dafür Ihren eigenen benutzerdefinierten Getter / Setter zu schreiben (und in einer separaten Eigenschaft real zu setzen _customer). Beispielsweise:

var get_creator = function(val) {
    if (this.hasOwnProperty( "__creator" )) {
        return this.__creator;
    }
    return val;
};
var set_creator = function(val) {
    this.__creator = val;
    return val;
};
var bookSchema = new mongoose.Schema({
  _creator: {
     type: mongoose.Schema.Types.ObjectId,
     ref: 'User',
     get: get_creator,
     set: set_creator
  },
  description: String,
});

HINWEIS: Ich habe es nicht getestet und es funktioniert möglicherweise seltsam mit .populateund beim Festlegen der reinen ID.

verrückt
quelle
es scheint, dass sie nicht versuchen, das Problem zu beheben.
Pykler
1
Dieses Problem wurde in 3.6
mkoryak
@ Pykler Sie müssen wirklich die akzeptierte Antwort in die am höchsten bewertete vorher ändern, da diese Antwort nicht mehr gültig ist
Matt Fletcher
1

Mungo 5.2.7

Das funktioniert bei mir (nur viel Kopfschmerzen!)

exports.create = (req, res, next) => {
  const author = req.userData;
  const postInfo = new Post({
    author,
    content: req.body.content,
    isDraft: req.body.isDraft,
    status: req.body.status,
    title: req.body.title
  });
  postInfo.populate('author', '_id email role display_name').execPopulate();
  postInfo.save()
    .then(post => {
      res.status(200).json(post);
    }).catch(error => {
      res.status(500).json(error);
    });
};
Whisher
quelle
0

Wahrscheinlich etw. mögen

Book.createAsync(bookToSave).then((savedBook) => savedBook.populateAsync("creator"));

Wäre der schönste und am wenigsten problematische Weg, um diese Arbeit zu machen (mit Bluebird-Versprechungen).

kboom
quelle
0

Am Ende haben wir einige curry-fähige Promise-Funktionen geschrieben, in denen Sie Ihre Schema-, query_adapter-, data_adapter-Funktionen deklarieren und Zeichenfolgen im Voraus füllen. Für eine kürzere / einfachere Implementierung einfacher.

Es ist wahrscheinlich nicht sehr effizient, aber ich fand das Ausführungsbit ziemlich elegant.

Github-Datei: curry_Promises.js

Deklaration

const update_or_insert_Item = mDB.update_or_insert({
    schema : model.Item,
    fn_query_adapter : ({ no })=>{return { no }},
    fn_update_adapter : SQL_to_MDB.item,
    populate : "headgroup"
    // fn_err : (e)=>{return e},
    // fn_res : (o)=>{return o}
})

Ausführung

Promise.all( items.map( update_or_insert_Item ) )
.catch( console.error )
.then( console.log )
WouldBeNerd
quelle