It doesn't update and doesn't show any errors - mongodb

I have a structure that looks like this:
{
_id: 10,
line_items: [
{
_id: 2,
name: "name",
quantity: 2,
},
{
_id: 3,
name: "name2",
quantity: 1,
}
],
sub_total: 100
}
And i'm trying to do an update:
query={_id: 10, 'line_items.$._id': 2}
db.orders.update(query, {$push: {$inc: {'line_items.$.quantity': 1}}, $inc: {sub_total: 32}})
But it doesn't do anything and doesn't show any errors. What's wrong?

There are several issues with your attempt:
you need to use $elemMatch when querying array fields for your case
your $push is incorrect. you can simply use $inc
Here is a working solution:
db.collection.update({
_id: 10,
line_items: {
$elemMatch: {
_id: 2
}
}
},
{
$inc: {
"line_items.$.quantity": 1,
sub_total: 32
}
})
Here is the Mongo playground for your reference.

Related

Projection and group on nested object mongodb aggregation query

How to get the nested object in projection and group in mongodb aggregate query.
[
{
city: "Mumbai",
meta: {
luggage: 2,
scanLuggage: 1,
upiLuggage: 1
},
cash: 10
},
{
city: "Mumbai",
meta: {
luggage: 4,
scanLuggage: 3,
upiLuggage: 1
},
cash: 24
},
]
I want to $match the above on the basis of city, and return the sum of each luggage type.
My code is as follows but $project is not working -
City.aggregate([
{
$match: { city: 'Mumbai' }
},
{
$project: {
city: 1,
mata.luggage: 1,
meta.scanLuggage: 1,
meta.upiLuggage: 1
}
},
{
$group: {
id: city,
luggage: {$sum: '$meta.luggage'},
scanLuggage: {$sum: '$meta.scanLuggage'},
upiLuggage: {$sum: '$meta.upiLuggage'}
}
}
])
But the $project is throwing error. I want my output to look like -
{
city: 'Mumbai',
luggage: 6,
scanLuggage: 4,
upiLuggage: 2
}
You should specify nested fields in quotes when using in $project, and also for grouping key should be _id.
db.collection.aggregate([
{
$match: {
city: "Mumbai"
}
},
{
$project: {
city: 1,
"meta.luggage": 1,
"meta.scanLuggage": 1,
"meta.upiLuggage": 1
}
},
{
$group: {
_id: "$city",
luggage: {
$sum: "$meta.luggage"
},
scanLuggage: {
$sum: "$meta.scanLuggage"
},
upiLuggage: {
$sum: "$meta.upiLuggage"
}
}
}
])
This is the playground link.

MongoDB Filter only IF ANY

Mongo Playgound
Lets say I have these results:
A)
[
{_id: 1, Name: 'A', Price: 10, xx:0},
{_id: 2, Name: 'B', Price: 15, xx:0},
{_id: 3, Name: 'A', Price: 100, xx:1},
{_id: 4, Name: 'B', Price: 150, xx:1},
]
B)
[
{_id: 1, Name: 'A', Price: 10, xx:0},
{_id: 2, Name: 'B', Price: 15, xx:0},
]
I want to:
If exists at least one x:1, return all x:1 only
If there is none x:1, return all x:0
Should I do a MAP & FILTER on root docs? or some kind of MATCH with conditionals? or Redact?
Results desired Ex.:
A) Removed x:0 because exists x:1, so returned only x:1
[
{_id: 3, Name: 'A', xx:1},
{_id: 4, Name: 'B', xx:1},
]
B) Returned only x:0 as there are only x:0
[
{_id: 1, Name: 'A', xx:0},
{_id: 2, Name: 'B', xx:0},
]
Group the documents by the xx field and add the grouped docs to the docs array using $push.
Sort the docs by the _id field in descending order.
Limit the result to 1.
If there are documents with both xx: 0 and xx: 1 values, only the xx: 1 group would be returned since we're sorting in descending order and limiting the result to the first group. If there are no documents with xx: 1 but documents with xx: 0 exist, the first group would be xx: 0 which gets returned.
You can then use $unwind to return a document for each grouped document and $replaceRoot to lift the document to the root level.
db.collection.aggregate([
{
$group: {
_id: "$xx",
docs: {
$push: "$$ROOT",
}
}
},
{
$sort: {
_id: -1,
}
},
{
$limit: 1,
},
{
$unwind: "$docs"
},
{
$replaceRoot: {
newRoot: "$docs"
},
}
])
MongoPlayground
If there might be docs with an xx value other than 0 and 1, you should filter those out using $match before grouping the docs using $group.
db.collection.aggregate([
{
$match: {
xx: {
$in: [
0,
1
]
}
}
},
{
$group: {
_id: "$xx",
docs: {
$push: "$$ROOT",
}
}
},
{
$sort: {
_id: -1,
}
},
{
$limit: 1,
},
{
$unwind: "$docs"
},
{
$replaceRoot: {
newRoot: "$docs"
},
}
])
MongoPlayground

