Aggregate - $push an Array Expression in $group - arrays

I have this aggregation pipeline:
{
$project: {
type: '$_id.type',
datapoints: [
{$multiply: ['$_id.timestamp', 1000]},
'$count',
],
},
},
{
$group: {
_id: {
type: '$type',
},
datapoints: {$push: '$datapoints'},
},
},
... which brought me to a following question:
How could I possibly append/push/add/etc an array to datapoints in $group (what could let me avoid $project stage))?
If I try putting the same expression for datapoints in the $project under $push in the $group it gives me an error

It's a fair point because you cannot just simply notate with "bracket" [] notation as available from MongoDB 3.2 into a $push.
It's a bit of a hack, but you can basically wrap the expression with $map instead of using the [] notation:
{ "$group": {
"_id": "$_id.type",
"datapoints": {
"$push": {
"$map": {
"input": [1],
"in": [
{ "$multiply": ["$_id.timestamp", 1000] },
"$count"
]
}
}
}
}}
Or even $concatArrays if your MongoDB supports it:
{ "$group": {
"_id": "$_id.type",
"datapoints": {
"$push": {
"$concatArrrays": [
[
{ "$multiply": ["$_id.timestamp", 1000] },
"$count"
]
]
}
}
}}
Or even $setUnion, which is actually probably the shortest to type, but it may not retain the order of elements if that is important to you:
{ "$group": {
"_id": "$_id.type",
"datapoints": {
"$push": {
"$setUnion": [
[
{ "$multiply": ["$_id.timestamp", 1000] },
"$count"
]
]
}
}
}}
Trying to simply do :
"datapoint": { "$push": [{ "$mulitply": [ ...
Will of course result in an error
"errmsg" : "The $push accumulator is a unary operator",
And this sort of expression would be syntactically incorrect:
"datapoint": { "$push": { ["a"] }
So you need an "expression" of some sort which is "unary" and not "list based" and returns a BSON Array in result. The above operators cover those cases.

Related

Mongo Query to modify the existing field value with new value + array of objects

I want to update many documents based on the condition in MongoDB.
MODEL is the collection which has the document with below information.
"info": [
{
"field1": "String1",
"field2": "String2"
},
{
"field1": "String1",
"field2": "String_2"
}
],
"var": "x"
I need to update all the "String1" value of field1 with "STRING_NEW". I used the below query to update but not working as expected.
db.model.updateMany(
{ "info.field1": { $exists: true } },
[
{ "$set": {
"info": {
"$map": {
"input": "$info.field1",
"in": {
"$cond": [
{ "$eq": ["$$this.field1", "String1"] },
"STRING_NEW",
$$this.field1
]
}
}
}
} }
]
)
Please have a look and suggest if anything is to be modified in the above query.
Solution 1
With the update with aggregation pipeline, you should iterate the object in info array and update the iterated object by merging the current object with new field1 field via $mergeObjects.
db.model.updateMany({
"info.field1": "String1"
},
[
{
"$set": {
"info": {
"$map": {
"input": "$info",
"in": {
"$cond": [
{
"$eq": [
"$$this.field1",
"String1"
]
},
{
$mergeObjects: [
"$$this",
{
"field1": "STRING_NEW"
}
]
},
"$$this"
]
}
}
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
Can also work with $[<identifier>] positional filtered operator and arrayFilters.
db.model.updateMany({
"info.field1": "String1"
},
{
"$set": {
"info.$[info].field1": "STRING_NEW"
}
},
{
arrayFilters: [
{
"info.field1": "String1"
}
]
})
Demo Solution 2 # Mongo Playground

MongoDB query to return docs based on an array size after filtering array of JSON objects?

I have MongoDB documents structured in this way:
[
{
"id": "car_1",
"arrayProperty": [
{
"model": "sedan",
"turbo": "nil"
},
{
"model": "sedan",
"turbo": "60cc"
}
]
},
{
"id": "car_2",
"arrayProperty": [
{
"model": "coupe",
"turbo": "50cc"
},
{
"model": "coupe",
"turbo": "60cc"
}
]
}
]
I want to be able to make a find query that translates into basic English as "Ignoring all models that have 'nil' value for 'turbo', return all documents with arrayProperty of length X." That is to say, the "arrayProperty" of car 1 would be interpreted as having a size of 1, while the array of car 2 would have a size of 2. The goal is to be able to make a query for all cars with arrayProperty size of 2 and only see car 2 returned in the results.
Without ignoring the nil values, the query is very simple as:
{ arrayProperty: { $size: 2} }
And this would return both cars 1 and 2. Moreover, if our array was just a simple array such as:
[1,2,3,'nil]
Then our query is simply:
{
arrayProperty: {
$size: X,
$ne: "nil"
}
}
However, when we introduce an array of JSON objects, things get tricky. I have tried numerous things to no avail including:
"arrayProperty": {
$size: 2,
$ne: {"turbo": "nil"}
}
"arrayProperty": {
$size: 2,
$ne: ["arrayProperty.turbo": "nil"]
}
Even without the $size operator in there, I can't seem to filter by the nil value. Does anyone know how I would properly do this in those last two queries?
use $and in $match
db.collection.aggregate([
{
$match: {
"$and": [
{
arrayProperty: {
$size: 2
}
},
{
"arrayProperty.turbo": {
$ne: "nil"
}
}
]
}
}
])
mongoplayground
use $set first
db.collection.aggregate([
{
"$set": {
"arrayProperty": {
"$filter": {
"input": "$arrayProperty",
"as": "a",
"cond": {
$ne: [
"$$a.turbo",
"nil"
]
}
}
}
}
},
{
$match: {
arrayProperty: {
$size: 1
}
}
}
])
mongoplayground
set a new field of size
db.collection.aggregate([
{
"$set": {
"size": {
$size: {
"$filter": {
"input": "$arrayProperty",
"as": "a",
"cond": {
$ne: [
"$$a.turbo",
"nil"
]
}
}
}
}
}
},
{
$match: {
size: 1
}
}
])
mongoplayground

(Mongodb) Getting an error (BadValue) $in needs an array , when using $map aggregate function

I am trying to convert array of strings to array of ObjectId using map function, map will return me array of ObjectIds that I am using in $match to check if _id matches with any of objectids present in array using $in operator.
Database configuration is
db={
"students": [
{
"_id": ObjectId("5c1a79f7c98da061141475b7"),
"firstName": "Ibrahim",
"kelasID": ObjectId("5c429f9906f2a805bc6cd494"),
"lastName": "Ali",
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"year": 2018,
"__v": 0,
"addedOn": ISODate("2018-12-25T04:27:47.909Z"),
"checkIn": false,
"checkInStatus": 1,
"contactNo1": "012225656",
"father": "Ali",
"fatherID": "8852245",
"idType": 0,
"lastModified": ISODate("2018-12-25T04:27:47.909Z"),
"mother": "",
"motherID": ""
},
{
"_id": ObjectId("5c3bfea37774fb0b55000cb5"),
"idType": 0,
"checkIn": false,
"checkInStatus": 1,
"year": 2019,
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"kelasID": ObjectId("5c1a7534c98da061141475a3"),
"firstName": "Umar",
"lastName": "Bin Al-Khattab",
"contactNo1": "601222",
"status": 1,
"addedOn": ISODate("2019-01-14T03:14:43.597Z"),
"lastModified": ISODate("2019-01-14T03:14:43.597Z"),
"__v": 0
},
{
"_id": ObjectId("5c1a7c69c98da061141475bb"),
"idType": 0,
"checkIn": false,
"checkInStatus": 1,
"year": 2018,
"schoolID": ObjectId("5c1a735fc98da061141475a1"),
"kelasID": ObjectId("5c1a7540c98da061141475a5"),
"firstName": "Abdul Rahman",
"lastName": "Affan",
"father": "Affan",
"fatherID": "54321",
"contactNo1": "602288",
"status": 1,
"addedOn": ISODate("2018-12-25T04:30:16.130Z"),
"lastModified": ISODate("2018-12-25T04:30:16.130Z"),
"__v": 0
}
]
}
and the query is
db.students.aggregate([
{
"$match": {
"_id": {
$in: {
"stud": {
"$map": {
"input": [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7"
],
"in": {
"$toObjectId": "$$this"
}
}
}
}
}
}
}
])
You can also check this out here- https://mongoplayground.net/p/8tQFHxtrttW
The above doesn't work but if I change the query like below it works.
db.students.aggregate([
{
"$match": {
"_id": {
$in: [
ObjectId("5c1a79f7c98da061141475b7"),
ObjectId("5c3bfea37774fb0b55000cb5")
]
}
}
}
])
Link to above query- https://mongoplayground.net/p/cjq77KIAcPE
Error i'm getting - query failed: (BadValue) $in needs an array
Can anybody help with the reason to the error , as according to docs map does return an array , so what am I doing wrong?
Not sure why it doesn't work it might have to do with $in operator and $in aggregation. When you use an array that is defined by a variable you can use $in operator but when you are using aggregation result ( in this case $map ) you have to use $in aggregation. But to use $in aggregation you need to work with $expr for $match.
You can try the below code which works fine
db.students.aggregate([
{
$addFields: {
studentIds: {
$map: {
input: [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7",
],
in: {
$toObjectId: "$$this",
},
},
},
},
},
{
$match: {
$expr: {
$in: ["$_id", "$studentIds"],
},
},
},
]);
or if you want to combine the two of the stages you can do it like this
db.students.aggregate([
{
$match: {
$expr: {
$in: [
"$_id",
{
$map: {
input: [
"5c1a79f7c98da061141475b7",
"5c3bfea37774fb0b55000cb5",
"5c1a7c69c98da061141475bb",
"5c3bfea37774fb0b55000cb4",
"5c1a7d32c98da061141475be",
"5c3bfea37774fb0b55000cb7",
],
in: {
$toObjectId: "$$this",
},
},
},
],
},
},
},
]);

MongoDB - How to sum 2 string values in array after their conversion to integer?

I've been breaking my head on this for a while now:
[
{
"_id": "12sdsd",
"TotalStudent": [
"10"
]
},
{
"_id": "22fdf",
"TotalStudent": [
"20"
]
}
]
I need to sum the two values after conversion from String to Integer.
This doesn't work:
db.collection.aggregate([
{
$group: {
_id: "",
TotalStudents: {
$sum: {
$toInt: "$TotalStudent.0"
}
}
}
}
])
See the playground, what am I doing wrong?
https://mongoplayground.net/p/Evm3tJUGmKa
"$TotalStudent.0" - This is not a valid syntax with aggregation queries. When working with array fields in aggregations, use Aggregation Array Operators. The operator to get an element of an array by its index is $arrayElemAt. So, the following $group stage will work fine:
{ $group: {
_id: "",
TotalStudents: { $sum: { $toInt: { $arrayElemAt: [ "$TotalStudent", 0 ] } } }
} }

Mongoose - How to retrieve an array of objects from each document in collection?

Each document in my collection looks something like this (JSON):
{
name: "Roger",
matches: [
{
opponent: "Rafael Nadal",
match_date: 1494536400000
},
{
opponent: "Nick Kyrgyos",
match_date: 1494557400000
}
]
}
I want to extract all the matches each player had and also sort them by match_date using Mongoose, but I don't know how.
Could you please help me with this?
You can try below aggregation
db.collection.aggregate([
{ "$unwind": "$matches" },
{ "$sort": { "matches.match_date": 1 }},
{ "$group": {
"_id": "$_id",
"matches": { "$push": "$matches" }
}},
{ "$limit": 20 },
{ "$project": { "matches": 1, "numberOfMatches": { "$size": "$matches" } }}
])

Resources