Предположим, у вас есть следующие тестовые документы, вставленные в тестовой коллекции для демонстрационных целей:
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), вычисляет count
поля для каждой группы, и выводит документ для каждого уникального name
и date
(до дня часть). В группе data
имеется дополнительное поле массива, которое использует системную переменную $$ROOT
для хранения исходного корневого документа, то есть документа верхнего уровня, который в настоящее время обрабатывается на этапе конвейерной агрегирования. Этот корневой документ добавляется в массив с помощью оператора массива $addToSet
.
Далее по трубопроводу вам необходимо будет отфильтровать те документы, которые являются дубликатами, сгруппированы по имени и дате с использованием конвейера $match
с указанными критериями, что счетчик должен быть больше одного.
Другой $unwind
операция затем наносят на data
поле, чтобы извлечь реальную _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++;
});
Претензии для обозначения '$ addToSet': я просто пропустил эту часть. Но почему бы просто не использовать '' $ addToSet ":" $ _id "'? Есть ли что-то очевидное, что я тоже там пропустил? –
@SylvainLeroux Это отличное предложение, хотя я не буду испытывать соблазн включить только поле '$ _id' еще в том случае, если OP может понадобиться другие поля, а также дальше по конвейеру, но да, просто для получения дубликатов, которые я делаю согласитесь, достаточно только поля '_id'. Спасибо, что заметили! – chridam
Большое спасибо вам обоим. Я многому научился. – yarons