Подтвердить что ты не робот

Mongoose населяют после сохранения

Я не могу вручную или автоматически заполнить поле создателя на недавно сохраненном объекте... единственный способ, который я могу найти, - это повторно запросить объекты, которые у меня уже есть, что я бы не хотел делать.

Это настройка:

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

Вот где я тяну свои волосы

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

EDIT: последний mongoose исправил эту проблему и добавил функцию заполнения, см. новый принятый ответ.

4b9b3361

Ответ 1

Вы можете использовать функцию заполнения модели для этого: http://mongoosejs.com/docs/api.html#model_Model.populate В обработчике сохранения для книги вместо

book._creator = user;

вы бы сделали что-то вроде:

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

Возможно, слишком поздно ответить, чтобы помочь вам, но я недавно застрял в этом, и это может быть полезно для других.

Ответ 2

Если кто-то все еще ищет это.

Mongoose 3.6 представила множество интересных функций для заполнения:

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

или

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

подробнее см.: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Но таким образом вы все равно будете запрашивать пользователя снова.

Небольшой трюк для выполнения этого без дополнительных запросов будет:

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

Ответ 3

Просто чтобы уточнить и привести еще один пример, поскольку он помог мне. Это может помочь тем, кто хочет получить частично заполненные объекты после сохранения. Метод также немного отличается. Прошло более часа или двух, ища правильный способ сделать это.

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

Ответ 4

Решение, которое возвращает обещание (без обратных вызовов):

Использовать документ # populate

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

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

Возможная реализация

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

Читайте о документе населения здесь.

Ответ 5

Решение для меня состояло в том, чтобы использовать execPopulate, например

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

Ответ 6

Я думал, что добавлю к этому, чтобы прояснить ситуацию для полных нубов, подобных мне.

Что массово запутывает, если вы не осторожны, так это три разных метода заполнения. Это методы разных объектов (Model vs. Document), используют разные входы и дают разные результаты (Document vs. Promise).

Здесь они предназначены для тех, кто озадачен:

Document.prototype.populate()

См. Полный документ.

Этот документ работает с документами и возвращает документ. В исходном примере это будет выглядеть так:

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

Поскольку он работает с документами и возвращает документ, вы можете связать их следующим образом:

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

Но не будьте глупыми, как я, и постарайтесь сделать это:

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

Помните: Document.prototype.populate() возвращает документ, так что это вздор. Если вам нужно обещание, вам нужно...

Document.prototype.execPopulate()

См. Полный документ.

Это работает над документами, но возвращает обещание, которое разрешает документ. Другими словами, вы можете использовать его следующим образом:

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

Это лучше. Наконец, есть...

Model.populate()

См. Полный документ.

Это работает на моделях и возвращает обещание. Поэтому он использовался несколько иначе:

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

Надеюсь, что это помогло другим новичкам.

Ответ 7

К сожалению, это давняя проблема с мангустом, который, я считаю, еще не решен:

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

Что вы можете сделать, так это написать собственный пользовательский getter/setter (и установить real _customer в отдельном свойстве). Например:

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

ПРИМЕЧАНИЕ. Я не тестировал его, и он мог бы работать странно с .populate и при установке чистого идентификатора.

Ответ 8

Вероятно, как

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

Был бы самый приятный и наименее проблематичный способ сделать эту работу (с помощью Bluebird promises).

Ответ 9

Mongoose 5.2.7

Это работает для меня (просто много головной боли!)

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

Ответ 10

закончил тем, что написал некоторые функции Promise, способные выполнять карри, где вы объявляете свои функции schema, query_adapter, data_adapter и заранее заполняете строку. Для более короткой/более простой реализации.

Это, вероятно, не суперэффективно, но я думал, что бит исполнения был довольно изящным.

Файл github: curry_Promises.js

declartion

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

выполнение

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