aggregate request MongoDB - mongodb

I'd like get a multiple fields in a collection list with a condition. I tried a aggregate request but i have an error.
My request
db.people.aggregate({$match:{createdDate:{$exists:true},"ad":"noc2"}},{$group:{value2:$value2}});
My Json :
db.test.findOne();
{
"_id" : ObjectId("51e7dd16d2f8db27b56ea282"),
"ad" : "noc2",
"list" : {
"p45" : {
"id" : "p45",
"date" : ISODate("2014-01-01T12:18:30.568Z"),
"value3" : 21,
"value1" : 100,
"value2" : 489
},
"p6" : {
"id" : "p6"
"date" : ISODate("2013-07-18T12:18:30.568Z"),
"value3" : 21,
"value1" : 100,
"value2" : 489
},
"p4578" : {
"id" : "4578"
"date" : ISODate("2013-07-18T12:18:30.568Z"),
"value3" : 21,
"value1" : 100,
"value2" : 489
}
}
}
I want to get this json, for example, in result :
{id:p45,value:587},{id:p4578,value:47},{id:p6,value:2}

There are several issues with your sample document and aggregation:
the sample doc will not match your aggregation query because you are matching on createdDate field existing
the $group() operator works on a document level and needs an _id field to group by
your list field is an embedded document, not an array
aside from formatting, there is no obvious way to relate the sample values to the calculated result you are looking for
Here is an adjusted example document with the list as an array as well as some unique values for each item that happen to add up to the value numbers you mentioned:
db.people.insert({
"ad" : "noc2",
"createdDate" : ISODate(),
"list" : [
{
"id" : "p45",
"date" : ISODate("2014-01-01T12:18:30.568Z"),
"value3" : 21,
"value1" : 77,
"value2" : 489
},
{
"id" : "p6",
"date" : ISODate("2013-07-18T12:18:30.568Z"),
"value3" : 20,
"value1" : 20,
"value2" : 7
},
{
"id" : "4578",
"date" : ISODate("2013-07-18T12:18:30.568Z"),
"value3" : 21,
"value1" : 300,
"value2" : -319
}
]
})
You can now use the $unwind operator to extract the matching subdocuments.
It is unclear from the current question description what $group operation you are trying to achieve and whether you actually need $group.
Here is an example using the Aggregation Framework to $add (total) the three values for each list item:
db.people.aggregate(
// Find relevant docs (can use index if available)
{ $match: {
createdDate: { $exists:true },
"ad":"noc2"
}},
// Convert list array to stream of documents
{ $unwind: "$list" },
// Add (value1, value2, value3) for each list item
{ $project: {
_id: "$list.id",
value: { $add: [ "$list.value1", "$list.value2", "$list.value3"] }
}}
);
Sample output:
{
"result" : [
{
"_id" : "p45",
"value" : 587
},
{
"_id" : "p6",
"value" : 47
},
{
"_id" : "4578",
"value" : 2
}
],
"ok" : 1
}
Note that I've only aggregated using a single document for this example, and the output will be one result document for every item in the list array.
In real usage you may want to add an additional aggregation step to $group by document _id or some other criteria, depending on the outcome you are trying to achieve.

Related

Querying aggregates on subdocuments then grouping by field in parent document

