2013-05-06 49 views
9

Sử dụng Mongoose phiên bản 3.6.4Mongoose.js: Cập nhật nguyên tử của thuộc tính lồng nhau?

Nói rằng tôi có một tài liệu MongoDB như vậy:

{ 
    "_id" : "5187b74e66ee9af96c39d3d6", 
    "profile" : { 
     "name" : { 
      "first" : "Joe", 
      "last" : "Pesci", 
      "middle" : "Frank" 
     } 
    } 
} 

Và tôi có giản đồ sau đây cho người dùng:

var UserSchema = new mongoose.Schema({ 
    _id: { type: String }, 
    email: { type: String, required: true, index: { unique: true }}, 
    active: { type: Boolean, required: true, 'default': false }, 
    profile: { 
    name: { 
     first: { type: String, required: true }, 
     last:  { type: String, required: true }, 
     middle: { type: String } 
    } 
    } 
    created: { type: Date, required: true, 'default': Date.now}, 
    updated: { type: Date, required: true, 'default': Date.now} 
); 

Và tôi gửi một hình thức qua một trường có tên: profile[name][first] có giá trị là Joseph

và do đó tôi muốn cập nhật chỉ là fi của người dùng tên đầu tiên, nhưng để lại của ông cuối cùng và trung bình một mình, tôi nghĩ rằng tôi sẽ chỉ làm:

User.update({email: "[email protected]"}, req.body, function(err, result){}); 

Nhưng khi tôi làm điều đó, nó "xóa" các profile.name.lastprofile.name.middle tính và tôi kết thúc với một doc trông giống như :

{ 
    "_id" : "5187b74e66ee9af96c39d3d6", 
    "profile" : { 
     "name" : { 
      "first" : "Joseph" 
     } 
    } 
} 

Vì vậy, về cơ bản nó ghi đè lên tất cả các profile với req.body.profile, mà tôi đoán có ý nghĩa. Có cách nào xung quanh nó mà không cần phải rõ ràng hơn bằng cách chỉ định các trường của tôi trong truy vấn cập nhật thay vì req.body?

+0

Cân nhắc chấp nhận câu trả lời của Aichholzer vì nó cung cấp giải pháp thực sự cho vấn đề. – zbr

Trả lời

7

Bạn là chính xác, Mongoose chuyển đổi cập nhật để $set cho bạn. Nhưng điều này không giải quyết được vấn đề của bạn. Hãy thử nó trong vỏ mongodb và bạn sẽ thấy hành vi tương tự.

Thay vào đó, để cập nhật một thuộc tính lồng nhau sâu, bạn cần chỉ định đường dẫn đầy đủ đến thuộc tính sâu trong $set.

User.update({ email: '[email protected]' }, { 'profile.name.first': 'Joseph' }, callback) 
+3

Phải, nhưng tôi đã hy vọng tôi có thể vượt qua trong 'req.body' vì vậy tôi không phải chỉ định tất cả các trường có thể. Vì vậy, tôi đã thực hiện 'findById()' trước tiên, xóa '__v' và' _id' khỏi tài liệu tìm thấy, sử dụng '_.deepExtend()' để nhập vào các thuộc tính mới được cập nhật, và sau đó truyền đối tượng frankenstein của tôi cho Mongoose's 'update'. – k00k

+1

@aaronheckmann, có anyway nói với mongoose hoặc mongo rằng "đây là đối tượng mới nhất của tôi, chỉ cần cập nhật nó"? –

+0

@ k00k - Cách tiếp cận đó có nguyên tử không? Nó sẽ không viết lại toàn bộ tài liệu sao? Tại sao bạn phải xóa __v và _id? –

0

Tôi nghĩ rằng bạn đang tìm kiếm $ thiết lập

http://docs.mongodb.org/manual/reference/operator/set/

User.update({email: "[email protected]"}, { $set : req.body}, function(err, result){}); 

Hãy thử điều đó

+0

Tôi nghĩ rằng Mongoose là "an toàn" theo mặc định và vì vậy một 'cập nhật' thực sự là một' $ set', tôi có sai không? – k00k

+0

Và để thêm, tôi chỉ thử nó và nó không hoạt động. Nó có vẻ là một vấn đề với các thuộc tính lồng nhau. Chỉ cần làm rõ, nó chỉ là thuộc tính lồng nhau ở cùng cấp (anh chị em) bị thổi bay đi. – k00k

+0

bạn đang tìm $ set với tên trường cụ thể - "name.first". Điều này không liên quan gì đến việc viết an toàn và không an toàn, chỉ cho dù bạn đang bảo nó cập nhật toàn bộ tài liệu hay một trường đơn lẻ. –

0

Có lẽ đó là một giải pháp tốt - thêm lựa chọn để Model.update, mà thay thế đối tượng lồng nhau như:

{field1: 1, fields2: {a: 1, b: 2}} => { 'field1': 1, 'field2.a': 1, 'field2.b': 2}

nestedToDotNotation: function(obj, keyPrefix) { 
    var result; 
    if (keyPrefix == null) { 
     keyPrefix = ''; 
    } 
    result = {}; 
    _.each(obj, function(value, key) { 
     var nestedObj, result_key; 
     result_key = keyPrefix + key; 
     if (!_.isArray(value) && _.isObject(value)) { 
     result_key += '.'; 
     nestedObj = module.exports.nestedToDotNotation(value, result_key); 
     return _.extend(result, nestedObj); 
     } else { 
     return result[result_key] = value; 
     } 
    }); 
    return result; 
    } 

});

cần cải thiện xử lý tham chiếu vòng tròn, nhưng điều này thực sự hữu ích khi làm việc với các đối tượng lồng nhau

Tôi đang sử dụng dấu gạch dưới.js ở đây, nhưng các chức năng này có thể dễ dàng được thay thế bằng chất tương tự khác

8

Một cách rất dễ dàng để giải quyết việc này với Moongose 4.1flat gói:

var flat = require('flat'), 
    Schema = mongoose.Schema, 
     schema = new Schema(
      { 
       name: { 
        first: { 
         type: String, 
         trim: true 
        }, 
        last: { 
         type: String, 
         trim: true 
        } 
       } 
      } 
     ); 

    schema.pre('findOneAndUpdate', function() { 
     this._update = flat(this._update); 
    }); 


    mongoose.model('User', schema); 

req.body (ví dụ) bây giờ có thể là:

{ 
    name: { 
     first: 'updatedFirstName' 
    } 
} 

Đối tượng sẽ được làm phẳng trước khi truy vấn thực tế được thực thi, do đó $set sẽ chỉ cập nhật các thuộc tính được mong đợi thay vì toàn bộ đối tượng name.

+0

chỉ cần cẩn thận khi có lồng nhau của ObjectId. căn hộ sẽ biến những người đó thành 'something._bsontype': 'ObjectID', 'something.id': ' – tdecs

Các vấn đề liên quan