Mangosta poblar después de guardar

96

No puedo completar manual o automáticamente el campo del creador en un objeto recién guardado ... la única forma en que puedo encontrar es volver a consultar los objetos que ya tengo y que odiaría hacer.

Esta es la configuración:

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

Aquí es donde me estoy tirando del pelo

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

EDITAR: la última mangosta solucionó este problema y agregó la funcionalidad de poblar, vea la nueva respuesta aceptada.

Pykler
fuente

Respuestas:

136

Debería poder usar la función rellenar del modelo para hacer esto: http://mongoosejs.com/docs/api.html#model_Model.populate En el controlador de guardado del libro, en lugar de:

book._creator = user;

harías algo como:

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

Probablemente una respuesta demasiado tarde para ayudarlo, pero me quedé atascado en esto recientemente y podría ser útil para otros.

usuario1417684
fuente
Sería bueno si esto funcionara con atributos virtuales. me gustacreator.profile
chovy
si el usuario tiene algunos atributos virtuales, no se incluyen.
Chovy
Esto funcionó para mí, aunque tuve algunos problemas cuando intenté establecer la referencia en nula justo antes de guardar. Después de guardar, llamar a Book.populate completó incorrectamente el campo con el valor de referencia anterior y no puedo entender por qué. La base de datos contiene correctamente el nulo.
Daniel Flippance
2
¿Y esto no volverá a consultar la base de datos?
Nepoxx
9
esto es volver a consultar la base de datos
bubakazouba
36

En caso de que alguien todavía esté buscando esto.

Mongoose 3.6 ha introducido muchas características interesantes para completar:

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

o:

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

ver más en: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Pero de esta forma seguiría consultando al usuario nuevamente.

Un pequeño truco para lograrlo sin consultas adicionales sería:

book = book.toObject();
book._creator = user;
Eric Saboia
fuente
Si hace book._creator = user;después de que save()es la única respuesta correcta entre todas las respuestas actuales, todas las demás respuestas requieren una consulta adicional.
Mr5o1
23

La solución para mí fue usar execPopulate, así

const t = new MyModel(value)
return t.save().then(t => t.populate('my-path').execPopulate())
François Romain
fuente
2
Muchas gracias @Francois, me salvaste la vida, estaba tratando de encontrar una solución para esto. Finalmente lo entendí.
Rupesh
18

Solución que devuelve una promesa (sin devoluciones de llamada):

Usar documento # rellenar

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

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

Posible implementación

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

Lea sobre la población de documentos aquí .

Govind Rai
fuente
Buen material. Gracias
Joel H
11

Solo para elaborar y dar otro ejemplo, ya que me ayudó. Esto podría ayudar a quienes deseen recuperar objetos parcialmente poblados después de guardar. El método también es ligeramente diferente. Pasé más de una hora o dos buscando la forma correcta de hacerlo.

  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
fuente
9

Pensé en agregar a esto para aclarar las cosas para los novatos como yo.

Lo que es enormemente confuso si no tiene cuidado es que hay tres métodos de poblado muy diferentes. Son métodos de diferentes objetos (Modelo vs. Documento), toman diferentes entradas y dan diferentes salidas (Documento vs. Promesa).

Aquí están para aquellos que están desconcertados:

Document.prototype.populate ()

Ver documentos completos.

Éste trabaja en documentos y devuelve un documento. En el ejemplo original, se vería así:

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

Debido a que funciona con documentos y devuelve un documento, puede encadenarlos de esta manera:

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

Pero no seas tonto, como yo, y trata de hacer esto:

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

Recuerde: Document.prototype.populate () devuelve un documento, así que esto es una tontería. Si quieres una promesa, necesitas ...

Document.prototype.execPopulate ()

Ver documentos completos.

Este funciona en documentos PERO devuelve una promesa que se resuelve en el documento. En otras palabras, puedes usarlo así:

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

Eso es mejor. Finalmente, hay ...

Model.populate ()

Ver documentos completos.

Este funciona en modelos y devuelve una promesa. Por lo tanto, se usa de manera un poco diferente:

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

Espero que haya ayudado a otros recién llegados.

curzmg
fuente
1

Desafortunadamente, este es un problema de larga data con la mangosta que creo que aún no está resuelto:

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

Lo que puede hacer es escribir su propio getter / setter personalizado (y establecer real _customer en una propiedad separada) para esto. Por ejemplo:

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: No lo probé y podría funcionar de manera extraña con .populatey al configurar ID puro.

monstruoso
fuente
parece que no buscan solucionar el problema.
Pykler
1
este problema se solucionó en 3.6
mkoryak
@Pykler, realmente necesita cambiar la respuesta aceptada a la mejor calificada antes, ya que esta respuesta ya no es válida
Matt Fletcher
1

Mangosta 5.2.7

Esto funciona para mí (¡solo un montón de dolor de cabeza!)

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
fuente
0

Probablemente algo. me gusta

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

Sería la forma más agradable y menos problemática de hacer que esto funcione (usando las promesas de Bluebird).

kboom
fuente
0

terminó escribiendo algunas funciones de Promise aptas para curry donde declara su esquema, query_adapter, funciones de data_adapter y completa la cadena de antemano. Para una implementación más corta / simple más fácil.

Probablemente no sea muy eficiente, pero pensé que la parte de ejecución fue bastante elegante.

archivo github : curry_Promises.js

declaracion

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

ejecución

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