La mangusta popola dopo il salvataggio


96

Non riesco a popolare manualmente o automaticamente il campo creatore su un oggetto appena salvato ... l'unico modo che posso trovare è eseguire nuovamente la query per gli oggetti che ho già, cosa che mi dispiacerebbe fare.

Questa è la configurazione:

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);

Qui è dove mi sto tirando i capelli

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?
    });
});

MODIFICA: l'ultima mangusta ha risolto questo problema e aggiunto la funzionalità di popolamento, vedere la nuova risposta accettata.

Risposte:


136

Dovresti essere in grado di utilizzare la funzione di popolamento del modello per farlo: http://mongoosejs.com/docs/api.html#model_Model.populate Nel gestore di salvataggio per il libro, invece di:

book._creator = user;

faresti qualcosa come:

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

Probabilmente una risposta troppo tardi per aiutarti, ma di recente ero bloccato su questo, e potrebbe essere utile per altri.


Sarebbe bello se funzionasse con attributi virtuali. likecreator.profile
chovy

se l'utente ha alcuni attributi virtuali non sono inclusi.
chovy

Questo ha funzionato per me anche se ho avuto alcuni problemi quando ho provato a impostare il riferimento su null appena prima di salvare. Dopo il salvataggio, la chiamata a Book.populate ha compilato in modo errato il campo con il valore di riferimento precedente e non riesco a capire perché. Il DB contiene correttamente il null.
Daniel Flippance

2
E questo non interrogherà nuovamente il database ?!
Nepoxx

9
questo sta interrogando nuovamente il database
bubakazouba

36

Nel caso qualcuno lo stia ancora cercando.

Mongoose 3.6 ha introdotto molte funzioni interessanti da popolare:

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

o:

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

vedere di più su: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Ma in questo modo continuerai a interrogare di nuovo l'utente.

Un piccolo trucco per realizzarlo senza query aggiuntive sarebbe:

book = book.toObject();
book._creator = user;

facendo book._creator = user;dopo save()è l'unica risposta corretta tra tutte le risposte correnti, tutte le altre risposte richiedono una query aggiuntiva.
Mr5o1

23

La soluzione per me era usare execPopulate, in questo modo

const t = new MyModel(value)
return t.save().then(t => t.populate('my-path').execPopulate())

2
Grazie mille @Francois, mi hai salvato la vita, stavo cercando di trovare una soluzione per questo. Finalmente capito.
Rupesh

18

Soluzione che restituisce una promessa (senza callback):

Usa il documento # popola

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

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

Possibile implementazione

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

Leggi la popolazione di documenti qui .


Roba buona. Grazie
Joel H

11

Solo per elaborare e fornire un altro esempio, poiché mi ha aiutato. Questo potrebbe aiutare coloro che desiderano recuperare oggetti parzialmente popolati dopo il salvataggio. Anche il metodo è leggermente diverso. Ho passato più di un'ora o due a cercare il modo corretto di farlo.

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

9

Ho pensato di aggiungere a questo per chiarire le cose per i niubbi completi come me.

Ciò che crea confusione se non stai attento è che ci sono tre metodi di popolamento molto diversi. Sono metodi di oggetti diversi (Modello vs Documento), prendono input differenti e danno output differenti (Documento vs. Promessa).

Eccoli per coloro che sono sconcertati:

Document.prototype.populate ()

Vedi la documentazione completa.

Questo funziona sui documenti e restituisce un documento. Nell'esempio originale, sarebbe simile a questo:

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

Poiché funziona sui documenti e restituisce un documento, puoi concatenarli insieme in questo modo:

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

Ma non essere sciocco, come me, e prova a farlo:

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

Ricorda: Document.prototype.populate () restituisce un documento, quindi non ha senso. Se vuoi una promessa, hai bisogno di ...

Document.prototype.execPopulate ()

Vedi la documentazione completa.

Questo funziona sui documenti MA restituisce una promessa che si risolve nel documento. In altre parole, puoi usarlo in questo modo:

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

Così va meglio. Infine, c'è ...

Model.populate ()

Vedi la documentazione completa.

Questo funziona su modelli e restituisce una promessa. Viene quindi utilizzato in modo leggermente diverso:

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

Spero che questo abbia aiutato altri nuovi arrivati.


1

Sfortunatamente questo è un problema di vecchia data con la mangusta che credo non sia ancora risolto:

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

Quello che puoi fare è scrivere il tuo getter / setter personalizzato (e impostare real _customer in una proprietà separata) per questo. Per esempio:

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,
});

NOTA: non l'ho testato e potrebbe funzionare in modo strano con .populatee durante l'impostazione dell'ID puro.


sembra che non stiano cercando di risolvere il problema.
Pykler

1
questo problema è stato risolto in 3.6
mkoryak

@Pykler devi davvero cambiare la risposta accettata a quella con il punteggio più alto prima, poiché questa risposta non è più valida
Matt Fletcher

1

Mangusta 5.2.7

Questo funziona per me (solo un sacco di mal di testa!)

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

0

Probabilmente qc. piace

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

Sarebbe il modo più simpatico e meno problematico per farlo funzionare (utilizzando le promesse di Bluebird).


0

ha finito per scrivere alcune funzioni Promise in grado di curare in cui si dichiara il proprio schema, query_adapter, funzioni data_adapter e si popola la stringa in anticipo. Per un'implementazione più breve / più semplice più facile.

Probabilmente non è super efficiente, ma ho pensato che la parte di esecuzione fosse piuttosto elegante.

file github: curry_Promises.js

declinazione

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

esecuzione

Promise.all( items.map( update_or_insert_Item ) )
.catch( console.error )
.then( console.log )
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.