Good Morning:
I have the following MongoDB structure:
{
"_id": 19,
"name": "Fredia Donan",
"faculty": "Zoomzone",
"lectures": [
{
"lecturerID": 25,
"name": "Sigismondo Brecknell",
"email": "sbrecknello#pinterest.com",
"imparts": [
{
"_id": 76,
"codCourse": 23,
"language": "Malay",
"max_students": 59,
"students": [
{
"studentID": 25
}
],
"classes": [
{
"date": ISODate("2022-02-02T09:23:32.59"),
"cod_classroom": 76
}
]
}
]
}
]
}
Having that I want to find the lecturer that imparts class to the maximum number of students. So I have to count the number of students that a lecturer teaches to and the find the max from across all lecturers from the department. How could I do it? Can it be done within the same code of the aggregation bellow? It is way over my MongoDB knowledge and I would be forever grateful if anyone could help.
I have read about $size and $count but everywhere I try it gives me different errors.
Thank you very much in advance!
The output above is done by an aggregation of two collections department and group with the following code:
[{"$unwind": "$lecturers"}, {"$unwind": "$lecturers.imparts"},
{"$lookup":
{"from": "group",
"localField": "lecturers.imparts.groupID",
"foreignField": "_id",
"as": "lecturers.imparts"}},
{"$group":
{"_id": {"_id": "$_id", "lecturersID": "$lecturers.lecturerID"},
"name": {"$first": "$name"},
"faculty": {"$first": "$faculty"},
"lecturers":
{"$first":
{"lecturerID": "$lecturers.lecturerID",
"name": "$lecturers.name",
"email": "$lecturers.email"}},
"imparts": {"$push": "$lecturers.imparts"}}},
{"$set":
{"lecturers":
{"$mergeObjects":
["$lecturers",
{"imparts":
{"$reduce":
{"input": "$imparts",
"initialValue": [],
"in": {"$concatArrays": ["$$value", "$$this"]}}}}]},
"imparts": "$$REMOVE"}},
{"$group":
{"_id": "$_id._id",
"name": {"$first": "$name"},
"faculty": {"$first": "$faculty"},
"lectures": {"$push": "$lecturers"}}}])
My desired output would be to get the lecturer of each department that teaches the most students. For instance, if I have this:
{
"_id": 19,
"name": "Fredia Donan",
"faculty": "Zoomzone",
"lectures": [
{
"lecturerID": 25,
"name": "Sigismondo Brecknell",
"email": "sbrecknello#pinterest.com",
"imparts": [
{
"_id": 76,
"codCourse": 23,
"language": "Malay",
"max_students": 59,
"students": [
{
"studentID": 25
}
],
"classes": [
{
"date": ISODate("2022-02-09T23:32:59.000Z"),
"cod_classroom": 76
}
]
},
{
"_id": 77,
"codCourse": 24,
"language": "Malayff",
"max_students": 59,
"students": [
{
"studentID": 28
}
],
"classes": [
{
"date": ISODate("2022-02-09T23:32:59.000Z"),
"cod_classroom": 77
}
]
}
]
},
{
"lecturerID": 36,
"name": "Sigismondo Brecknell",
"email": "sbrecknello#pinterest.com",
"imparts": [
{
"_id": 76,
"codCourse": 23,
"language": "Malay",
"max_students": 59,
"students": [
{
"studentID": 45
}
],
"classes": [
{
"date": ISODate("2022-02-09T23:32:59.000Z"),
"cod_classroom": 76
}
]
},
{
"_id": 77,
"codCourse": 24,
"language": "Malayff",
"max_students": 59,
"students": [
{
"studentID": 54
},
{
"studentID": 435
},
{
"studentID": 45
}
],
"classes": [
{
"date": ISODate("2022-02-09T23:32:59.000Z"),
"cod_classroom": 77
}
]
}
]
}
]
}
]
I would like to get that for the department with _id 19 the lecturer that teaches the most students is the one with id 36 since he imparts class to 4 students while the lecturer with id 25 imparts class to only 2 students.
So I would like to get the following output:
"_id": 19,
"name": "Fredia Donan",
"lecturerID": 36,
"maxImpartsStudents": 4
}
And I would like to get this information for every existing department. Every document in the collection department
Welcome Cristina Aranda!
EDIT:
For each lecturer, iterate all imparts using $map and creates one set that includes all students, returning its size as maxImpartsStudents. We use $setUnion to avoid counting the same student more than once, if they are in more than one class of the same lecturer.
$reduce the lecturers in each document to include only the one with maximal value of maxImpartsStudents .
Format
db.collection.aggregate([
{
$project: {
name: 1,
lectures: {
$map: {
input: "$lectures",
as: "item",
in: {
$mergeObjects: [
{
maxImpartsStudents: {
$size: {
$reduce: {
input: "$$item.imparts",
initialValue: [],
in: {$setUnion: ["$$value", "$$this.students"]}
}
}
}
},
{
name: "$$item.name",
lecturerID: "$$item.lecturerID"
}
]
}
}
}
}
},
{
$set: {
lectures: {
$reduce: {
input: "$lectures",
initialValue: {maxImpartsStudents: 0},
in: {
$cond: [
{$gte: ["$$this.maxImpartsStudents", "$$value.maxImpartsStudents"]},
"$$this", "$$value"]
}
}
}
}
},
{
$project: {
lecturerID: "$lectures.lecturerID",
maxImpartsStudents: "$lectures.maxImpartsStudents",
departmentName: "$name"
}
}
])
See how it works on the playground example
Related
I want to update the array grades for a specific user. I want to push an object into grades if in the array there is no object that matches the semester and subject values.
Input :
{
"users": [
{
"userID": "id_1",
"grades": [
{
"semester": 1,
"subject": "math",
"value": 15
},
{
"semester": 1,
"subject": "french",
"value": 15
}
]
},
{
"userID": "id_2",
"grades": [
{
"semester": 1,
"subject": "math",
"value": 18
}
]
}
]
}
For example if I want to push :
{
"semester": 2,
"subject": "french",
"value": 16
}
for userID = id_1.
The result is :
{
"users": [
{
"userID": "id_1",
"grades": [
{
"semester": 1,
"subject": "math",
"value": 15
},
{
"semester": 1,
"subject": "french",
"value": 15
},
{
"semester": 2,
"subject": "french",
"value": 16
}
]
},
{
"userID": "id_2",
"grades": [
{
"semester": 1,
"subject": "math",
"value": 18
}
]
}
]
}
But also if I try to push
{
"semester": 1,
"subject": "french",
"value": 10
}
for userID = id_1.
It won't update, because there is already an object that match "semester" : 1 and "subject" : "french"
I tried to use arrayFilter with array identifier to filter on userID first, but then I cannot achieve to apply the push condition on grades array.
{"$push":{ "users.$[user].grades": { "semester": 1, "subject": "math", "value" : 10 } }}
arrayFilter = [{"user.userID" : "id_1"}]
Thank you in advance for the help.
You could use a mix of $not with $elemMatch to achieve what you're aiming to, which translate to if no element of grades match the condition on semester & subject of the new one, then add it,
Here's how you can do it:
db.users.update({
"userID": "id_1",
"grades": {
"$not": {
"$elemMatch": {
"semester": 2,
"subject": "french"
}
}
}
}, {
"$push": {
"grades": {
"semester": 2,
"subject": "french",
"value": 16
}
}
})
I have a restaurant collection with its documents formed like this one:
{
"address": {
"building": "1007",
"coord": [
-73.856077,
40.848447
],
"street": "Moris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{
"date": {
"$date": 1393804800000
},
"grade": "A",
"score": "81"
},
{
"date": {
"$date": 1378857600000
},
"grade": "A",
"score": "6"
},
{
"date": {
"$date": 1358985600000
},
"grade": "A",
"score": "99"
},
{
"date": {
"$date": 11322006400000
},
"grade": "B",
"score": "14"
},
{
"date": {
"$date": 1288715200000
},
"grade": "B",
"score": "14"
}
],
"name": "Morris Park Bake Shop"
}
My homework asked me to find any restaurants having score from 80 to 100 and I do this
db.restaurants.find({ $expr: {$and: [{$gt: [ { $toInt: "$grades.score" }, 80 ]}, {$lt: [ { $toInt: "$grades.score" }, 100 ]}] } }).pretty()
And received "Executor error during find command :: caused by :: Unsupported conversion from array to int in $convert with no onError value".
I try
db.restaurants.find({ $expr: {$and: [{$gt: [ { $toInt: "$grades.$score" }, 80 ]}, {$lt: [ { $toInt: "$grades.$score" }, 100 ]}] } }).pretty()
And this returned:"FieldPath field names may not start with '$'. Consider using $getField or $setField."
Then i try
db.restaurants.find({$and:[{'grades.score': {$gt: 80}}, {'grade.score':{$lt:100}}]}).collation({locale:'en_US' ,numericOrdering: true})
And that returned nothing. It has to return at least the document i mentioned above, right?.
Perhaps this homework is about learning proper field value types or collation. With collation, numericOrdering can be used as commented by #prasad_. If score is truly numeric, for several reasons it's best to store it as numeric.
Unfortunately there doesn't seem to be a way at this time to specify a collation with mongoplayground.net. Without using a collation, there are many ways to achieve your desired output. Here's one way.
db.collection.aggregate([
{
// make grades.score numeric
"$set": {
"grades": {
"$map": {
"input": "$grades",
"as": "grade",
"in": {
"$mergeObjects": [
"$$grade",
{ "score": { "$toDecimal": "$$grade.score" } }
]
}
}
}
}
},
{
"$match": {
"grades.score": {
"$gt": 80,
"$lt": 100
}
}
},
{
"$project": {
"_id": 0,
"name": 1
}
}
])
Try it on mongoplayground.net.
How to calculate the sum of confident_score for every individual vendor?
Data stored in the DB:
[
{
"_id": "61cab38891152daf9387c0c7",
"name": "dummy",
"company_email": "abc#mailinator.com",
"brief_msg": "Cillum sed est prae",
"similar_case_ids": [],
"answer_id": [
"61cab38891152daf9387c0c9"
],
"pros_cons": [
{
"vendor_name": "xyzlab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor F",
"confident_score": 80,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf0f"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor FFF",
"confident_score": 40,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf10"
}
]
},
{
"vendor_name": "abclab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor B",
"confident_score": 50,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf16"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor BB",
"confident_score": 60,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf17"
}
]
}
]
the query for getting the matching id and grouping objects according to the vendor_name:
aggregate([
{
$match: { _id: id }
},
{
$unwind: {
path: '$pros_cons'
}
},
{
$group: {
_id: '$pros_cons'
}
},
])
};
After query I'm getting this:
[
{
"_id": {
"vendor_name": "abclab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor B",
"confident_score": 50,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf16"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor BB",
"confident_score": 60,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf17"
}
],
}
},
{
"_id": {
"vendor_name": "xyzlab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor F",
"confident_score": 80,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf0f"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor FFF",
"confident_score": 40,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf10"
}
],
}
}
]
Need to calculate sum for (vendor_name:abclab)TOTAL=110 and for (vendor_name:xyzlab)TOTAL=120 INDIVIDUALLY
required output:
[
{
"vendor_name": "abclab",
"totalScore": 110,
"count" : 2
},
{
"vendor_name": "xyzlab",
"totalScore": 120,
"count" : 2
}
]
$match - Filter documents by id.
$unwind - Deconstruct pros_cons array to multiple documents.
$project - Decorate output documents. With $reduce, to create totalScore field by summing confident_score from each element in pros_cons.score array.
db.collection.aggregate([
{
$match: {
_id: "61cab38891152daf9387c0c7"
}
},
{
$unwind: {
path: "$pros_cons"
}
},
{
$project: {
_id: 0,
vendor_name: "$pros_cons.vendor_name",
totalScore: {
$reduce: {
input: "$pros_cons.score",
initialValue: 0,
in: {
$sum: [
"$$value",
"$$this.confident_score"
]
}
}
}
}
}
])
Sample Demo on Mongo Playground
Let's say I have a Customer document like the following
db.collection.insertOne( {
"customerName": "John Doe",
"orders": [
{
"type": "regular",
"items": [
{
"name": "itemA",
"price": 11.1
},
{
"name": "itemB",
"price": 22.2
}
]
},
{
"type": "express",
"items": [
{
"name": "itemC",
"price": 33.3
},
{
"name": "itemD",
"price": 44.4
}
]
}
]
})
How can I calculate the total price of all orders (111 in this example)?
You can $unwind twice (because nested array) and group using $sum like this:
db.collection.aggregate([
{
"$unwind": "$orders"
},
{
"$unwind": "$orders.items"
},
{
"$group": {
"_id": "$customerName",
"total": {
"$sum": "$orders.items.price"
}
}
}
])
Example here
Sample data
{
"_id": ObjectId("6068b4d1ba926fd240c5216f"),
"name": "Amit",
"branch": "ECE",
"joiningYear": 2017,
"language": [
"Python",
"C#"
],
"personal": {
"contactinfo": 234556789,
"state": "UP",
"age": 25,
"semestermarks": [
80,
80.1,
98,
70
]
},
"salary": 10000
}
Query
db.college.find({$or:[{"age":24},{"state":"UP"}]})
Demo - https://mongoplayground.net/p/mJeXeT-oCe8
Key is "personal.age"
As your data is inside personal key you have to query inside it.
db.collection.find({
$or: [
{
"personal.age": 24
},
{
"personal.state": "UP"
}
]
})