Luwak mengisi setelah simpan


99

Saya tidak dapat secara manual atau otomatis mengisi kolom pencipta pada objek yang baru disimpan ... satu-satunya cara yang dapat saya temukan adalah dengan menanyakan ulang objek yang sudah saya miliki yang tidak ingin saya lakukan.

Ini adalah pengaturannya:

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

Di sinilah saya menarik rambut saya

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: luwak terbaru memperbaiki masalah ini dan menambahkan fungsi populasi, lihat jawaban baru yang diterima.

Jawaban:


140

Anda harus dapat menggunakan fungsi populate Model untuk melakukan ini: http://mongoosejs.com/docs/api.html#model_Model.populate Di save handler for book, alih-alih:

book._creator = user;

Anda akan melakukan sesuatu seperti:

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

Mungkin jawaban yang terlambat untuk membantu Anda, tetapi saya terjebak dalam hal ini baru-baru ini, dan mungkin berguna bagi orang lain.


Akan lebih baik jika ini bekerja dengan atribut virtual. seperticreator.profile
chovy

jika pengguna memiliki beberapa atribut virtual, atribut tersebut tidak disertakan.
chovy

Ini berhasil untuk saya meskipun saya mengalami beberapa masalah ketika saya mencoba mengatur referensi ke nol tepat sebelum saya menyimpannya. Setelah penyimpanan, memanggil Book.populate salah mengisi bidang dengan nilai referensi sebelumnya dan saya tidak tahu mengapa. DB berhasil berisi null.
Daniel Flippance

2
Dan ini tidak akan meminta ulang database ?!
Nepoxx

9
ini menanyakan kembali database
bubakazouba

37

Jika ada yang masih mencari ini.

Mongoose 3.6 telah memperkenalkan banyak fitur keren untuk diisi:

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

atau:

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

lihat lebih lanjut di: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Namun dengan cara ini Anda masih akan menanyakan pengguna itu lagi.

Trik kecil untuk menyelesaikannya tanpa pertanyaan tambahan adalah:

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

melakukan book._creator = user;setelah save()adalah satu-satunya jawaban yang benar di antara semua jawaban saat ini, semua jawaban lainnya memerlukan pertanyaan tambahan.
Mr5o1

25

Solusi bagi saya adalah menggunakan execPopulate, seperti itu

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

3
Terima kasih banyak @Francois, Anda menyelamatkan hidup saya, saya mencoba menemukan solusi untuk ini. Akhirnya mengerti.
Rupesh

22

Solusi yang mengembalikan sebuah janji (tanpa panggilan balik):

Gunakan Document # populate

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

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

Implementasi yang Mungkin

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

Baca tentang populasi dokumen di sini .


Barang bagus. Terima kasih
Joel H

11

Hanya untuk menguraikan dan memberikan contoh lain, karena itu membantu saya. Ini mungkin membantu mereka yang ingin mengambil objek yang terisi sebagian setelah menyimpan. Metodenya juga sedikit berbeda. Menghabiskan lebih dari satu atau dua jam mencari cara yang benar untuk melakukannya.

  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

Saya pikir saya akan menambahkan ini untuk mengklarifikasi hal-hal untuk noob lengkap seperti saya.

Yang sangat membingungkan jika Anda tidak berhati-hati adalah ada tiga metode populasi yang sangat berbeda. Mereka adalah metode dari objek yang berbeda (Model vs. Dokumen), mengambil input yang berbeda dan memberikan output yang berbeda (Dokumen vs. Janji).

Ini dia untuk mereka yang bingung:

Document.prototype.populate ()

Lihat dokumen lengkapnya.

Yang ini bekerja pada dokumen dan mengembalikan dokumen. Dalam contoh aslinya, akan terlihat seperti ini:

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

Karena berfungsi pada dokumen dan mengembalikan dokumen, Anda dapat menyatukannya seperti ini:

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

Tapi jangan konyol, seperti saya, dan coba lakukan ini:

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

Ingat: Document.prototype.populate () mengembalikan dokumen, jadi ini tidak masuk akal. Jika Anda menginginkan janji, Anda perlu ...

Document.prototype.execPopulate ()

Lihat dokumen lengkapnya.

Yang ini berfungsi pada dokumen NAMUN mengembalikan janji yang menyelesaikan dokumen. Dengan kata lain, Anda bisa menggunakannya seperti ini:

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

Itu lebih baik. Akhirnya, ada ...

Model.populate ()

Lihat dokumen lengkapnya.

Yang ini bekerja pada model dan mengembalikan janji. Oleh karena itu digunakan sedikit berbeda:

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

Harapan itu membantu beberapa pendatang baru lainnya.


1

Sayangnya ini adalah masalah lama dengan luwak yang saya yakin belum terselesaikan:

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

Apa yang dapat Anda lakukan adalah menulis getter / setter kustom Anda sendiri (dan menyetel real _customer di properti terpisah) untuk ini. Sebagai contoh:

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

CATATAN: Saya tidak mengujinya dan mungkin bekerja dengan aneh .populatedan saat menyetel id murni.


tampaknya mereka tidak ingin memperbaiki masalah tersebut.
Pykler

1
masalah ini diperbaiki di 3.6
mkoryak

@Pykler Anda benar-benar perlu mengubah jawaban yang diterima menjadi yang berperingkat tertinggi sebelumnya, karena jawaban ini tidak lagi valid
Matt Fletcher

1

Luwak 5.2.7

Ini bekerja untuk saya (hanya banyak sakit kepala!)

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

Mungkin sth. Suka

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

Akan menjadi cara terbaik dan paling tidak bermasalah untuk membuat ini bekerja (Menggunakan janji Bluebird).


0

akhirnya menulis beberapa fungsi Promise yang bisa digunakan untuk mendeklarasikan skema, query_adapter, fungsi data_adapter, dan mengisi string terlebih dahulu. Untuk implementasi yang lebih singkat / sederhana lebih mudah.

Ini mungkin tidak super efisien, tapi saya pikir sedikit eksekusi cukup elegan.

file github: curry_Promises.js

deklarasi

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

eksekusi

Promise.all( items.map( update_or_insert_Item ) )
.catch( console.error )
.then( console.log )
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.