MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。
aggregate() 方法
MongoDB中聚合的方法使用aggregate()。
语法
aggregate() 方法的基本语法格式如下所示:
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
我们知道,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" : "15827577345", "actId" : ObjectId("5694565fa50fea7705f01789") }, "uniqueIds" : [ ObjectId("569b5d03b3d206f709f97685"), ObjectId("569b5d01b3d206f709f97684") ], "count" : 2.0000000000000000 }, { "_id" : { "cellPhone" : "18171282716", "actId" : ObjectId("566b0d8dc02f61ae18e68e48") }, "uniqueIds" : [ ObjectId("566d16e6cf86d12d1abcee8b"), ObjectId("566d16e6cf86d12d1abcee8a") ], "count" : 2.0000000000000000 } ], "ok" : 1.0000000000000000 }
从结果中可以看到,一共有两组数据相同的记录,所以返回的result数组的长度为2。uniqueIds属性为一个数组,其中存放了重复记录的_id字段的值,通过该值我们可以使用remove()方法来查找并删除对应的数据。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。