Nie mogę ręcznie lub automatycznie wypełnić pola twórcy nowo zapisanego obiektu ... jedynym sposobem, w jaki mogę znaleźć, jest ponowne wysłanie zapytania o obiekty, które już mam, czego nie chciałbym robić.
To jest konfiguracja:
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);
W tym miejscu ciągnę za włosy
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?
});
});
EDYCJA: najnowsza mangusta naprawiła ten problem i dodała funkcję wypełniania, zobacz nową zaakceptowaną odpowiedź.
creator.profile
Na wypadek, gdyby ktoś nadal tego szukał.
Mongoose 3.6 wprowadził wiele fajnych funkcji do zapełnienia:
book.populate('_creator', function(err) { console.log(book._creator); });
lub:
Book.populate(book, '_creator', function(err) { console.log(book._creator); });
zobacz więcej na: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population
Ale w ten sposób nadal będziesz pytać o użytkownika.
Mała sztuczka, aby to osiągnąć bez dodatkowych zapytań, to:
źródło
book._creator = user;
posave()
jest jedyną poprawną odpowiedzią spośród wszystkich aktualnych odpowiedzi, wszystkie inne odpowiedzi wymagają dodatkowego zapytania.Rozwiązaniem dla mnie było użycie
execPopulate
, takconst t = new MyModel(value) return t.save().then(t => t.populate('my-path').execPopulate())
źródło
Rozwiązanie, które zwraca obietnicę (bez oddzwonień):
Użyj wypełnienia dokumentu #
book.populate('creator').execPopulate(); // summary doc.populate(options); // not executed doc.populate(options).execPopulate() // executed, returns promise
Możliwa implementacja
var populatedDoc = doc.populate(options).execPopulate(); var populatedDoc.then(doc => { ... });
Przeczytaj o populacji dokumentów tutaj .
źródło
Żeby rozwinąć i podać inny przykład, bo to mi pomogło. Może to pomóc tym, którzy chcą odzyskać częściowo zapełnione obiekty po zapisaniu. Metoda jest również nieco inna. Spędził ponad godzinę lub dwie, szukając właściwego sposobu na zrobienie tego.
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); }); });
źródło
Pomyślałem, że dodam do tego, aby wyjaśnić rzeczy kompletnym noobom takim jak ja.
Jeśli nie jesteś ostrożny, ogromnie zagmatwane jest to, że istnieją trzy bardzo różne metody wypełniania. Są to metody różnych obiektów (model vs. dokument), pobierają różne dane wejściowe i dają różne wyniki (dokument vs. obietnica).
Oto one dla tych, którzy są zaskoczeni:
Document.prototype.populate ()
Zobacz pełną dokumentację.
Ten pracuje na dokumentach i zwraca dokument. W oryginalnym przykładzie wyglądałoby to tak:
book.save(function(err, book) { book.populate('_creator', function(err, book) { // Do something }) });
Ponieważ działa na dokumentach i zwraca dokument, możesz je połączyć w następujący sposób:
book.save(function(err, book) { book .populate('_creator') .populate('/* Some other ObjectID field */', function(err, book) { // Do something }) });
Ale nie bądź głupi, jak ja, i spróbuj zrobić to:
book.save(function(err, book) { book .populate('_creator') .populate('/* Some other ObjectID field */') .then(function(book) { // Do something }) });
Pamiętaj: Document.prototype.populate () zwraca dokument, więc to nonsens. Jeśli chcesz mieć obietnicę, potrzebujesz ...
Document.prototype.execPopulate ()
Zobacz pełną dokumentację.
Ten działa na dokumentach, ALE zwraca obietnicę, która rozwiązuje się w dokumencie. Innymi słowy, możesz go używać w ten sposób:
book.save(function(err, book) { book .populate('_creator') .populate('/* Some other ObjectID field */') .execPopulate() .then(function(book) { // Do something }) });
Tak lepiej. Wreszcie jest ...
Model.populate ()
Zobacz pełną dokumentację.
Ten działa na modelach i zwraca obietnicę. Dlatego jest używany nieco inaczej:
book.save(function(err, book) { Book // Book not book .populate(book, { path: '_creator'}) .then(function(book) { // Do something }) });
Mam nadzieję, że pomogło to niektórym innym nowo przybyłym.
źródło
Niestety jest to długotrwały problem z mangustą, który moim zdaniem nie został jeszcze rozwiązany:
https://github.com/LearnBoost/mongoose/issues/570
To, co możesz zrobić, to napisać własny niestandardowy program pobierający / ustawiający (i ustawić wartość real
_customer
w oddzielnej właściwości). Na przykład: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, });
UWAGA: Nie testowałem tego i może działać dziwnie z
.populate
i podczas ustawiania czystego identyfikatora.źródło
Mangusta 5.2.7
To działa dla mnie (tylko duży ból głowy!)
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); }); };
źródło
Prawdopodobnie coś. lubić
Book.createAsync(bookToSave).then((savedBook) => savedBook.populateAsync("creator"));
Byłby to najmilszy i najmniej problematyczny sposób na wykonanie tego zadania (obietnice dotyczące korzystania z Bluebird).
źródło
skończyło się na napisaniu kilku funkcji Promise, które mogą być używane w curry, w których deklarujesz schemat, funkcje query_adapter, data_adapter i wypełniasz ciąg z wyprzedzeniem. Dla łatwiejszej / krótszej implementacji.
Prawdopodobnie nie jest super wydajny, ale pomyślałem, że wykonanie było dość eleganckie.
plik github: curry_Promises.js
Deklaracja
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} })
wykonanie
Promise.all( items.map( update_or_insert_Item ) ) .catch( console.error ) .then( console.log )
źródło