I am having difficulty in querying the follwing nested document using pymongo - mongodb

If these are the following nested documents
[
{
"_id": 5,
"name": "Wilburn Spiess",
"scores": [
{
"score": 44.87186330181261,
"type": "exam"
},
{
"score": 25.72395114668016,
"type": "quiz"
},
{
"score": 63.42288310628662,
"type": "homework"
}
]
},
{
"_id": 6,
"name": "Jenette Flanders",
"scores": [
{
"score": 37.32285459166097,
"type": "exam"
},
{
"score": 28.32634976913737,
"type": "quiz"
},
{
"score": 81.57115318686338,
"type": "homework"
}
]
},
{
"_id": 7,
"name": "Salena Olmos",
"scores": [
{
"score": 90.37826509157176,
"type": "exam"
},
{
"score": 42.48780666956811,
"type": "quiz"
},
{
"score": 96.52986171633331,
"type": "homework"
}
]
}
]
I need to access the score part 'type' = exam.
Can somebody help me with this?

If you're asking for a python program to access the score, you can print them out like:
collection = mongo_connection['db']['collection']
documents = collection.find({})
for doc in documents:
for score in doc['scores']:
if score['type'] == 'exam':
print(f'Score: {score["score"]}')
If you are trying to retrieve only the scores and ignore the rest, I'd do an $unwind on the scores, $match on the type, and then project the fields you want (or not).
db.test.aggregate([
{
$unwind: '$scores'
},
{
$match: {
'scores.type': 'exam'
}
},
{
$project: {
'name': '$name',
'score': '$scores.score'
}
}
])
This would output:
{
"_id" : 5,
"name" : "Wilburn Spiess",
"score" : 44.8718633018126
},
{
"_id" : 6,
"name" : "Jenette Flanders",
"score" : 37.322854591661
},
{
"_id" : 7,
"name" : "Salena Olmos",
"score" : 90.3782650915718
}

Related

PyMongo - Update the field for the object in nested array

Trying to update the nested object for the document in MongoDB.
{
"items" : [
{
"id": 1,
"name": "a",
"child": [
{ "id": 11, "name": "aa" },
{ "id": 12, "name": "bb" },
]
},
]
}
Need to update the child id to 13 whose name is "aa".
O/P, which I am trying to get
{
"items" : [
{
"id": 1,
"name": "a",
"child": [
{ "id": 13, "name": "aa" },
{ "id": 12, "name": "bb" },
]
},
]
}
Work with $[<identifier>] filtered positional operator and arrayFilters.
db.collection.update({
"items.child.name": "aa"
},
{
$set: {
"items.$[].child.$[c].id": 13
}
},
{
arrayFilters: [
{
"c.name": "aa"
}
]
})
Sample Mongo Playground

Multiple group in aggregate

In my db there are two types of sessions - "studio", "classroom". I want the count of number of studio sessions and number of classroom sessions AND count of subjects in a classroom session and count of subjects in studio session.
Eg data:
[
{
"key": 1,
"type": "studio",
"sub": "english"
},
{
"key": 2,
"type": "studio",
"sub": "history"
},
{
"key": 3,
"type": "classroom",
"sub": "english"
},
{
"key": 4,
"type": "studio",
"sub": "english"
},
{
"key": 5,
"type": "classroom",
"sub": "english"
},
{
"key": 5,
"type": "classroom",
"sub": "geography"
}
]
This is the query I have to group by session type.
db.collection.aggregate([
{
$group: {
_id: "$type",
groupedData: {
$push: {
key: "$key",
"sub": "$sub",
"type": "$type"
}
}
}
}
])
By this I get
[
{
"_id": "classroom",
"groupedData": [
{
"key": 3,
"sub": "english",
"type": "classroom"
},
{
"key": 5,
"sub": "english",
"type": "classroom"
},
{
"key": 5,
"sub": "geography",
"type": "classroom"
}
]
},
{
"_id": "studio",
"groupedData": [
{
"key": 1,
"sub": "english",
"type": "studio"
},
{
"key": 2,
"sub": "history",
"type": "studio"
},
{
"key": 4,
"sub": "english",
"type": "studio"
}
]
}
]
But I want the count of a subject in studio and class room and total count of sessions in studio and classroom
Eg.
{
studio: { count: 3, englishInStudio: 2, historyInStudio: 1 },
classroom: {count: 3, englishInClassroom: 2, geographyInClassroom: 1}
}
You can do this by specifying a document as your key to group on
db.test.aggregate([
{ $group: {
_id: { "type": "$type", "sub" : "$sub"},
count: { $sum: 1}
}
}]);
This will output the following, which get your required values but it's in a different format:
{ "_id" : { "type" : "classroom", "sub" : "english" }, "count" : 2 }
{ "_id" : { "type" : "studio", "sub" : "history" }, "count" : 1 }
{ "_id" : { "type" : "classroom", "sub" : "geography" }, "count" : 1 }
{ "_id" : { "type" : "studio", "sub" : "english" }, "count" : 2 }

