How to group data using mongo-template - mongodb

I have to filter data based on a criteria and set a field isObtained true or false. Then I am trying to group the data based on field grade. This is my data:
{
"school": "xyz",
"studentName": "John Doe",
"grade": "first",
"Hobbies": [
"Painting",
"Singing"
],
"language": [
"English"
],
"sport": "Badminton",
"totalStudyHours": 85
},
{
"school": xyz,
"studentName": "Jane Doe",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"Spanish"
],
"sport": "Karate",
"totalStudyHours": 65
},
{
"school": "xyz",
"studentName": "joey",
"grade": "first",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Cricket",
"totalStudyHours": 75,
"ispassed": true,
},
{
"studentName": "jason",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"English",
"Spanish"
],
"sport": "Tennis",
"totalStudyHours": 95,
"isObtained": true
},
{
"studentName": "mike",
"grade": "third",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Badminton",
"totalStudyHours": 70,
"isObtained": true
}
The expected output is
[
{
"grade": "first",
"values": [
{
"studentName": "John Doe",
"grade": "first",
"Hobbies": [
"Painting",
"Singing"
],
"language": [
"English"
],
"sport": "Badminton",
"totalStudyHours": 85,
"isObtained": true
},
{
"studentName": "joey",
"grade": "first",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Cricket",
"totalStudyHours": 75,
"isObtained": true
}
]
},
{
"grade": "third",
"values": [
{
"studentName": "jason",
"grade": "third",
"Hobbies": [
"Painting",
],
"language": [
"English",
"Spanish"
],
"sport": "Tennis",
"totalStudyHours": 95,
"isObtained": true
},
{
"studentName": "mike",
"grade": "third",
"Hobbies": [
"Singing"
],
"language": [
"English",
"Italian"
],
"sport": "Badminton",
"totalStudyHours": 70,
"isObtained": true
}
]
}
]
In the mongoDb query, the isObtained field is set based on what field we want. For example, if we want records with sport as "Badminton" then the isObtained field will be true when sport is Badminton and false otherwise.
Here is the query, but I am facing problem in grouping based on grade.
db.students.aggregate([
{
"$match": {
"$and": [
{
"school": "xyz"
}
]
}
},
{
"$project": {
"sport": 1,
"language": 1,
"hobbies": 1,
"isObtained": {
"$and": [
{
"$eq": [
"$sport",
"Badminton"
]
}
]
}
}
}

$match your conditions
$group by grade and make array of root documents in values,
define required fields and check condition created field isObtained if sport is Badminton then true otherwise false
db.students.aggregate([
{ $match: { school: "xyz" } },
{
$group: {
_id: "$grade",
values: {
$push: {
sport: "$sport",
language: "$language",
Hobbies: "$Hobbies",
isObtained: {
$cond: [{ $eq: ["$sport", "Badminton"] }, true, false]
}
}
}
}
}
])
Playground
If you want to go with dynamic approach then try $mergeObjects with $$ROOT,
Playground

Related

How to use update aggregation in MongoDb

my sample db:
"Employee": [ { "empeId": "e001",
"fName": "James",
"lName": "Bond",
"email": "jamesbond#hotmail.com",
"experience": [
"Database Design",
"SQL",
"Java" ]
},
{ "empeId": "e002",
"fName": "Harry",
"lName": "Potter",
"experience": [
"Data Warehouse",
"SQL",
"Spark Scala",
"Java Scripts" ]
} ],
"Project": [ { "projectId": "p001",
"projectTitle": "Install MongoDB" },
{ "projectId": "p002",
"projectTitle": "Install Oracle" },
{ "projectId": "p003",
"projectTitle": "Install Hadoop" } ],
"EmployeeProject": [ { "empeId": "e001",
"projectId": "p001",
"hoursWorked": 4 },
{ "empeId": "e001",
"projectId": "p003",
"hoursWorked": 2 },
{ "empeId": "e002",
"projectId": "p003",
"hoursWorked": 5 } ]
I want to update the array experience with 'test' of user with empeId: e001
desired output:
"Employee": [ { "empeId": "e001",
"fName": "James",
"lName": "Bond",
"email": "jamesbond#hotmail.com",
"experience": [
"Database Design",
"SQL",
"Java",
"test"
]
}]
I tried using
db.emp.updateOne([
{$unwind: "$Employee"},
{$match: {"Employee.emepId" : "e001" }}
],
{$push: {"Employee.experience" : "test"}})
and I get Syntax Error: Invalid property id #(shell):1:31
is this how the syntax for update with pipeline aggregation works?
The easiest and clean way(using arrayFilters):
db.collection.update({ Employee.empeId" : "e001"},
{
"$push": {
"Employee.$[x].experience": "test"
}
},
{
arrayFilters: [
{
"x.empeId": "e001"
}
]
})
Playground1
And via update/aggregation:
db.collection.update({ Employee.empeId" : "e001" },
[
{
$set: {
"Employee": {
$map: {
input: "$Employee",
as: "m",
in: {
$cond: [
{
$eq: [
"$$m.empeId",
"e001"
]
},
{
$mergeObjects: [
"$$m",
{
experience: {
$concatArrays: [
"$$m.experience",
[
"test"
]
]
}
}
]
},
"$$m"
]
}
}
}
}
}
])
Playground2

Add two new fields inside the array object if conditions meet in mongodb collection

[
{
"Empname": "Doug",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
},
{
"Categoryid": 233,
"Categoryname": "Maths"
}
]
}
]
},
{
"Empname": "stark",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
},
{
"Categoryid": 144,
"Categoryname": "language "
}
]
}
]
}
]
Here I want to insert two fields if it meets the following conditions
{
"Categoryid": 123,
"Categoryname": "science"
}
output should be like
[
{
"Empname": "Doug",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science"
"CategoryLabel": "Hex",
"CategoryCode": "Hex-D",
},
{
"Categoryid": 233,
"Categoryname": "Maths"
}
]
}
]
},
{
"Empname": "stark",
"Group": [
{
"Category": [
{
"Categoryid": 123,
"Categoryname": "science",
"CategoryLabel": "Hex",
"CategoryCode": "Hex-D",
},
{
"Categoryid": 144,
"Categoryname": "language "
}
]
}
]
}
]
You need update with arrayFilters to meet the condition for Category array.
And with multi: true to update multiple documents.
db.collection.update({},
{
$set: {
"Group.$[].Category.$[category].CategoryLabel": "Hex",
"Group.$[].Category.$[category].CategoryCode": "Hex-D"
}
},
{
arrayFilters: [
{
"category.Categoryid": 123,
"category.Categoryname": "science"
}
],
multi: true
})
Sample Mongo Playground

