一尘不染

MongoDB-$ set更新或推送Array元素

node.js

在产品集合中,我有一个nearviews数组,其中有2个fieldsbyBy和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中的所有其他元素。


阅读 451

收藏
2020-07-07

共1个答案

一尘不染

实际上,按照您说的去做并不是一个单一的操作,但是我将逐步介绍所需的步骤,以完成此操作或覆盖其他可能的情况。

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

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

声明它不是。

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

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消息中有一些实现更改。现在不确定猫鼬API如何实际实现numAffected回调中参数的返回,这可能意味着一些不同。

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

从MongoDB
2.6开始,写关注响应包含两个部分。一部分显示修改后的文档,另一部分显示匹配项。因此,尽管匹配部分将由与现有元素匹配的查询部分返回,但实际修改后的文档数将返回,0好像实际上不需要进行任何更改一样。

因此,根据在猫鼬中实际实现返回号的方式,
$addToSet

在内部更新中使用运算符实际上可能更安全,以确保受影响的文档为零的原因不仅在于确切的元素已经存在。

2020-07-07