I have a Mongoose Schema that looks like this:
{
_id: ObjectID,
storage: [{
location: String,
storedFood: [{
code: String,
name: String,
weight: Number
}]
}]
}
And for example in storedFood can be the same Food twice. But I only want to update one of the weights of these items. This is my code to $inc all of the items.... How can I reduce this to only one?
try{
const deletedFoodFromStorage = await User.updateOne(
{_id: user, "storage.location": location},
{ $inc: {"storage.$.storedFood.$[food].weight": -weight}},
{ arrayFilters: [ { "food.code": code } ]},
);
res.json(deletedFoodFromStorage);
}catch(err){
res.status(400).json('Error: ' err)
}
CodePudding user response:
Should have been a simple one. Only way I found at the moment is not simple:
db.collection.update(
{_id: user, "storage.location": location},
[
{$set: {
newItem: {
$reduce: {
input: {$getField: {
input: {$first: {$filter: {
input: "$storage",
as: "st",
cond: {$eq: ["$$st.location", location]}
}}},
field: "storedFood"
}},
initialValue: [],
in: {$concatArrays: [
"$$value",
{$cond: [
{$and: [
{$eq: ["$$this.code", code]},
{$not: {$in: [code, "$$value.code"]}}
]},
[{$mergeObjects: [
"$$this",
{weight: {$subtract: ["$$this.weight", weight]}}
]}],
["$$this"]
]
}
]
}
}
}
}},
{$set: {
storage: {
$map: {
input: "$storage",
in: {$cond: [
{$eq: ["$$this.location", location]},
{$mergeObjects: ["$$this", {storedFood: "$newItem"}]},
"$$this"
]}
}
},
newItem: "$$REMOVE"
}}
])
See how it works on the playground example
CodePudding user response:
Borrowing liberally from nimrod serok's answer, here's one way to do it with a single pass through all the arrays. I suspect this can be improved, at least for clarity.
db.collection.update({
_id: user,
"storage.location": location
},
[
{
"$set": {
"storage": {
"$map": {
"input": "$storage",
"as": "store",
"in": {
"$cond": [
{"$ne": ["$$store.location", location]},
"$$store",
{
"$mergeObjects": [
"$$store",
{
"storedFood": {
"$getField": {
"field": "theFoods",
"input": {
"$reduce": {
"input": "$$store.storedFood",
"initialValue": {
"incOne": false,
"theFoods": []
},
"in": {
"$cond": [
"$$value.incOne",
{
"incOne": "$$value.incOne",
"theFoods": {
"$concatArrays": [
"$$value.theFoods",
["$$this"]
]
}
},
{
"$cond": [
{"$ne": ["$$this.code", code]},
{
"incOne": "$$value.incOne",
"theFoods": {
"$concatArrays": [
"$$value.theFoods",
["$$this"]
]
}
},
{
"incOne": true,
"theFoods": {
"$concatArrays": [
"$$value.theFoods",
[
{
"$mergeObjects": [
"$$this",
{"weight": {"$add": ["$$this.weight", -weight]}}
]
}
]
]
}
}
]
}
]
}
}
}
}
}
}
]
}
]
}
}
}
}
}
])
Try it on mongoplayground.net.