How to find distinct values of fields using facet operation in mongodb

The filteredAccording part and the categorizedBy is working as expected using the query which I provided in the link but I am facing issues in the findDistinct part.
In mongodb I have the following data:
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
I wanted the response to be:
"university": [
{
"name": "Literature",
"values": [
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
]
}
],
"findDistinct": [
{​​​​​​​​
"name": "Courses",
"values": [
"English",
"French"
]
}​​​​​​​​,
{​​​​​​​​
"name": "Status",
"values": [
"ACTIVE",
"NON-ACTIVE"
]
}​​​​​​​​
]
I tried it using this link but the response is not coming as expected.
https://mongoplayground.net/p/XECZvRMmt3T
Right now, the response is coming like this
"universities": [
{
"name": "Literature",
"values": [
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
]
}
],
"findDistinct": [
{​​​​​​​​
"Courses": [
"English",
"French"
]
}​​​​​​​​,
{​​​​​​​​
"status": [
"ACTIVE",
"NON-ACTIVE"
]
}​​​​​​​​
]
Any Help will be appreciated!!
Quick fixes in your query,
universities:
$addFields, remove $project and add only one operation for isMarked
$unwind deconstruct dept array
$group by dept and get values array of root
findDistinct:
$group by null and get unique courses array and status
$reduce to iterate loop of Courses nested array and get unique array using $setUnion
Make array of course and status in dest field
$unwind deconstruct dest array
$replaceRoot replace dest object to root
db.collection.aggregate([
{ $match: { university: "SPYU" }
},
{
$facet: {
universities: [
{ $addFields: { isMarked: { $in: ["French", "$Courses"] } } },
{ $unwind: "$dept" },
{
$group: {
_id: "$dept",
values: { $push: "$$ROOT" }
}
}
],
findDistinct: [
{
$group: {
_id: null,
Courses: { $addToSet: "$Courses" },
Status: { $addToSet: "$status" }
}
},
{
$project: {
_id: 0,
dist: [
{
name: "Courses",
values: {
$reduce: {
input: "$Courses",
initialValue: [],
in: { $setUnion: ["$$this", "$$value"] }
}
}
},
{
name: "Status",
values: "$Status"
}
]
}
},
{ $unwind: "$dist" },
{ $replaceRoot: { newRoot: "$dist" } }
]
}
}
])
Playground

