【问题标题】:MongoDB - $set to update or push Array elementMongoDB - $set 更新或推送数组元素
【发布时间】:2014-07-29 16:29:17
【问题描述】:

在产品集合中,我有一个最近视图数组,其中包含 2 个字段viewedBy 和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 中的所有其他元素。

【问题讨论】:

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


    【解决方案1】:

    实际上做你所说的事情并不是一个单一的操作,但我将介绍执行此操作所需的部分或以其他方式涵盖其他可能的情况。

    您正在寻找的部分是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 运算符实际上可能更安全,以确保零受影响文档的原因不仅仅是确切的元素已经存在。

    【讨论】:

    • 你知道这和mongo 4.2是否一样吗?
    猜你喜欢
    • 2016-02-25
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 2016-01-20
    • 1970-01-01
    相关资源
    最近更新 更多