我有一个像
这样的结构的mongodb集合[
{
name: "name1",
instances: [{value:1, score:2, date:<ISODate>},
{value:2, score:5, date:<ISODate>},
{value:2.5, score:9, date:<ISODate>},
...]
},
{
name: "name2",
instances: [{value:6, score:3, date:<ISODate>},
{value:1, score:6, date:<ISODate>},
{value:3.7, score:5.2, date:<ISODate>},
...]
},
...
]
我想查找实例的日期是否来自同一天的同一name
的两个(或更多)实例,并返回这些实例。
稍后我想删除除了其中一个实例之外的所有实例,但作为一个开始,我希望能够找到它们。
我尝试按日期汇总和分组,但无法弄清楚如何只比较当天(而不是整个日期)。
答案 0 :(得分:4)
假设您为了演示目的在测试集合中插入了以下测试文档:
db.test.insert([
{
"name" : "name1",
"instances" : [
{
"value" : 1,
"score" : 2,
"date" : ISODate("2015-03-04T00:00:00.000Z")
},
{
"value" : 2,
"score" : 5,
"date" : ISODate("2015-04-01T00:00:00.000Z")
},
{
"value" : 2.5,
"score" : 9,
"date" : ISODate("2015-03-05T00:00:00.000Z")
}
]
},
{
"name" : "name2",
"instances" : [
{
"value" : 6,
"score" : 3,
"date" : ISODate("2015-03-05T00:00:00.000Z")
},
{
"value" : 1,
"score" : 6,
"date" : ISODate("2015-03-04T00:00:00.000Z")
},
{
"value" : 3.7,
"score" : 5.2,
"date" : ISODate("2015-02-04T00:00:00.000Z")
}
]
},
{
"name" : "name1",
"instances" : [
{
"value" : 6,
"score" : 3,
"date" : ISODate("2015-03-05T00:00:00.000Z")
},
{
"value" : 1,
"score" : 6,
"date" : ISODate("2015-03-04T00:00:00.000Z")
},
{
"value" : 3.7,
"score" : 5.2,
"date" : ISODate("2015-02-04T00:00:00.000Z")
}
]
}
])
然后以下聚合将完成这项工作:
var pipeline = aggregate([
{
"$unwind": "$instances"
},
{
"$group": {
"_id": {
"name": "$name",
"year": {
"$year": "$instances.date"
},
"month": {
"$month": "$instances.date"
},
"day": {
"$dayOfYear": "$instances.date"
}
},
"count": {
"$sum": 1
},
"data": {
"$addToSet": "$$ROOT"
}
}
},
{
"$match": {
"count": {
"$gt": 1
}
}
},
{
"$unwind": "$data"
},
{
"$group": {
"_id": {
"name": "$data.name",
"_id": "$data._id"
}
}
},
{
"$project": {
"_id": "$_id._id",
"name": "$_id.name"
}
}
]);
db.test.aggregate(pipeline);
<强>输出强>:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("55506d0a180e849972939056"),
"name" : "name1"
},
{
"_id" : ObjectId("55506d0a180e849972939058"),
"name" : "name1"
}
],
"ok" : 1
}
上述聚合管道具有$unwind
操作作为第一步,它从输入文档解构instances
数组字段以输出每个元素的文档。每个输出文档都使用元素值替换数组。
下一个管道阶段$group
按"name"
,"instances.date"
字段对文档进行分组(使用 Date Aggregation Operators <将日期字段拆分为三个字段/ strong>),计算每个组的count
字段,并为每个唯一name
和date
(截至日期部分)输出文档。组data
中有一个额外的数组字段,它使用系统变量$$ROOT
来存储当前正在聚合管道阶段处理的原始根文档,即顶级文档。使用$addToSet
数组运算符将此根文档添加到数组中。
在管道的下方,您需要通过使用$match
管道按照名称和日期进行分组来过滤那些重复的文档,其中指定的计数应该大于1。
然后在data
字段上应用另一个$unwind
操作,以提取重复项的实际_id
和name
,这些副本将再次分组以进一步简化文档。
通过修改字段来构建最终文档结构需要额外的$project
管道阶段。
使用聚合结果游标然后使用forEach()
方法迭代结果并删除其他重复文档:
var cur = db.test.aggregate(pipeline);
cur.forEach(function (doc){
var count = 0;
if (count != 0){
db.test.remove({"_id": doc._id});
}
count++;
});
另一种选择是将$out
运算符作为最终的管道阶段,将聚合管道返回的文档写入指定的集合,然后可以查询并执行删除:
var cur = db.outputcollection.find();
cur.forEach(function (doc){
var count = 0;
if (count != 0){
db.test.remove({"_id": doc._id});
}
count++;
});
答案 1 :(得分:3)
如果我理解得那么你应该$unwind
然后$group
按日期和实例,filtering out只有一个文档的组。这样的事情(我现在无法访问MongoDB - 谨防拼写错误):
db.coll.aggregate([
{$unwind: "$instances"},
{$group: { _id: { name:"$name", day:{$dayOfYear:"$date"}, year:{$year:"$date"}}, count: {$sum: 1} }},
{$match: {count: {$gt: 1}}}
])