Mongoose cư trú sau khi lưu


96

Tôi không thể điền thủ công hoặc tự động trường trình tạo trên một đối tượng mới được lưu ... cách duy nhất tôi có thể tìm là truy vấn lại các đối tượng tôi đã có mà tôi không muốn làm.

Đây là thiết lập:

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

Đây là nơi tôi đang nhổ tóc

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

CHỈNH SỬA: mongoose mới nhất đã khắc phục sự cố này và thêm chức năng điền, xem câu trả lời mới được chấp nhận.

Câu trả lời:


136

Bạn sẽ có thể sử dụng chức năng điền của Model để thực hiện việc này: http://mongoosejs.com/docs/api.html#model_Model.populate Trong trình xử lý lưu cho sách, thay vì:

book._creator = user;

bạn sẽ làm một cái gì đó như:

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

Có lẽ là quá muộn để có câu trả lời giúp bạn, nhưng tôi đã gặp khó khăn về vấn đề này gần đây và nó có thể hữu ích cho những người khác.


Sẽ rất tuyệt nếu điều này hoạt động với các thuộc tính ảo. thíchcreator.profile
chovy

nếu người dùng có một số thuộc tính ảo thì chúng không được đưa vào.
chovy

Điều này đã làm việc cho tôi mặc dù tôi đã gặp một số vấn đề khi tôi cố gắng đặt tham chiếu thành null ngay trước khi tôi lưu. Sau khi lưu, việc gọi Book.populate đã điền không chính xác trường với giá trị tham chiếu trước đó và tôi không thể tìm ra lý do. DB thành công chứa null.
Daniel Flippance

2
Và điều này sẽ không truy vấn lại cơ sở dữ liệu ?!
Nepoxx

9
đây là tái truy vấn cơ sở dữ liệu
bubakazouba

36

Trong trường hợp có ai đó vẫn đang tìm kiếm điều này.

Mongoose 3.6 đã giới thiệu rất nhiều tính năng thú vị để đưa vào:

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

hoặc là:

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

xem thêm tại: https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

Nhưng bằng cách này, bạn vẫn sẽ truy vấn lại người dùng.

Một mẹo nhỏ để hoàn thành nó mà không cần truy vấn thêm sẽ là:

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

làm book._creator = user;sau save()là câu trả lời đúng duy nhất trong số tất cả các câu trả lời hiện tại, tất cả các câu trả lời khác yêu cầu một truy vấn bổ sung.
Mr5o1

23

Giải pháp cho tôi là sử dụng execPopulate, như vậy

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

2
Cảm ơn @Francois rất nhiều, Bạn đã cứu mạng tôi, tôi đang cố gắng tìm giải pháp cho việc này. FInally đã hiểu.
Rupesh

18

Giải pháp trả về một lời hứa (không gọi lại):

Sử dụng Tài liệu # điền

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

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

Khả năng thực hiện

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

Đọc về dân số tài liệu tại đây .


Đồ tốt. Cảm ơn
Joel H

11

Chỉ để giải thích và đưa ra một ví dụ khác, vì nó đã giúp tôi. Điều này có thể giúp ích cho những ai muốn truy xuất các đối tượng được điền một phần sau khi lưu. Phương pháp cũng hơi khác một chút. Đã dành hơn một hoặc hai giờ để tìm cách thực hiện chính xác.

  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

Tôi nghĩ rằng tôi sẽ thêm vào điều này để làm rõ mọi thứ cho những người hoàn chỉnh như tôi.

Điều rất khó hiểu nếu bạn không cẩn thận là có ba phương pháp điền rất khác nhau. Chúng là các phương thức của các đối tượng khác nhau (Model so với Document), lấy các đầu vào khác nhau và đưa ra các đầu ra khác nhau (Document so với Promise).

Đây là chúng dành cho những người bị cản trở:

Document.prototype.populate ()

Xem tài liệu đầy đủ.

Cái này hoạt động trên tài liệu và trả về một tài liệu. Trong ví dụ ban đầu, nó sẽ như thế này:

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

Bởi vì nó hoạt động trên tài liệu và trả về một tài liệu, bạn có thể chuỗi chúng lại với nhau như vậy:

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

Nhưng đừng ngớ ngẩn như tôi, và cố gắng làm điều này:

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

Hãy nhớ rằng: Document.prototype.populate () trả về một tài liệu, vì vậy điều này là vô nghĩa. Nếu bạn muốn một lời hứa, bạn cần ...

Document.prototype.execPopulate ()

Xem tài liệu đầy đủ.

Cái này hoạt động trên các tài liệu NHƯNG nó trả về một lời hứa giải quyết cho tài liệu. Nói cách khác, bạn có thể sử dụng nó như sau:

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

Cái đó tốt hơn. Cuối cùng, có ...

Model.populate ()

Xem tài liệu đầy đủ.

Điều này hoạt động trên các mô hình và trả về một lời hứa. Do đó, nó được sử dụng hơi khác một chút:

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

Hy vọng điều đó đã giúp một số người mới khác.


1

Thật không may, đây là một vấn đề lâu dài với mongoose mà tôi tin rằng vẫn chưa được giải quyết:

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

Những gì bạn có thể làm là viết bạn sở hữu getter / setter tùy chỉnh (và đặt thực _customer trong một thuộc tính riêng biệt) cho việc này. Ví dụ:

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

LƯU Ý: Tôi đã không kiểm tra nó và nó có thể hoạt động lạ với .populatevà khi đặt id thuần.


Có vẻ như họ không tìm cách khắc phục sự cố.
Pykler

1
sự cố này được khắc phục trong ngày 3.6
mkoryak

@Pykler bạn thực sự cần phải thay đổi câu trả lời được chấp nhận cho một đánh giá cao nhất trước đó, như câu trả lời này là không còn giá trị
Matt Fletcher

1

Mongoose 5.2.7

Điều này làm việc cho tôi (chỉ là rất nhiều đau đầu!)

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

Có lẽ là sth. giống

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

Sẽ là cách tốt nhất và ít vấn đề nhất để thực hiện công việc này (Sử dụng lời hứa của Bluebird).


0

đã kết thúc bằng việc viết một số hàm Promise có thể sử dụng được trong curry, nơi bạn khai báo các hàm lược đồ, query_adapter, data_adapter và điền trước chuỗi. Để thực hiện ngắn hơn / đơn giản hơn dễ dàng hơn.

Nó có lẽ không quá hiệu quả, nhưng tôi nghĩ rằng bit thực thi khá thanh lịch.

tệp github: curry_Promises.js

tuyên bố

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

chấp hành

Promise.all( items.map( update_or_insert_Item ) )
.catch( console.error )
.then( console.log )
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.