I'm a noob when it comes to Mongo and I've been struggling to wrap my head around how to fetch data in the following fashion. I have a collection of order documents that contain some data such as an event_id and a subcollection (if that's the term) of issued_tickets. issued_tickets contains one to many subdocuments that contain fields such as name, date, etc. What I am trying to do is fetch the number of each type of issued tickets for each event_id in the parent document. So I would be wanting to do a count on each issued_tickets grouped by issued_tickets.name and then that goes up to the parent which is then summed and grouped on the parent's event_id.
Can anyone help me accomplish this? I keep spinning myself out on trying groupings and projections still.
Here is a sample document:
{
"_id" : ObjectId("5ce7335c1c666f000414f74a"),
"event_id" : ObjectId("5cb54f966668a9719ef6a103"),
"subtotal" : 3000,
"service_fee" : 760,
"processing_fee" : 143,
"total" : 3903,
"customer_id" : ObjectId("5ce7666c1c335f000414f747"),
"updated_at" : ISODate("2019-05-23T23:57:17.524Z"),
"created_at" : ISODate("2019-05-23T23:57:17.524Z"),
"ref" : "60d5fcf9-86c6-469b-b86b-315a9b55caca",
"issued_tickets" : [
{
"_id" : ObjectId("5ce7335c1c335f000414f666"),
"name" : "Tier 1",
"stub_name" : "Tier 1",
"price" : 1500,
"base_fee" : 200,
"perc_fee" : "0.12",
"access_code" : "163a1b9ee98338a8a4288a1c87446665",
"redeemed" : false
},
{
"_id" : ObjectId("5ce7335c1c335f0004146669"),
"name" : "Tier 2",
"stub_name" : "Tier 2",
"price" : 1500,
"base_fee" : 200,
"perc_fee" : "0.12",
"access_code" : "f50f262cd0bf1ec4ab36667c2a762446",
"redeemed" : true
}
]
}
We can do aggregations like following
$unwind to deconstruct the array
$group to reconstruct the array. While regrouping by eventId and issued_tickets.name, we can count using $sum
Mongo script :
db.collection.aggregate([
{
$unwind: "$issued_tickets"
},
{
$group: {
_id: {
_id: "$event_id",
ticketName: "$issued_tickets.name"
},
count: {
$sum: 1
}
}
},
{
$project: {
event_id: "$_id._id",
ticketName: "$_id.ticketName",
count: 1,
_id: 0
}
}
])
Working Mongo playground

MongoDB - Query to count the nested documents in an array

MongoDB - Layout
I'm a newbie to MongoDB, so pardon me if I'm using wrong terminologies here.
Given the above layout (attached image), I need to come up with two queries:
Use case 1 : A query that would output the count of total number of elements under someArrayField for a given document identifier.
For example, for the above layout, I expect the query to return 2 for someArrayField, because it has 2 elements under it as nested documents.
Use case 2 : A query that would output the count of total number of elements under someArrayField for every document under Collection_1 (Collection_1 can contain multiple documents with the similar layout).
I know, I need to somehow use "Aggregation Pipeline" to get the desired results, but due to the lack of experience with NoSQL, I'm struggling a bit here.
So, any help is greatly appreciated!
Given the following test set :
{
"_id" : ObjectId("5b453cc7799fb211dc44a1e8"),
"id" : 1.0,
"nestedDoc" : {
"nestedArray" : [
{
"field1" : "value1",
"field2" : "value2",
"field3" : "value3"
},
{
"field1" : "value11",
"field2" : "value21",
"field3" : "value31"
},
{
"field1" : "value12",
"field2" : "value22",
"field3" : "value32"
}
]
}
}
{
"_id" : ObjectId("5b453d23799fb211dc44a1e9"),
"id" : 2.0,
"nestedDoc" : {
"nestedArray" : [
{
"field1" : "value1",
"field2" : "value29",
"field3" : "value39"
},
{
"field1" : "value118",
"field2" : "value281",
"field3" : "value381",
"field4" : "value281"
},
{
"field1" : "value172",
"field2" : "value272",
"field3" : "value372"
}
]
}
}
Use case 1 :
Run the following aggregation query :
db.test1.aggregate(
[
// Stage 1 : filter documents
{
$match: {
id:1
}
},
// Stage 2 : count array size and return only it.
{
$project: {
arraySize:{$size:"$nestedDoc.nestedArray"},
_id:0
}
},
]
);
**Use case 2 ** Run the following :
db.test1.aggregate(
[
// Stage 1 count array size for each document
{
$project: {
_id:0,
arraySize:{$size:"$nestedDoc.nestedArray"}
}
},
// Stage 2 group all documents and sum arraySize
{
$group: {
_id:null,
totalArraysSize:{$sum:"$arraySize"}
}
},
]
);

Getting array of object with limit and offset doesn't work using mongodb

First let me say that I am new to mongodb. I am trying to get the data from the collection
Here is the document in my collection student:
{
"_id" : ObjectId("5979e0473f00003717a9bd62"),
"id" : "l_7c0e37b9-132e-4054-adbf-649dbc29f43d",
"name" : "Raj",
"class" : "10th",
"assignments" : [
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc571",
"name" : "1"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc572",
"name" : "2"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc573",
"name" : "3"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc574",
"name" : "4"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc575",
"name" : "5"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc576",
"name" : "6"
}
]
}
the output which i require is
{
"assignments" : [
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc571",
"name" : "1"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc572",
"name" : "2"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc573",
"name" : "3"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc574",
"name" : "4"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc575",
"name" : "5"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc576",
"name" : "6"
}
]
}
for this response i used the following query
db.getCollection('student').find({},{"assignments":1})
Now what exactly I am trying is to apply limit and offset for the comments list I tried with $slice:[0,3] but it gives me whole document with sliced result
but not assignments alone so how can I combine these two in order to get only assignments with limit and offset.
You'll need to aggregate rather than find because aggregate allows you to project+slice.
Given the document from your question, the following command ...
db.getCollection('student').aggregate([
// project on assignments and apply a slice to the projection
{$project: {assignments: {$slice: ['$assignments', 2, 5]}}}
])
... returns:
{
"_id" : ObjectId("5979e0473f00003717a9bd62"),
"assignments" : [
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc573",
"name" : "3"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc574",
"name" : "4"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc575",
"name" : "5"
},
{
"id" : "v_539f65c2-9f45-4d92-b05e-973cf08cc576",
"name" : "6"
}
]
}
This represents the assignments array (and only the assignments array) with a slice from element 2 to 5. You can change the slice arguments (2, 5 in the above example) to apply your own offset and limit (where the first argument is the offset and the limit is the difference between the first and second arguments).
If you want to add a match condition (to address specific documents) to the above then you'd do something like this:
db.getCollection('other').aggregate([
/// match a specific document
{$match: {"_id": ObjectId("5979e0473f00003717a9bd62")}},
// project on assignments and apply a slice to the projection
{$project: {assignments: {$slice: ['$assignments', 2, 5]}}}
])
More details on the match step here.

