Complex nested array of object get record from mongodb - mongodb

in project it has following complex format of document in mongodb
{
_id: class_1,
students:[
{
_id: student_1,
questions: [
{
_id: s1q1,
answers:[
{
_id: s1q1a1,
},
{
_id: s1q1a2,
},
{
_id: s1q1a3,
}
]
},
{
_id: s1q2,
answers:[
{
_id: s1q2a1,
},
{
_id: s1q2a2,
},
{
_id: s1q2a3,
}
]
}
]
},
{
_id: student_2,
questions: [
{
_id: s2q1,
answers:[
{
_id: s2q1a1,
},
{
_id: s2q1a2,
},
{
_id: s2q1a3,
}
]
},
{
_id: s2q2,
answers:[
{
_id: s2q2a1,
},
{
_id: s2q2a2,
},
{
_id: s2q2a3,
}
]
}
]
}
]
}
I tried lot with aggregation but unable to get response as following
{
_id: class_1,
students:[
{
_id: student_1,
questions: [
{
_id: s1q1,
answers:[
{
_id: s1q1a1,
}
]
}
]
}
]
}
the query is like db.collection.find({students.questions.answers._id:s1q1a1})
if I query like above it returns get all child elements as well, so how to get only selected object with keeping nesting hierarchy?
I also tried with aggregation it gives me result till second hierarchy after it unable to filter because of mongo errors.

Try this, I did this using aggregation. Also this might not be the best way to achieve the desired result but it will work
https://mongoplayground.net/p/WQGWuZKh2zQ

Related

Getting distinct value for a field in Mongo

I have a database collection and this is its document structure.
{
_id: ObjectId("xxxxddsdsfdfdfdf")
category: electronics
sku: 10902
}
{
_id: ObjectId("dfdfdgfsdfdsgsf")
category: apparels
sku: 90345
}
{
_id: ObjectId("sdfdfdsggfgsgsdgsgsf")
category: electronics
sku: 10345
}
{
_id: ObjectId("dfndsnfkjdfdfsdnfsdf")
category: electronics
sku: 43435
}
I am trying to find the total number of SKUs per category. It should eliminate duplication and keep the values distinct. For example, electronics: 3, apparels: 1.
I have written a query, but it is giving me a total number of SKUs across categories which is not at all intended.
db.ecomm_sku_count.aggregate([
{
$group: {
_id: {
category: '$category',
sku_count: '$sku'
},
total_sku: {
$sum: 1
}
}
},
{
$count: "total_sku_units"
}
])
#output= [ { total_sku_units: 4 } ]
The intended output must be somewhat like this.
[
{ _id: { category: 'electronics', sku_count: 3 } },
{ _id: { category: 'apparels', sku_count: 1} }
]
I am trying to find the distinct SKU values per category.
I am beginner to mongo aggregation framework. Pardon me if the question is of noob type.
I think the below code is what you are looking for:
db.collection.aggregate([
{
"$group": {
"_id": {
"category": "$category",
},
"total_sku": {
"$addToSet": "$sku"
}
},
},
{
"$project": {
"total_sku": {
"$size": "$total_sku"
}
},
},
])
Mongo Playground Sample Execution

Mongodb Group stage, and query last two document

In mongodb, if we want to take first or last document of the group stage, then the FIRST and LAST operator will helpful. I want to group the collection based on _id:"$department" and also take the LAST document and the LAST-1 document. Is there any way to achieve this.
Assume you have this collection:
[
{ department: "HR", employees: 20 },
{ department: "Finance", employees: 30 },
{ department: "Sales", employees: 5 },
{ department: "IT", employees: 50 }
]
Then you can run this aggregation:
db.collection.aggregate([
{ $sort: { department: 1 } },
{
$group: {
_id: null,
employees: { $sum: "$employees" },
all_departments: { $push: "$department" }
}
},
{
$set: {
last_departments: {
$concatArrays: [
[{ $arrayElemAt: ["$all_departments", -1] }],
[{ $arrayElemAt: ["$all_departments", -2] }]
]
}
}
}
])
Mongo Playground
Update
With $slice it is even shorter:
db.collection.aggregate([
{ $sort: { department: 1 } },
{
$group: {
_id: null,
employees: { $sum: "$employees" },
all_departments: { $push: "$department" }
}
},
{ $set: { last_departments: { $slice: ["$all_departments", -2] } } }
])

MongoDB multiple levels embedded array query

