我们知道,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来完成。

使用aggregate在MongoDB中查找重复的数据记录的更多相关文章

  1. Excel中如何在两个工作表中查找重复数据

    有时我们可能会在两种工作表中查找重复记录,当数据记录很多时,就必须通过简单的方法来实现.下面小编就与大家一起分享一下查看重复记录数据的方法,希望对大家有所帮助. 方法/步骤   为了讲解的需要,小编特 ...

  2. Java实现 LeetCode 609 在系统中查找重复文件(阅读理解+暴力大法)

    609. 在系统中查找重复文件 给定一个目录信息列表,包括目录路径,以及该目录中的所有包含内容的文件,您需要找到文件系统中的所有重复文件组的路径.一组重复的文件至少包括二个具有完全相同内容的文件. 输 ...

  3. 在scrapy中过滤重复的数据

    当为了确保爬到的数据中没有重复的数据的时候,可以实现一个去重的item pipeline 增加构造器方法,在其中初始化用于对与书名的去重的集合 在process_item方法中,先取出item中要判断 ...

  4. MongoDB中的映射,限制记录和记录拼排序 文档的插入查询更新删除操作

    映射 在 MongoDB 中,映射(Projection)指的是只选择文档中的必要数据,而非全部数据.如果文档有 5 个字段,而你只需要显示 3 个,则只需选择 3 个字段即可. find() 方法 ...

  5. SQL SERVER按多字段查找重复的数据并删除只保留一条

    由于一次操作失误,给表中插入了多条重复的数据,所以就需要删除重复的数据只保留一条,一时想不到好方法,各种查资料,终于找到了,特意写到这里,方便以后自己用~ 查询: select A.n_PatentI ...

  6. Python从MongoDB中按天读取数据并格式化日志

    #$cat SpeechMongoHandle.py from pymongo import Connection import time import datetime # CTRL_A='\x01 ...

  7. sql中去除重复的数据 select distinct * from table

    总的思路就是先找出表中重复数据中的一条数据,插入临时表中,删除所有的重复数据,然后再将临时表中的数据插入表中.所以重点是如何找出重复数据中的一条数据,有三种情况 1.重复数据完全一样,使用distin ...

  8. python删除列表中得重复得数据

    解决思想:将列表转换为 集合,利用集合删除重复数据得特性删除重复数据,然后将集合转换为列表 #删除列表中得重复元素 def delect_1 (lt): s = set(lt) lt = list(s ...

  9. c# 如何中List<object>中去掉object对象中的重复列数据?

    //去掉重复 var title = modelList.GroupBy(m => m.Title.ToLower().Trim()).Select(m => new { ID = m.F ...

随机推荐

  1. 《第一本docker书》- 第一章笔记

    环境: Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-30-generic i686) 第一章: 1 Docker客户端和服务器 2 Docker镜像 添加一个文件,执行一 ...

  2. easyui form表单提交应注意的问题

    今天在一个项目中用到表单提交,代码如下: $('#CreateForm').form('submit', { onSubmit: function () { ajaxCreateFrom(this, ...

  3. DOM查找元素

    1. 查找元素5种: 1. 按id查找1个元素对象: var elem=document.getElementById("id值"); 何时使用:1. 元素必须有id 2. 精确查 ...

  4. Java File创建新目录和文件

    创建目录 当不存在目录aa文件夹时: File file1=new File("/aa"); Boolean aa=file.mkdir();// true File file1= ...

  5. <%%>标签 什么意思

    <%%>是说这里面的文本不是普通直接输出到客户端的文本,而是需要服务器来解释的.不光是可以写JAVA脚本,这要看具体服务器端使用的是什么技术ASP/JSP/PHP/ASP.NET都使用相同 ...

  6. 删除从第i个位置开始,长度为len的子串

    /*字符串采用带头结点的链表存储,设计算法函数void delstring(linkstring s, int i,int len)在字符串s中删除从第i个位置开始,长度为len的子串.*//**** ...

  7. MVC4相关Razor语法浅谈

    1._LayOut.cshtml 文件为mvc的布局文件,里面包函的是htm的静态文件,作为mvc其他view的基础母版使用,子视图要不想想调用它只需在页面设置@{Layout=null;}即可,现对 ...

  8. Java中将0x开头的十六进制字符串转换成十进制整数

    1.Integer.toString(int i) 由于input(输入数据)是以0x开头的字符串,并不是整型.因而在用 String s = Integer.toString(input); 时用会 ...

  9. 使用Maven自动部署Tomcat 6和Tomcat 7下Web应用

    使用Maven自动部署Tomcat 6和Tomcat 7下Web应用 开启Tomcat远程管理权限 在tomcat的config目录下的tomcat-users.xml文件, <role rol ...

  10. css长度值与颜色值

    颜色值 在网页中的颜色设置是非常重要的,有字体颜色,背景颜色,边框颜色等,设置颜色的方法也有很多种: 1.英文命令颜色 p{color:red;} 2.rgb颜色 p{color:rgb(133,45 ...