How to create an index on the "name" field of a document in mongodb

I want to create an index on the name field of a document in mongodb so that when I do a find,I should get all the names to be displayed in the alphabetical order.How can I achieve this ? Can anyone please help me out ...
My documents in mongodb:
db.col.find();
{ "_id" : ObjectId("5696256b0c50bf42dcdfeae1"), "name" : "Daniel", "age" : 24 }
{ "_id" : ObjectId("569625850c50bf42dcdfeae2"), "name" : "Asha", "age" : 21 }
{ "_id" : ObjectId("569625a40c50bf42dcdfeae3"), "name" : "Hampi", "age" : 34 }
{ "_id" : ObjectId("5696260f0c50bf42dcdfeae5"), "name" : "Bhavana", "age" : 14 }
Actually you don't need an index in order to display your result alphabetically. What you need is the .sort() method.
db.collection.find().sort({'name': 1})
Which returns
{ "_id" : ObjectId("569625850c50bf42dcdfeae2"), "name" : "Asha", "age" : 21 }
{ "_id" : ObjectId("5696260f0c50bf42dcdfeae5"), "name" : "Bhavana", "age" : 14 }
{ "_id" : ObjectId("5696256b0c50bf42dcdfeae1"), "name" : "Daniel", "age" : 24 }
{ "_id" : ObjectId("569625a40c50bf42dcdfeae3"), "name" : "Hampi", "age" : 34 }
Creating an index on a field in your document will not automatically sort your result on that particular field you still need to use the .sort() method. see Use Indexes to Sort Query Results
If you want to return an array of all names in your documents in ascending order then you will need to use the .aggregate() method.
The first stage in the pipeline is the $sort stage where you sort your documents by "name" in ascending order. The last stage is the $group stage where you group your documents and use the $push accumulator operator to return an array of "names"
db.collection.aggregate([
{ "$sort": { "name": 1 } },
{ "$group": { "_id": null, "names": { "$push": "$name" } } }
])
Which yields:
{ "_id" : null, "names" : [ "Asha", "Bhavana", "Daniel", "Hampi" ] }

MongoDB Query group and distinct together

Consider the following set of documents:
[
{
"name" : "nameA",
"class" : "classA",
"age" : 24,
"marks" : 45
},
{
"name" : "nameB",
"class" : "classB",
"age" : 22,
"marks" : 65
},
{
"name" : "nameC",
"class" : "classA",
"age" : 14,
"marks" : 55
}
]
I need to fetch the min and max values for age and marks as well as the distinct values for name and class.
I know that I can use aggregate and group to get the max and min values of age and marks with one query, and I can get distinct values of name and class using distinct query.
But I don't want to do separate queries to fetch that information. Is there a way in which I can get the result with one query? Let's say if I can merge the aggregate and distinct somehow.
Sure, you can do it with one aggregation command. You need to use $group with $addToSet operator:
db.collection.aggregate([{
$group : {
_id : null,
name : { $addToSet : "$name" },
class : { $addToSet : "$class" },
ageMin : { $min : "$age" },
ageMax : { $max : "$age" },
marksMin : { $min : "$marks" },
marksMax : { $max : "$marks" }
}
}]);
$addToSet will create an array with unique values for the selected field.
This aggregation will return the following response for your example docs:
{
"_id" : null,
"name" : [
"nameC",
"nameB",
"nameA"
],
"class" : [
"classB",
"classA"
],
"ageMin" : 14,
"ageMax" : 24,
"marksMin" : 45,
"marksMax" : 65
}