I have this document and i need remove only 3 items with name "Test" using only 1 request.
{ "_id" : 1, "items" : ["Test, "Test", "Test", "Test", "Test", "Sword", "Sword]}
It must become { "_id" : 1, "items" : ["Test", "Test", "Sword", "Sword]}
after request.
CodePudding user response:
Maybe something like this:
db.collection.aggregate([
{
"$unwind": "$items"
},
{
$group: {
_id: "$items",
cnt: {
$sum: 1
},
it: {
$push: "$items"
},
origid: {
"$first": "$_id"
}
}
},
{
$project: {
_id: "$origid",
items: {
$slice: [
"$it",
0,
2
]
}
}
},
{
$group: {
_id: "$_id",
items: {
$push: "$items"
}
}
},
{
$addFields: {
items: {
"$reduce": {
"input": "$items",
"initialValue": [],
"in": {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
}
}
}
])
Explained:
- unwind the items array so you can group after
- group by item so you get the repeating values
- project with slice to remove more then 2x items in array
- group to form back the document but without the >2 repeating values
- project with concatArrays to concat arrays of items.
CodePudding user response:
You can use this aggregation to get the desired result. The query uses the $reduce
Aggregate Array Operator to iterate over the items
array, and discards the first 3 matching "Test" items.
db.collection.aggregate([
{
$set: {
items: {
$reduce: {
input: "$items",
initialValue: { result: [], count: 0 },
in: {
$cond: [ { $and: [ { $eq: [ "$$this", "Test" ] }, { $lt: [ "$$value.count", 3 ] } ] },
{ result: "$$value.result", count: { $add: [ "$$value.count", 1 ] } },
{ result: { $concatArrays: [ "$$value.result", [ "$$this" ] ] }, count: "$$value.count" }
]
}
}
}
}
},
{
$set: {
items: "$items.result"
}
}
])