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

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

 


更多专业前端知识,请上 【猿2048】www.mk2048.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gradle入门:创建二进制分发

创建有用的应用程序之后,很可能我们想与其他人共享它。 一种方法是创建一个可以从我们的网站下载的二进制发行版。 这篇博客文章描述了如何满足以下要求的二进制发行版: 我们的二进制分发绝对不能使用所谓的“胖子”方法。 换句话说,我们的…

我的Google Adsense帐户被关

一、 上周四,我收到Google的邮件,宣布关闭我的Adsense帐户。 "您好! 查看了相关记录后,我们确认您的 AdSense 帐户存在引起无效活动的风险。保护 AdWords 广告客户,使其免受无效活动的侵害是我们的责任&#xff0…

网格布局之网格元素放置算法

接下来的网格元素放置算法将网格元素的自动位置解析为确定位置,确保每个网格元素具有布局明确的网格区域。(Grid spans 不需要特别的解析;如果没有明确指定,默认是1) 注意:当显式网格中没有位置放置自动放置…

csharp: ODP.NET,System.Data.OracleClient(.net 4.0) and System.Data.OleDb读取Oracle g 11.2.0的区别...

ODP.NET: 引用: using Oracle.DataAccess; //Oracle g 11.2.0 using Oracle.DataAccess.Client; using Oracle.DataAccess.Types; //下载 http://www.oracle.com/technetwork/topics/dotnet/downloads/net-downloads-160392.html //引用:D:\app\geovindu…

AngularJS快速入门指南15:API

API即Application Programming Interface(应用程序接口)。 AngularJS全局API AngularJS全局API是一组全局JavaScript函数,用来进行一些常用的操作,例如: 比较两个对象迭代对象进行数据格式转换 全局API函数可以通过an…

Java 9幕后花絮:新功能从何而来?

找出Java幕后发生的事情,以及新功能如何实现 在上一篇文章中,我们介绍了即将发布的Java 9版本的新功能和尚待解决的功能,并简要提到了将新功能添加到下一个版本之前要经历的过程。 由于此过程几乎影响了所有Java开发人员,但大多数…

TypeScript 联合类型(union type)

TS是JS的超集,在JS的基础上添加了一套类型系统,这样的TS可以被静态分析带来的好处显而易见。 let val: string val;声明一个string类型的变量val。 let val: string val; val 1; // Type number is not assignable to type string.因为number类型和…

sudo apt-get install libstdc++6

sudo apt-get install libstdc6 yum install libncurses.so.5 sudo apt-get install libncurses.so.5 sudo apt-get install lib32ncurses5 apt-get update把源更新一下 使用gdb时的指令 (gbd) info line *0x08xxxx sudo apt-get install lib32z1 lib32ncurses5 lib32bz2-1.…

AngularJS快速入门指南03:表达式

AngularJS通过表达式将数据绑定到HTML。 AngularJS表达式 AngularJS表达式写在双大括号中:{{ 表达式语句 }}。 AngularJS表达式绑定数据到HTML的方式与ng-bind指令的方式相同。 AngularJS会准确地将表达式“输出”为计算的结果。 AngularJS表达式与JavaScript表达式…

零基础快速上手HarmonyOS ArkTS开发2---ArkTS开发实践

ArkTS开发实践: 接着上一次零基础快速上手HarmonyOS ArkTS开发1---运行Hello World、ArkTS开发语言介绍继续, 在上一次对于ArkTS的基础知识进行了学习,依照官方的课程计划,还有两个具体的小案例需要来实践实践: 实践出…

无状态Spring安全性第2部分:无状态身份验证

Spring Stateless Security系列的第二部分是关于以无状态方式探索身份验证的方法。 如果您错过了CSRF的第一部分,可以在这里找到。 因此,在谈论身份验证时,其全部内容就是让客户端以可验证的方式向服务器标识自己。 通常,这始于服…

TypeScript 交叉类型(intersection type)

在TS中和联合类型(union type)对应的还有交叉类型(intersection type)。 交叉类型的出现主要为了组合多个对象类型(object type),因为相对于interface,object type没法继承,那么就可以通过union type来实现混合的目的,从而实现继承…

【转】JAVA中的转义字符

JAVA中转义字符: 1.八进制转义序列:\ 1到3位5数字;范围\000~\377 \0:空字符 2.Unicode转义字符:\u 四个十六进制数字;0~65535 \u0000:空字符 3.特殊字符:就3个 \"&#xff1a…

八、VueJs 填坑日记之参数传递及内容页面的开发

我们在上一篇博文中&#xff0c;渲染出来了一个列表&#xff0c;并在列表中使用了router-link标签&#xff0c;标签内的&#xff1a;to就是链接地址&#xff0c;昨天咱们是<router-link :to"/content/ i.id">这样写的&#xff0c;今天我们来完成内容页面的渲染…

为Kindeditor控件添加图片自动上传功能

Kindeditor是一款功能强大的开源在线HTML编辑器&#xff0c;支持所见即所得的编辑效果。它使用JavaScript编写&#xff0c;可以无缝地与多个不同的语言环境进行集成&#xff0c;如.NET、PHP、ASP、Java等。官方网站可以查看这里&#xff1a;http://kindeditor.net/index.php Ki…

TypeScript类型推论(Type Inference)

要完全理解类型推论需要完整理解类型上下文&#xff0c;并且理解TS对于是否可以使用类型推论是基于静态分析完成的。 上下文类型应用在许多地方。常见的例子包括函数调用的参数&#xff0c;赋值的右手端位置&#xff0c;类型断言&#xff0c;对象和数组的成员&#xff0c;和返回…

4个万无一失的技巧让您开始使用JBoss BRMS 6.0.3

上周&#xff0c;红帽发布了标记为6.0.3的JBoss BRMS的下一版本&#xff0c;已订阅的用户可以在其客户门户中使用。 如果您对该版本的新增功能感到好奇&#xff0c;请在客户门户网站上在线查看版本说明和其余文档 。 我们正在寻找一些简单的方法来开始使用此新版本&#xff0…

统一命名规则

1. #define 保护 所有头文件都应该使用 #define 防止头文件被多重包含, 命名格式当是:<PROJECT>_<PATH>_<FILE>_H_ 项目 SkinTK中的头文件 SkinTK/SkinTK/targetver.h 可按如下方式保护: #ifndef SKINTK_SKINTK_TARGETVER_H_ #define SKINTK_SKINTK_TARGETVE…

中国移动MM7 API用户手册(七)

4.4 VASP接收状态报告&#xff08;上行业务&#xff09;当VASP在发送MM7SubmitReq给MMSC时设置需要发送状态报告的请求为true时&#xff0c;MMSC在收到MM7SubmitReq后&#xff0c;会发送状态报告给VASP&#xff0c;此时VASP可以进行接收。接收方式和接收传送消息一样&#xff…

如何仅通过CSS实现多行文本超长自动省略号

在CSS中&#xff0c;我们可以通过下面的样式实现DIV元素中文本超长后自动截断并以省略号结尾&#xff1a; overflow: hidden;word-break: normal;text-overflow: ellipsis; text-overflow: ellipsis是实现文本截断后以省略号结尾的关键样式&#xff0c;但问题是如果添加该样式则…