I have a document like this:
{
_id: 1,
data: [
{
_id: 2,
rows: [
{
myFormat: [1,2,3,4]
},
{
myFormat: [1,1,1,1]
}
]
},
{
_id: 3,
rows: [
{
myFormat: [1,2,7,8]
},
{
myFormat: [1,1,1,1]
}
]
}
]
},
I want to get distinct myFormat values as a complete array.
For example: I need the result as: [1,2,3,4], [1,1,1,1], [1,2,7,8]
How can I write mongoDB query for this?
Thanks for the help.
Please try this, if every object in rows has only one field myFormat :
db.getCollection('yourCollection').distinct('data.rows')
Ref : mongoDB Distinct Values for a field
Or if you need it in an array & also objects in rows have multiple other fields, try this :
db.yourCollection.aggregate([{$project :{'data.rows.myFormat':1}},{ $unwind: '$data' }, { $unwind: '$data.rows' },
{ $group: { _id: '$data.rows.myFormat' } },
{ $group: { _id: '', distinctValues: { $push: '$_id' } } },
{ $project: { distinctValues: 1, _id: 0 } }])
Or else:
db.yourCollection.aggregate([{ $project: { values: '$data.rows.myFormat' } }, { $unwind: '$values' }, { $unwind: '$values' },
{ $group: { _id: '', distinctValues: { $addToSet: '$values' } } }, { $project: { distinctValues: 1, _id: 0 } }])
Above aggregation queries would get what you wanted, but those can be tedious on large datasets, try to run those and check if there is any slowness, if you're using for one-time then if needed you can consider using {allowDiskUse: true} & irrespective of one-time or not you need to check on whether to use preserveNullAndEmptyArrays:true or not.
Ref : allowDiskUse , $unwind preserveNullAndEmptyArrays

Using $project to Change Data Structure Returned in MongoDB

I have a function in my MongoDB/Node backend that returns a list of departments.
The documents in the collection look like this:
[
{
_id: 111,
department: "abc"
},
{
_id: 222,
department: "def"
},
{
_id: 333,
department: "ghi"
}
]
This is working with aggregation looks like this:
$group: {
_id: null,
data: { $addToSet: "$department" }
}
However, what this produces is not ideal. The output looks like this:
{
"count": 1,
"data": [
{
"_id": null,
"data": [
"abc",
"def",
"ghi"
]
}
]
}
What I'd like to do is return data where there isn't a nested array structure with "data" inside "data". I'd like this output:
{
"count": 1,
"data": [
"abc",
"def",
"ghi",
]
}
Is there a way I can do this with a projection stage?
I tried this:
{
$group: {
_id: null,
data: { $addToSet: "$department" }
}
},
{
$project: {
data: 0,
_id: 0
}
}
But end up with the same data structure returned. Is there a way I can do this with $project?
UPDATE:
After a suggestion I tried this:
db.staffmembers.aggregate([
{
$group: {
_id: null,
data: { $addToSet: "$department" }
}
},
{
$project: {
data: {
$reduce: {
input: "$data.data",
initialValue: [],
in: {
$concatArrays: ["$$this", "$$value"]
}
}
}
}
}
]);
... but this outputs an empty array for data:
{
"_id" : null,
"data" : [
]
}
This is what you're after:
db.collection.aggregate({
$project: {
data: { $arrayElemAt: [ "$data.data", 0 ] },
_id: 0
}
})
Mind you, I have a feeling there's something funky about your pipeline and/or data setup. It would probably be beneficial if you could share the input data and the entire pipeline.
You can use the $project stage in your pipeline to cherry pick what you require from the previous stage of the aggregation
db.staffmembers.aggregate([
{
$group:{
_id:null,
data:{$addToSet:"$department"}
}
},
{
$project:{
data:1,
_id:0
}
}
])

Mongodb aggregate query issue

I have the following query which is supposed to be getting, for each state, the amount of flights that fly within the state. Basically, the origin state and the destination state are the same. Pretty straightforward.
function(){
return db.flight_info.aggregate([
{ $project: { matches: { $eq:[ '$ORIGIN_STATE_ABR', '$DEST_STATE_ABR' ] }} },
{ $match: { matches:true } },
{ $group: { _id: "$ORIGIN_STATE_ABR", total: {$sum: 1} } }
]);
}
The issue is that this is not actually grouping my data for some reason. The output simply looks like this:
{
"_id": null,
"total": 61402
}
while it should be something along the lines of:
{
{"_id": "FL",
"total": 620 },
{"_id": "GA",
"total": 896},
...
}
You're missing the ORIGIN_STATE_ABR in your $project and that's causing an issue in your grouping.
db.flight_info.aggregate([
{ $project: { ORIGIN_STATE_ABR: 1,
matches: { $eq:[ '$ORIGIN_STATE_ABR', '$DEST_STATE_ABR' ] } } },
{ $match: { matches:true } },
{ $group: { _id: "$ORIGIN_STATE_ABR", total: { $sum: 1 } } }
]);