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

时间:2014-06-09 06:52:21

标签: javascript node.js mongodb mongoose mongodb-query

在产品系列中,我有一系列近期视图,其中有2个字段被浏览过& amp; viewedDate。

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

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

用户为abc,因此我需要更新上述&如果abc我没有记录$push

我尝试$set如下: -

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

以上查询将删除Array中的所有其他元素。

1 个答案:

答案 0 :(得分:17)

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

您正在寻找的是positional $运算符的一部分。您需要部分查询来“查找”所需数组的元素。

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": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

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

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

但是,如果您只是想通过单个键值“推送”到数组(如果不存在)那么您需要进行更多的手动处理,首先查看数组中的元素是否存在然后制作它不是$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) {

                }
            );
        }            

    }
);

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

在以前的版本中,即使您在初始更新中发送的数据与现有元素完全匹配且没有实际需要更改,那么“已修改”金额将返回为1,即使实际上没有更新任何内容

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

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