How can I get the projection of some fields of a specific document embedded in an array?

Consider the following (simplified) example:
{
_id: 1,
name: "Andrew",
role: "ATT",
Sesons: [
{
year: "2018-2019",
age: 20,
goals: 10
},
{
year: "2019-2020",
age: 21,
goals: 101
}]
}
{
_id: 2,
name: "Paul",
role: "DF",
Sesons: [
{
year: "2018-2019",
age: 15,
goals: 102
},
{
year: "2019-2020",
age: 16,
goals: 1
}]
}
How can I get this specific result from a query, based on {"Seasons.year": "2019-2020"} for example ?
{_id: 2, name: "Paul", role: "DF", Seasons: {age: 16}}
And how can I get this result from a query by getting an entire embedded document?
{_id: 2, name: "Paul", role: "DF", Seasons: {year: "2018-2019", age: 16, goals: 1}}
Thank you so much!
I apologize for the question, perhaps, not correctly formatted, but it is my first question about Stack Overflow.
You can use projection.You can query like following.
[
{
$match: {
"_id": 2
}
},
{
$project: {
_id: 1,
name: 1,
role: 1,
Sesons: {
$filter: {
input: "$Sesons",
cond: {
$eq: [
"$$this.year",
"2019-2020"
]
}
}
}
}
}
]
Working Mongo playground . Once you get your result, again use the $project to get only age with other fields. Eg :
{
$project: {
"Sesons.year": 0,
"Sesons.goals": 0
}
}

Aggregation with mongodb

We are saving player stats for each match in MongoDb.
{idPlayer: 27, idTeam: 6, matchId: 1, score: 90},
{idPlayer:38, idTeam: 9, matchId:1, score: 6},
{idPlayer:5, idTeam:8, matchId:2, score: 20}
We want to know how many matches a team has played:
We want result as:
{idTeam, sumMatches}
{idTeam: 8, sumMatches: 6}
{idTeam: 9, sumMatches: 4}
We are tryning with aggregations but we don't get this result.
Any idea how to aproach this issue?
This should do it:
db.collection.aggregate([
{
$group: {
_id: "$idTeam",
matches: {
$addToSet: "$matchId"
}
}
},
{
$project: {
_id: 0,
idTeam: "$_id",
sumMatches: {
$size: "$matches"
}
}
}
])

Duplicate elements in a mongo db collection

Is there an quick efficient way to duplicate elements in a mongo db collections based on a property. In the example below, I am trying to duplicate the elements based on a jobId.
I am using Spring boot, so any example using Spring boot API would be even more helpful.
Original Collection
{ _id: 1, jobId: 1, product: "A"},
{ _id: 2, jobId: 1, product: "B"},
{ _id: 3, jobId: 1, product: "C"},
After duplication
{ _id: 1, jobId: 1, product: "A"},
{ _id: 2, jobId: 1, product: "B"},
{ _id: 3, jobId: 1, product: "C"},
{ _id: 4, jobId: 2, product: "A"},
{ _id: 5, jobId: 2, product: "B"},
{ _id: 6, jobId: 2, product: "C"},
You can use following aggregation:
db.col.aggregate([
{
$group: {
_id: null,
values: { $push: "$$ROOT" }
}
},
{
$addFields: {
size: { $size: "$values" },
range: { $range: [ 0, 3 ] }
}
},
{
$unwind: "$range"
},
{
$unwind: "$values"
},
{
$project: {
_id: { $add: [ "$values._id", { $multiply: [ "$range", "$size" ] } ] },
jobId: { $add: [ "$values.jobId", "$range" ] },
product: "$values.product",
}
},
{
$sort: {
_id: 1
}
},
{
$out: "outCollection"
}
])
The algorithm is quite simple here: we want to iterate over two sets:
first one defined by all items from your source collection (that's why I'm grouping by null)
second one defined artificially by $range operator. It will define how many times we want to multiply our collection (3 times in this example)
Double unwind generates as much documents as we need. Then the formula for each _id is following: _id = _id + range * size. Last step is just to redirect the aggregation output to your collection.