How to sort a field inside an array in MongoDB aggregation

mongo playground
I want to sort the views from biggest to smallest inside for each category. How to sort this?
So the expected outcome should be like this:
[
{
"_id": "101",
"cars": [
{
"carName": "CyberTruck",
"driverId": "202",
"views": 50
},
{
"carName": "Model 3",
"driverId": "202",
"views": 40
},
{
"carName": "Model x",
"driverId": "202",
"views": 30
}
],
"categoryName": "Tesla"
},
{
"_id": "102",
"cars": [
{
"carName": "X1",
"driverId": "201",
"views": 20
}
],
"categoryName": "BMW"
}
]

In Mongo query in Subdocuments multiple fields

I have a collection in Mongo
{
"_id": 1,
"favorites": {
"artist": "Picasso",
"food": "pizza"
},
"finished": [
17,
3
],
"badges": [
"blue",
"black"
],
"points": [
{
"points": 85,
"bo nus": 20
},
{
"points": 85,
"bonus": 10
}
]
}{
"_id": 2,
"favorites": {
"artist": "Miro",
"food": "meringue"
},
"finished": [
11,
25
],
"badges": [
"green"
],
"points": [
{
"points": 85,
"bonus": 20
},
{
"points": 64,
"bonus": 12
}
]
}{
"_id": 3,
"favorites": {
"artist": "Cassatt",
"food": "cake"
},
"finished": [
6
],
"badges": [
"blue",
"red"
],
"points": [
{
"points": 85,
"bonus": 8
},
{
"points": 55,
"bonus": 20
}
]
}{
"_id": 4,
"favorites": {
"artist": "Chagall",
"food": "chocolate"
},
"finished": [
5,
11
],
"badges": [
"red",
"black"
],
"points": [
{
"points": 53,
"bonus": 15
},
{
"points": 51,
"bonus": 15
}
]
}{
"_id": 5,
"favorites": {
"artist": "Noguchi",
"food": "nougat"
},
"finished": [
14,
6
],
"badges": [
"orange"
],
"points": [
{
"points": 71,
"bonus": 20
}
]
}{
"_id": 6,
"favorites": {
"food": "pizza",
"artist": "Picasso"
},
"finished": [
18,
12
],
"badges": [
"black",
"blue"
],
"points": [
{
"points": 78,
"b onus": 8
},
{
"points": 57,
"bonus": 7
}
]
}
I want to retrieve all elements having points = 85 and bonus = 20.
Query will be
db.temp2.find({"points":{"points":85,"bonus":20}})
it returns documents with id : 1 and 2.
Now if i want to retrieve elements having ( points=85 and bonus = 20 ) and another sub-documents with {points=85 and bonus > 10). basically i want to retrieve element with id = 2
if query is
db.temp2.find({$and:[{"points":{"points":85,"bonus":20}},{"points":{"points":64,"bonus":{$gte:10}}}]}).pretty()
it gives no results whereas query
db.temp2.find({$and:[{"points":{"$elemMatch":{"points":85,"bonus":20}}},{"points":{"$elemMatch":{"points":64,"bonus":{$gte:10}}}}]})
gives me id=2.
Same thing i tried with anther sets
[
{
"name": "User1",
"tags": [
{
"k": "group",
"v": "test"
},
{
"k": "color",
"v": "blue"
}
]
},
{
"name": "User2",
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "blue"
}
]
},
{
"name": "User3",
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "red"
}
]
}
]
and if you want to find out elements having
"tags": [
{
"k": "group",
"v": "dev"
},
{
"k": "color",
"v": "blue"
}
]
query :
db.temp4.find({$and:[{"tags":{"k":"group","v":"dev"}},{"tags":{"k":"color","v":"blue"}}]})
and
db.temp4.find({$and:[{"tags":{"$elemMatch":{"k":"group","v":"dev"}}},{"tags":{"$elemMatch":{"k":"color","v":"blue"}}}]})
in both case you will get response.
Please help me to understand when to use $elemMatch and "$and".
Thanks in advance.
Sorry for grammatical mistakes.
The below query should work. As it is an embedded array, the "points.bonus" and "points.points" should be referred like this when $gte is used.
db.collection.find({$and:[{"points":{"points":85,"bonus":20}}, {"points.points" : 64, "points.bonus" : {$gte : 10}}]})
In the second example, there is no $gte. So, you are getting response for both queries.