พังพอนเติมหลังจากบันทึก


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

คำตอบอาจจะสายเกินไปที่จะช่วยคุณได้ แต่เมื่อเร็ว ๆ นี้ฉันติดขัดและอาจเป็นประโยชน์สำหรับคนอื่น ๆ


คงจะดีถ้าสิ่งนี้ใช้ได้กับคุณสมบัติเสมือนจริง ชอบcreator.profile
chovy

หากผู้ใช้มีคุณลักษณะเสมือนบางอย่างจะไม่รวมอยู่ด้วย
chovy

สิ่งนี้ใช้ได้ผลสำหรับฉันแม้ว่าฉันจะมีปัญหาบางอย่างเมื่อพยายามตั้งค่าการอ้างอิงเป็น null ก่อนที่ฉันจะบันทึก หลังจากบันทึกแล้วการเรียก Book.populate เติมข้อมูลในฟิลด์ไม่ถูกต้องด้วยค่าอ้างอิงก่อนหน้านี้และฉันไม่สามารถหาสาเหตุได้ DB มี null สำเร็จ
Daniel Flippance

2
และสิ่งนี้จะไม่ทำการสืบค้นฐานข้อมูลซ้ำ?!
Nepoxx

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

โซลูชันที่คืนสัญญา (ไม่มีการโทรกลับ):

ใช้ Document # เติมข้อมูล

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

9

ฉันคิดว่าฉันจะเพิ่มสิ่งนี้เพื่อชี้แจงสิ่งต่าง ๆ สำหรับ noobs ที่สมบูรณ์แบบเช่นตัวฉันเอง

สิ่งที่ทำให้สับสนอย่างมากหากคุณไม่ระวังคือมีวิธีการเติมข้อมูลที่แตกต่างกันสามวิธี เป็นวิธีการของออบเจ็กต์ที่แตกต่างกัน (Model vs. Document) ใช้อินพุตที่แตกต่างกันและให้เอาต์พุตที่แตกต่างกัน (เอกสารเทียบกับสัญญา)

ที่นี่มีไว้สำหรับผู้ที่งุนงง:

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

หวังว่าจะช่วยให้ผู้มาใหม่คนอื่น ๆ


1

น่าเสียดายที่นี่เป็นปัญหาที่ยาวนานกับพังพอนซึ่งฉันเชื่อว่ายังไม่สามารถแก้ไขได้:

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

สิ่งที่คุณทำได้คือเขียนว่าคุณเป็นเจ้าของ getter / setter ที่กำหนดเอง (และตั้งค่าจริง _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
ปัญหานี้ได้รับการแก้ไขใน 3.6
mkoryak

@Pykler คุณต้องเปลี่ยนคำตอบที่ได้รับการยอมรับเป็นอันดับสูงสุดก่อนหน้านี้เนื่องจากคำตอบนี้ใช้ไม่ได้อีกต่อไป
Matt Fletcher

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

0

น่าจะเป็น sth. ชอบ

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

จะเป็นวิธีที่ดีที่สุดและมีปัญหาน้อยที่สุดในการทำงานนี้ (การใช้สัญญา Bluebird)


0

ลงเอยด้วยการเขียนฟังก์ชัน Promise ที่สามารถแกงได้ซึ่งคุณจะประกาศ schema, 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 )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.