本文介绍了MongoDB - $set 更新或推送数组元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在产品集合中,我有一个最近查看的数组,其中包含 2 个字段 viewsBy &查看日期.

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

"recentviews" : [{"viewedby": "abc",查看日期":ISODate(2014-05-08T04:12:47.907Z")}]

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

我试过 $set 如下:-

db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },{ $设置:{最近浏览":{浏览者:'abc',查看日期:ISODate("2014-05-09T04:12:47.907Z")}}})

上述查询删除了我在 Array 中的所有其他元素.

解决方案

实际上,按照您所说的去做,并不是一个单一的操作,但我将介绍执行此操作或其他操作所需的部分涵盖其他可能的情况.

您正在寻找的部分是位置$ 运算符.您还需要查询的一部分来查找"您想要的数组元素.

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

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

db.products.update({"_id": ObjectId("536c55bf9c8fb24c21000095"),"recentviews.viewedby": "abc"},{$set":{最近浏览量.$":{"viewedby": "abc",查看日期":ISODate(2014-05-09T04:12:47.907Z")}})

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

db.products.update({"_id": ObjectId("536c55bf9c8fb24c21000095"),"recentviews.viewedby": "abc"},{$addToSet:{最近浏览":{"viewedby": "abc",查看日期":ISODate(2014-05-09T04:12:47.907Z")}})

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

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

Product.update({"_id": ObjectId("536c55bf9c8fb24c21000095"),"recentviews.viewedby": "abc"},{$set":{最近浏览量.$":{"viewedby": "abc",查看日期":ISODate(2014-05-09T04:12:47.907Z")}},功能(错误,numAffected){如果(numAffected == 0){//文档未更新,因此您可以推送到数组产品.更新({"_id": ObjectId("536c55bf9c8fb24c21000095")},{$push":{最近浏览":{"viewedby": "abc",查看日期":ISODate(2014-05-09T04:12:47.907Z")}}},功能(错误,numAffected){});}});

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

在以前的版本中,即使您在初始更新中发送的数据与现有元素完全匹配并且不需要真正的更改,那么修改"的数量也会作为 1 返回,即使什么都没有实际更新了.

从 MongoDB 2.6 开始,写关注响应包含两部分.一部分显示修改后的文档,另一部分显示匹配.因此,虽然匹配将由匹配现有元素的查询部分返回,但如果实际上不需要更改,则实际修改的文档计数将返回为 0.

因此,根据返回编号在 mongoose 中实际实现的方式,使用 $addToSet 操作符对内部更新进行操作,以确保受影响的文档为零的原因不仅仅是确切的元素已经存在.>

In products collection, i have an Array of recentviews which has 2 fields viewedBy & viewedDate.

In a scenario if i already have a record with viewedby, then i need to update it. For e.g if i have array like this :-

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

And user is abc, so i need to update the above & if there is no record for abc i have to $push.

I have tried $set as follows :-

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

The above query erases all my other elements in Array.

解决方案

Actually doing what it seems like you say you are doing is not a singular operation, but I'll walk through the parts required in order to do this or otherwise cover other possible situations.

What you are looking for is in part the positional $ operator. You need part of your query to also "find" the element of the array you want.

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

So the $ stands for the matched position in the array so the update portion knows which item in the array to update. You can access individual fields of the document in the array or just specify the whole document to update at that position.

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

If the fields do not in fact change and you just want to insert a new array element if the exact same one does not exist, then you can use $addToSet

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

However if you are just looking for for "pushing" to an array by a singular key value if that does not exist then you need to do some more manual handling, by first seeing if the element in the array exists and then making the $push statement where it does not.

You get some help from the mongoose methods in doing this by tracking the number of documents affected by the update:

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

                }
            );
        }            

    }
);

The only word of caution here is that there is a bit of an implementation change in the writeConcern messages from MongoDB 2.6 to earlier versions. Being unsure right now as to how the mongoose API actually implements the return of the numAffected argument in the callback the difference could mean something.

In prior versions, even if the data you sent in the initial update exactly matched an existing element and there was no real change required then the "modified" amount would be returned as 1 even though nothing was actually updated.

From MongoDB 2.6 the write concern response contains two parts. One part shows the modified document and the other shows the match. So while the match would be returned by the query portion matching an existing element, the actual modified document count would return as 0 if in fact there was no change required.

So depending on how the return number is actually implemented in mongoose, it might actually be safer to use the $addToSet operator on that inner update to make sure that if the reason for the zero affected documents was not just that the exact element already existed.

这篇关于MongoDB - $set 更新或推送数组元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-26 08:40