【问题标题】:How do I update a specific key inside a MongoDB sub-document using Sails.js & Waterline?如何使用 Sails.js 和 Waterline 更新 MongoDB 子文档中的特定键?
【发布时间】:2016-11-22 12:04:00
【问题描述】:

我在尝试使用 Sails.js 和 Waterline ORM 更新 MongoDB 子文档中的单个键时遇到问题。这是我的person.js 模型的样子:

module.exports = {
    attributes: {
        name: { type: 'string', required: true },
        favorites: {
            type: 'json',
            defaultsTo: {
                color: null,
                place: null,
                season: null
            }
        }
    }
}

现在,假设我想从控制器更新特定人的姓名和最喜欢的季节,我正在这样做:

Person.update({ id: '1' }, {
    name: 'Dan',
    favorites: {
        season: 'Summer'
    }
}).exec(function(error, updatedPerson) {

    console.log(updatedPerson);
});

当我运行它时,favorites 对象被完全替换为只有我更新的一个键(季节键),而不是在只更新我想要的一个时保留其他两个键(颜色和位置)。我错过了什么?如何让它只更新我指定的键?

【问题讨论】:

    标签: javascript node.js mongodb sails.js waterline


    【解决方案1】:

    您可以在可以直接访问 mongo 驱动程序的模型上使用 .native() 方法,然后使用 $set 运算符独立更新字段。但是,您需要先将对象转换为具有点符号的单级文档,例如

    {
        "name": "Dan",
        "favorites.season": "Summer"
    }
    

    这样您就可以将其用作:

    var criteria = { "id": "1" },
        update = { "$set": { "name": "Dan", "favorites.season": "Summer" } },
        options = { "new": true };
    
    // Grab an instance of the mongo-driver
    Person.native(function(err, collection) {        
        if (err) return res.serverError(err);
    
        // Execute any query that works with the mongo js driver
        collection.findAndModify(
            criteria, 
            null,
            update,
            options,
            function (err, updatedPerson) {
                console.log(updatedPerson);
            }
        );
    });
    

    要转换需要更新的原始对象,请使用以下函数

    var convertNestedObjectToDotNotation = function(obj){
        var res = {};
        (function recurse(obj, current) {
            for(var key in obj) {
                var value = obj[key];
                var newKey = (current ? current + "." + key : key);  // joined key with dot
                if  (value && typeof value === "object") {
                    recurse(value, newKey);  // it's a nested object, so do it again
                } else {
                    res[newKey] = value;  // it's not an object, so set the property
                }
            }
        })(obj);
    
        return res;
    }
    

    然后你可以在你的更新中调用它

    var criteria = { "id": "1" },
        update = { "$set": convertNestedObjectToDotNotation(params) },
        options = { "new": true };
    

    查看下面的演示。

    var example = {
    	"name" : "Dan",
    	"favorites" : {
    		"season" : "winter"
    	}
    };
    
    var convertNestedObjectToDotNotation = function(obj){
    	var res = {};
    	(function recurse(obj, current) {
    		for(var key in obj) {
    			var value = obj[key];
    			var newKey = (current ? current + "." + key : key);  // joined key with dot
    			if	(value && typeof value === "object") {
    				recurse(value, newKey);  // it's a nested object, so do it again
    			} else {
    				res[newKey] = value;  // it's not an object, so set the property
    			}
    		}
    	})(obj);
    	
    	return res;
    }
    
    
    var update = { "$set": convertNestedObjectToDotNotation(example) };
    
    pre.innerHTML = "update =  " + JSON.stringify(update, null, 4);
    <pre id="pre"></pre>

    【讨论】:

    • 效果很好!虽然我应该补充一点,我必须参考 stackoverflow.com/a/25701157/2674003,因为原生查询 { "id": 1 } 不会自动工作,因为 MongoDB 使用 ObjectId 作为 ID。
    【解决方案2】:

    只需更改您的查询:

    Person.update({ id: '1' },{ name: 'Dan',favorites: { $set: {season: Summer'}}}})
    

    【讨论】:

    • 问题是,需要更新的参数是由使用我们 API 的客户端应用程序发送的。我们不能强制我们 API 的用户使用 $set 等发送它。他们向我们发送一个原始 JSON 对象。还能做吗?
    • 检查所有将要更新的嵌套对象,然后在该对象上使用 $set。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 2016-08-20
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    相关资源
    最近更新 更多