• 使用aggregate在MongoDB中查找重复的数据记录


     

      http://www.cnblogs.com/jaxu/p/5143707.html
     

    db.slice.aggregate([
    { $group: {
    _id: { UserId: "$UserId", NodeKey: "$NodeKey" ,DocumentId:"$DocumentId",SliceIndex:"$SliceIndex" },
    uniqueIds: { $addToSet: "$_id" },
    count: { $sum: 1 }
    }},
    { $match: {
    count: { $gt: 1 }
    }}
    ])

      

     
     

      我们知道,MongoDB属于文档型数据库,其存储的文档类型都是JSON对象。正是由于这一特性,我们在Node.js中会经常使用MongoDB进行数据的存取。但由于Node.js是异步执行的,这就导致我们无法保证每一次的数据库save操作都是原子型的。也就是说,如果客户端连续两次发起同一事件将数据存入数据库,很可能会导致数据被重复保存。高并发的情况下,哪怕是你在代码中已经做了非常严格的校验,例如插入数据前判断要保存的数据是否已经存在,但仍然有可能会出现数据被重复保存的风险。因为在异步执行中,你没有办法保证哪个线程先执行,哪个线程后执行,客户端发起的所有请求并非按我们想象的都是顺序执行的。一个较好的解决办法是在Mongo数据库的所有表中创建唯一索引。事实上,MongoDB默认会为所有表创建一个_id字段的唯一索引(可以取消)。如果你想在Node.js中通过mongoose.schema来自动创建索引,可以参考下面的代码:

    复制代码
    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    
    var customerSchema = new mongoose.Schema({
        cname: String,
        cellPhone, String,
        sender: String,
        tag: String,
        behaviour: Number,
        createTime: {
            type: Date,
            default: Date.now
        },
        current:{
            type: Boolean,
            default: true
        }
    }, {
        versionKey: false
    });
    
    customerSchema.index({cname:1,cellPhone:1,sender:1,tag:1,behaviour:1}, {unique: true});

    module.exports = mongoose.model('customer', customerSchema);
    复制代码

      上面的model中我们定义了表customer的结构,并通过index()方法在字段cname,cellPhone,sender,tag,behaviour上创建了唯一索引,这样当包含这些字段的重复数据被插入时,数据库会抛出异常。借用mongoose,如果数据库表之前已经被创建并且程序正在运行中,当我们修改model并添加索引,然后重新启动app,只要有对该model的访问,mongoose会自动进行检测并创建索引。当然,如果数据出现重复,则索引创建会失败。此时我们可以通过在创建索引时添加dropDups选项,让数据库自动将重复的数据删除,如:

    customerSchema.index({cname:1,cellPhone:1,sender:1,tag:1,behaviour:1}, {unique: true, dropDups: true});

      不过据MongoDB的官方说明,自3.0以后的版本不再使用该选项,而且也并没有提供替代的解决办法。貌似官方不再提供创建索引时自动删除重复记录的功能。那如何才能快速有效地找出重复的记录并且删除呢?首先我们要找出这些记录,然后通过remove()方法进行删除。下面的查询语句可以找出给定字段有重复数据的记录:

    复制代码
    db.collection.aggregate([
      { $group: { 
        _id: { firstField: "$firstField", secondField: "$secondField" }, 
        uniqueIds: { $addToSet: "$_id" },
        count: { $sum: 1 } 
      }}, 
      { $match: { 
        count: { $gt: 1 } 
      }}
    ])
    复制代码

      替换_id属性的值以指定你想要进行判断的字段。相应地,在Node.js中代码如下:

    复制代码
    var deferred = Q.defer();
    var group = { firstField: "$firstField", secondField: "$secondField"};
    
    model.aggregate().group({
        _id: group,
        uniqueIds: {$addToSet: '$_id'},
        count: {$sum: 1}
    }).match({ count: {$gt: 1}}).exec(deferred.makeNodeResolver());
    
    return deferred.promise;
    复制代码

      上述代码使用了Q来替换函数执行中的回调。在Node.js的异步编程中,使用Q来处理回调是个不错的选择。

      下面是返回的结果:

    复制代码
    /* 1 */
    {
        "result" : [ 
            {
                "_id" : {
                    "cellPhone" : "15827571111",
                    "actId" : ObjectId("5694565fa50fea7705f01789")
                },
                "uniqueIds" : [ 
                    ObjectId("569b5d03b3d206f709f97685"), 
                    ObjectId("569b5d01b3d206f709f97684")
                ],
                "count" : 2.0000000000000000
            }, 
            {
                "_id" : {
                    "cellPhone" : "18171282222",
                    "actId" : ObjectId("566b0d8dc02f61ae18e68e48")
                },
                "uniqueIds" : [ 
                    ObjectId("566d16e6cf86d12d1abcee8b"), 
                    ObjectId("566d16e6cf86d12d1abcee8a")
                ],
                "count" : 2.0000000000000000
            }
        ],
        "ok" : 1.0000000000000000
    }
    复制代码

      从结果中可以看到,一共有两组数据相同的记录,所以返回的result数组的长度为2。uniqueIds属性为一个数组,其中存放了重复记录的_id字段的值,通过该值我们可以使用remove()方法来查找并删除对应的数据。

    补充:Mongoose支持findOneAndUpdate(在MongoDB中对应的方法叫findAndModify),选项upsert=true表示当要要更新的数据不存在时会自动创建。该选项默认值为false。示例代码如下:

    复制代码
    var query = {'username':req.user.username};
    req.newData.username = req.user.username;
    MyModel.findOneAndUpdate(query, req.newData, {upsert:true}, function(err, doc){
        if (err) return res.send(500, { error: err });
        return res.send("succesfully saved");
    });
    复制代码

      通过该方法我们可以将数据的唯一性校验交给MongoDB来完成。

  • 相关阅读:
    ORA-01045: user XXZY lacks CREATE SESSION privilege; logon denied
    ORA-31626:作业不存在 ORA-31633:无法创建主表"XXX.SYS_IMPORT_FULL_05"
    HTTP 错误 401.3
    mysql 简单游标
    mysql 多重游标嵌套
    表单校验 “灰白字提示”
    Eclipse连接mysql数据库出现问题
    虚拟机无法使用桥接,没有未桥接网络适配器解决办法
    每周进度条(16)
    人月神话阅读笔记06
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/10219191.html
Copyright © 2020-2023  润新知