Мангуст заселен после сохранения

100

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

Это установка:

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

РЕДАКТИРОВАТЬ: последний мангуст исправил эту проблему и добавил функцию заполнения, см. Новый принятый ответ.

Пиклер
источник

Ответы:

141

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

book._creator = user;

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

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

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

user1417684
источник
Было бы неплохо, если бы это работало с виртуальными атрибутами. likecreator.profile
chovy 05
если у пользователя есть виртуальные атрибуты, они не включаются.
chovy
Это сработало для меня, хотя у меня были некоторые проблемы, когда я попытался установить ссылку на null непосредственно перед сохранением. После сохранения вызов Book.populate неправильно заполнил поле предыдущим ссылочным значением, и я не могу понять, почему. БД успешно содержит нуль.
Дэниел Флиппанс
2
И это не будет повторно запрашивать базу данных ?!
Nepoxx 02 фев.15,
9
это повторный запрос к базе данных
bubakazouba
37

В случае, если кто-то все еще ищет это.

В 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;
Эрик Сабоя
источник
выполнение book._creator = user;после save()- это единственный правильный ответ среди всех текущих ответов, все остальные ответы требуют дополнительного запроса.
Mr5o1
25

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

const t = new MyModel(value)
return t.save().then(t => t.populate('my-path').execPopulate())
Франсуа Ромен
источник
3
Большое спасибо @Francois, Ты спас мне жизнь, я пытался найти решение для этого. Наконец-то понял.
Rupesh
22

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

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

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 => {
   ... 
});

О заполнении документа читайте здесь .

Говинд Рай
источник
Хорошая вещь. Спасибо
Joel H
11

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

  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
источник
10

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

Что сильно сбивает с толку, если вы не будете осторожны, так это то, что есть три очень разных метода заполнения. Это методы разных объектов (Модель против Документа), они принимают разные входные данные и дают разные результаты (Документ против Обещания).

Вот они для тех, кто сбит с толку:

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

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

curzmg
источник
1

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

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

Что вы можете сделать, так это написать собственный пользовательский метод получения / установки (и установить для этого значение 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и при установке чистого идентификатора.

причудливый
источник
похоже, они не пытаются решить эту проблему.
Pykler
1
эта проблема исправлена ​​в
версии
@Pykler, вам действительно нужно изменить принятый ответ на ответ с наивысшим рейтингом раньше, так как этот ответ больше недействителен
Мэтт Флетчер,
1

Мангуст 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);
    });
};
Whisher
источник
0

Наверное, что-то. подобно

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

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

кбум
источник
0

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

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

файл на github: curry_Promises.js

заявление

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 )
WillBeNerd
источник