MongoDB - $设置更新或推送Array元素

Anu*_*nup 17 javascript mongoose mongodb node.js mongodb-query

在产品系列中,我有一系列Recentviews,其中有2个字段被查看和查看日期.

在一个场景中,如果我已经有一个记录viewedby,那么我需要更新它.例如,如果我有这样的数组: -

"recentviews" : [ 
        {
            "viewedby" : "abc",
            "vieweddate" : ISODate("2014-05-08T04:12:47.907Z")
        }
    ]
Run Code Online (Sandbox Code Playgroud)

用户是abc,所以我需要更新上面的&如果没有abc我必须的记录$push.

我试过$set如下: -

db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },
                    { $set: 
                        { "recentviews": 
                            { 
                                viewedby: 'abc',
                                vieweddate: ISODate("2014-05-09T04:12:47.907Z") 
                            }
                        } 
                     }
            )
Run Code Online (Sandbox Code Playgroud)

上面的查询将删除Array中的所有其他元素.

Nei*_*unn 20

实际上做你所说的你正在做的事情并不是一个单一的操作,但我将完成所需的部分,以便做到这一点或以其他方式涵盖其他可能的情况.

您正在寻找的是位置$操作员.您需要部分查询来"查找"所需数组的元素.

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

因此,$代表数组中匹配位置的位置,以便更新部分知道要更新的数组中的哪个项目.您可以访问数组中文档的各个字段,或者只指定要在该位置更新的整个文档.

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

如果字段实际上没有改变,你只想插入一个新的数组元素,如果不存在完全相同的那个,那么你可以使用 $addToSet

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        $addToSet:{ 
            "recentviews": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

但是,如果您只是想通过单个键值"推送"到数组(如果不存在)那么您需要进行更多的手动处理,首先查看数组中的元素是否存在然后将$push语句放在何处它不是.

通过跟踪受更新影响的文档数量,您可以通过mongoose方法获得一些帮助:

Product.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    },
    function(err,numAffected) {

        if (numAffected == 0) {
            // Document not updated so you can push onto the array
            Product.update(
                { 
                    "_id": ObjectId("536c55bf9c8fb24c21000095")
                },
                { 
                    "$push": { 
                        "recentviews": {
                            "viewedby": "abc",
                            "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
                        }
                    }
                },
                function(err,numAffected) {

                }
            );
        }            

    }
);
Run Code Online (Sandbox Code Playgroud)

这里唯一需要注意的是,从MongoDB 2.6到早期版本的writeConcern消息中有一些实现更改.目前还不确定mongoose API如何实际实现numAffected回调中的参数返回,差异可能意味着什么.

在以前的版本中,即使您在初始更新中发送的数据与现有元素完全匹配并且不需要进行实际更改,1即使没有实际更新,也会返回"已修改"的数量.

从MongoDB 2.6开始,写入关注响应包含两部分.一部分显示修改后的文档,另一部分显示匹配.因此,当匹配现有元素的查询部分返回匹配时,实际修改的文档计数将返回,就0好像实际上不需要进行任何更改一样.

因此,根据实际在mongoose中实现返回数的方式,$addToSet在内部更新上使用运算符实际上可能更安全,以确保如果受影响的文档的原因不仅仅是已存在的确切元素.