$map
и $range
на помощь. Вместе они позволяют создавать «петли» для массивов. Учитывая входной набор, подобный этому:
[
{_id:0,
"data": [
{ "status": "status_A", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_C", "other": "data" },
{ "status": "status_D", "other": "data" }
]
}
,{_id:1,
"data": [
{ "status": "status_A", "other": "data" },
{ "status": "status_X", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_X", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_D", "other": "data" },
{ "status": "status_D", "other": "data" }
]
}
]
, тогда этот конвейер будет определять, где в массиве data
поле status
дублируется в элементах n
и n+1
:
db.foo.aggregate([
{$project: {dupe: {$map: {
input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
as: "z",
in: {$cond: [ {$eq: [
// Important trick: $arrayElemAt[array,n] will give you the whole object at offset n.
// $arrayElemAt[arr plus dotpath into object, n] will give you just the field at the
// dotpath; in our case here, status is a single scalar string:
{$arrayElemAt: ["$data.status", "$$z"]},
{$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
]},
{$arrayElemAt: ["$data.status", "$$z"]},null]}
}}
}}
]);
, чтобы получить:
{ "_id" : 0, "dupe" : [ null, null, null ] }
{
"_id" : 1,
"dupe" : [
null,
null,
"status_B",
"status_B",
null,
null,
"status_B",
null,
"status_D"
]
}
Некоторые могут найти конвейер проще, устанавливая переменные с помощью $let
:
db.foo.aggregate([
{$project: {dupe: {$map: {
input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
as: "z",
in: {$let: {
vars: { n0: {$arrayElemAt: ["$data.status", "$$z"]},
n1: {$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
},
in: {$cond: [ {$eq: [ "$$n0", "$$n1" ]}, "$$n0", null ]}
}}
}}
}}
]);
Если вы хотите более простой результат, который говорит, есть ли статус n / n + 1 дублируется для любого количества раз, используйте $anyElementTrue
, чтобы получить простой логический вывод:
db.foo.aggregate([
{$project: {dupe: {$anyElementTrue: { $map: {
input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
as: "z",
in: {$cond: [ {$eq: [
{$arrayElemAt: ["$data.status", "$$z"]},
{$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
]},
{$arrayElemAt: ["$data.status", "$$z"]},null]}
}}
}
}}
]);
, чтобы получить:
{ "_id" : 0, "dupe" : false }
{ "_id" : 1, "dupe" : true }