Join on Related properties of two Arrays

I have the following result. Below it would be possible to make a map joining the array each with its objectid.
{
"_id": ObjectId("597233b50e717e0585dbd94a"),
"createdAt": ISODate("2017-07-21T17:02:45.119+0000"),
"name": "cardoso",
"gender": "female",
"profile": [{
"profession": "master",
"_id": ObjectId("597233b50e717e0585dbd94b"),
"departament": ObjectId("597233b50e717e0585dbd94e")
},
{
"_id": ObjectId("59766719003e7d552fe40e8c"),
"profession": "master",
"departament": ObjectId("59766719003e7d552fe40e8b")
},
{
"_id": ObjectId("5976b8f99d8a4326c6bf1ae5"),
"profession": "master",
"departament": ObjectId("5974d8fe398e5b2fd433410f")
}
],
"Institution": {
"_id": ObjectId("597233b50e717e0585dbd94c"),
"cnpj": 64837134000144.0,
"deletedAt": false,
"departament": [{
"title": "cardoso",
"category": "Sub-17",
"_id": ObjectId("597233b50e717e0585dbd94e")
},
{
"sport": "Tênis",
"title": "novo",
"category": "Sub-12",
"_id": ObjectId("59766719003e7d552fe40e8b")
},
{
"_id": ObjectId("5974d8fe398e5b2fd433410f"),
"category": "Intercâmbio",
"title": "testeJJJ",
"sport": "natação"
}
]
}
}
I need to do the following result. I did not want to have to manipulate the result in the node.
{
"sport": "Tênis",
"profession": "master",
"title": "novo",
"category": "Sub-12",
"_id": ObjectId("59766719003e7d552fe40e8b")
}
I already tended to do something but the query ends up getting very big
The basic premise here is to "lookup" the content in the other array whilst processing via $map.
This is either done via $indexOfArray with MongoDB 3.4:
db.collection.aggregate([
{ "$addFields": {
"Institution": {
"departament": {
"$map": {
"input": "$Institution.departament",
"as": "d",
"in": {
"sport": "$$d.title",
"profession": {
"$arrayElemAt": [
"$profile.profession",
{ "$indexOfArray": [ "$profile.departament", "$$d._id" ] }
]
},
"title": "$$d.title",
"category": "$$d.category"
}
}
}
}
}}
])
In that first index we look for the "index position" from the "profile" array that matches the current value of _id on the specified field. Then extract the data at that index via $arrayElemAt.
Or using $filter and the $arrayElemAt "the other way around" with MongoDB 3.2:
db.collection.aggregate([
{ "$addFields": {
"Institution": {
"departament": {
"$map": {
"input": "$Institution.departament",
"as": "d",
"in": {
"sport": "$$d.title",
"profession": {
"$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": "$profile",
"as": "p",
"cond": { "$eq": [ "$$p.departament", "$$d._id" ] }
}
},
"as": "p",
"in": "$$p.profession"
}},
0
]
},
"title": "$$d.title",
"category": "$$d.category"
}
}
}
}
}}
])
In which case the $filter reduces the array content in "profile" down to only matching elements, which should be just one. This is then extracted at the 0 index by $arrayElemAt.
Same result in either case:
{
"_id" : ObjectId("597233b50e717e0585dbd94a"),
"createdAt" : ISODate("2017-07-21T17:02:45.119Z"),
"name" : "cardoso",
"gender" : "female",
"profile" : [
{
"profession" : "master",
"_id" : ObjectId("597233b50e717e0585dbd94b"),
"departament" : ObjectId("597233b50e717e0585dbd94e")
},
{
"_id" : ObjectId("59766719003e7d552fe40e8c"),
"profession" : "master",
"departament" : ObjectId("59766719003e7d552fe40e8b")
},
{
"_id" : ObjectId("5976b8f99d8a4326c6bf1ae5"),
"profession" : "master",
"departament" : ObjectId("5974d8fe398e5b2fd433410f")
}
],
"Institution" : {
"_id" : ObjectId("597233b50e717e0585dbd94c"),
"cnpj" : 64837134000144.0,
"deletedAt" : false,
"departament" : [
{
"sport" : "cardoso",
"profession" : "master",
"title" : "cardoso",
"category" : "Sub-17"
},
{
"sport" : "novo",
"profession" : "master",
"title" : "novo",
"category" : "Sub-12"
},
{
"sport" : "testeJJJ",
"profession" : "master",
"title" : "testeJJJ",
"category" : "Intercâmbio"
}
]
}
}

