【问题标题】:Add elements to array using $addtoset in mongodb在 mongodb 中使用 $addtoset 将元素添加到数组
【发布时间】:2017-06-29 12:39:23
【问题描述】:

我正在尝试使用 addtoset 更新集合中的数组的示例。正在添加新元素,但未按预期添加。根据addtoset,只有当它不在列表中时才会添加新元素。

问题:

它只是获取正在添加的任何元素。

这是我的代码示例

架构(mongo_database.js):

    var category = new Schema({
    Category_Name: { type: String, required: true},
    //SubCategories: [{}]
    Category_Type: { type: String},
    Sub_Categories: [{Sub_Category_Name: String, UpdatedOn: { type:Date, default:Date.now} }],
    CreatedDate: { type:Date, default: Date.now},
    UpdatedOn: {type: Date, default: Date.now}

});

service.js

exports.addCategory = function (req, res){
//console.log(req.body);
    var category_name = req.body.category_name;
    var parent_category_id = req.body.parent_categoryId;


            console.log(parent_category_id);    
            var cats = JSON.parse('{ "Sub_Category_Name":"'+category_name+'"}');
            //console.log(cats);
            var update = db.category.update(
                { 
                    _id: parent_category_id
                },
                { 
                    $addToSet: { Sub_Categories: cats}
                },
                {
                    upsert:true
                }
            );

            update.exec(function(err, updation){

            })
    }

有人可以帮我解决这个问题吗?

非常感谢..

【问题讨论】:

  • $addToSet 适用于完全匹配,因此因为这些元素还包含 UpdatedAt 字段,所以它们不会匹配并且将始终被添加。有关类似问题,请参阅 this question,但它不适用于 upsert,因此它不是完全重复的。

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


【解决方案1】:

如前所述,$addToSet 不能以这种方式工作,因为数组或“集合”中的元素旨在真正代表一个“集合”,其中每个元素都是完全唯一的。此外,.update() 等操作方法不考虑 mongoose 架构默认或验证规则。

但是,.update() 等操作比“查找”文档,然后操作和使用 .save() 来更改客户端代码要有效得多。它们还避免了其他进程或事件操作可能在检索文档后对其进行修改的并发问题。

要执行您想要的操作,需要向服务器发出“多个”更新语句。这是一种“回退”逻辑情况,当一个操作未更新文档时,您回退到下一个操作:

models/category.js

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var category = new Schema({
    Category_Name: { type: String, required: true},
    Category_Type: { type: String},
    Sub_Categories: [{Sub_Category_Name: String, UpdatedOn: { type:Date, default:Date.now} }],
    CreatedDate: { type:Date, default: Date.now},
    UpdatedOn: {type: Date, default: Date.now}
});

exports.Category = mongoose.model( "Category", category );

在您的代码中

var Category = require('models/category').Category;

exports.addCategory = function(req,res) {
    var category_name = req.body.category_name;
    var parent_category_id = req.body.parent_categoryId;

    Category.update(
        { 
            "_id": parent_category_id, 
            "Sub_Categories.Sub_Category_Name": category_name
        },
        {
            "$set": { "Sub_Categories.$.UpdatedOn": new Date() }
        },
        function(err,numAffected) {
           if (err) throw error;     // or handle

           if ( numAffected == 0 )
               Category.update(
                   {
                       "_id": parent_category_id, 
                       "Sub_Categories.Sub_Category_Name": { "$ne": category_name }
                   },
                   {
                       "$push": {
                           "Sub_Categories": {
                               "Sub_Category_Name": category_name,
                               "UpdatedOn": new Date()
                           }
                       }
                   },
                   function(err,numAffected) {
                       if (err) throw err;     // or handle

                       if ( numAffected == 0 )
                           Category.update(
                               {
                                   "_id": parent_category_id
                               },
                               { 
                                   "$push": {
                                       "Sub_Categories": {
                                           "Sub_Category_Name": category_name,
                                           "UpdatedOn": new Date()
                                       }
                                   }
                               },
                               { "$upsert": true },
                               function(err,numAffected) {
                                   if (err) throw err;
                               }
                           );
                   });
               );
        }
    );                    
};

基本上尝试了三种可能的操作:

  1. 尝试匹配存在类别名称的文档并更改匹配数组元素的“UpdatedOn”值。

  2. 如果没有更新。查找与 parentId 匹配但类别名称不存在于数组中的文档并推送新元素。

  3. 如果没有更新。执行一个尝试匹配 parentId 的操作,并将 upsert 设置为 true 的数组元素推送。由于之前的两次更新都失败了,这基本上是一个插入。

您可以通过使用 async.waterfall 之类的方法来清除它,以传递 numAffected 值并避免缩进蔓延,或者根据我个人的偏好,不费心检查受影响的值并一次传递所有语句通过Bulk Operations API 到服务器。

后者可以像这样从猫鼬模型中访问:

var ObjectId = mongoose.mongo.ObjectID,
   Category = require('models/category').Category;

exports.addCategory = function(req,res) {
    var category_name = req.body.category_name;
    var parent_category_id = req.body.parent_categoryId;


    var bulk = Category.collection.initializeOrderBulkOp();

    // Reversed insert
    bulk.find({ "_id": { "$ne": new ObjectId( parent_category_id ) })
        .upsert().updateOne({
            "$setOnInsert": { "_id": new ObjectId( parent_category_id ) },
            "$push": {
                "Sub_Category_Name": category_name,
                "UpdatedOn": new Date()
            }
        });

    // In place
    bulk.find({ 
        "_id": new ObjectId( parent_category_id ), 
        "Sub_Categories.Sub_Category_Name": category_name
    }).updateOne({
        "$set": { "Sub_Categories.$.UpdatedOn": new Date() }
    });

    // Push where not matched
    bulk.find({
        "_id": new ObjectId( parent_category_id ), 
        "Sub_Categories.Sub_Category_Name": { "$ne": category_name }
    }).updateOne({
        "$push": {
            "Sub_Category_Name": category_name,
            "UpdatedOn": new Date()
        }
    });

    // Send to server
    bulk.execute(function(err,response) {
        if (err) throw err;    // or handle
        console.log( JSON.stringify( response, undefined, 4 ) );
    });
};

注意“upsert”首先出现的相反逻辑,但如果成功,则只有“second”语句将适用,但实际上在 Bulk API 下,这不会影响文档。您将获得一个WriteResult 对象,其基本信息与此类似(简略形式):

{ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }

或者在“upsert”上:

{
    "nMatched" : 1,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("54af8fe7628bee196ce97ce0")
}

还要注意需要包含来自基本 mongo 驱动程序的 ObjectId 函数,因为这是来自基本驱动程序的“原始”方法,它不像 mongoose 方法那样基于架构“自动转换”。

另外要非常小心,因为它是一个基本驱动方法并且不共享猫鼬逻辑,所以如果没有建立到数据库的连接,那么调用.collection访问器将不会返回Collection对象和随后的方法调用失败。 Mongoose 本身对数据库连接进行“惰性”实例化,并且方法调用被“排队”,直到连接可用。基本驱动程序方法并非如此。

所以可以做到,只是您需要自己处理此类数组处理的逻辑,因为没有本地运算符可以做到这一点。但是,如果您采取适当的措施,它仍然非常简单且非常有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 2022-01-19
    • 2018-04-30
    相关资源
    最近更新 更多