Return union of embedded arrays

I have a collection with documents, each document has an array. I want to get an array which is the result of the union of the document's embedded arrays.
This is my collection:
{
"_id": ObjectId("...."),
"filter": "a",
"images": [
{
"_id": ObjectId("...."),
"file": "file_a_1.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_a_2.jpg"
}
]
},
{
"_id": ObjectId("...."),
"filter": "b",
"images": [
{
"_id": ObjectId("...."),
"file": "file_b_3.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_b_4.jpg"
}
]
},
{
"_id": ObjectId("...."),
"filter": "a",
"images": [
{
"_id": ObjectId("...."),
"file": "file_a_5.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_a_6.jpg"
}
]
}
And I would like to get the embedded arrays of the documents with filter = "a" for example.
{
"_id": ObjectId("...."),
"file": "file_a_1.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_a_2.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_a_5.jpg"
},
{
"_id": ObjectId("...."),
"file": "file_a_6.jpg"
}
And I would like to be able to limit the size of the returned array and the offset.
you need to unwind such array, then project to get new document shape.
db.david1.aggregate([{
$match : {
"filter" : "a"
}
}, {
$unwind : "$images"
}, {
$project : {
_id : "$images._id",
file : "$images.file"
}
}, {
$skip : 2
}, {
$limit : 1
}
]).toArray()

Mongo returning an array element

I have the following JSON document in my mongoDB which I added with mingoimport.
I am trying to return a single element from the questions array where theQuestion equals "q1".
{
"questions": [
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q1",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o1"
},
{
"type": 1,
"value": "o1"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes"
]
}
},
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q2",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o2"
},
{
"type": 1,
"value": "o2"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes",
"Glenn Rhee"
]
}
}
]
}
I ran the query db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}) but this retruned the whole document (both questionEntry's in question array!
I have tried db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}, _id:0," questions.questionItem": {$elemMatch : {theQuestion: "q1"}}})
But get the following error:
Error: error: {
"$err" : "Can't canonicalize query: BadValue Cannot use $elemMatch projection on a nested field.", "code" : 17287
Is there a way I could limit the result to just the array element which contains it?
Thanks
db.questions.find({},{"questions.questionEntry.questionItem.theQuestion" : "q1"});
or
db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"},{'questions.$':1});
please try these.
If you want to use $elemMatch the query should be:
db.questions.find(
{"questions.questionEntry.questionItem.theQuestion" : "q1"},
{
'_id':0,
"questions": {
$elemMatch : {"questionEntry.questionItem.theQuestion": "q1"}
}
}
)