Using $match and $unwind to get a count - mongodb

I am learning MongoDB aggregation pipeline, i tried to get matched count from output of $unwind and $group. I am able to see the results for $unwind and $group. But I am not sure why I didn't get the matched count. Please help to get percentage field greater than 25.
Here's an example document:
{
"_id":ObjectId("599e9dbd8fbad926e712f902"),
"sample":"1",
"attribute":[
{
"functionName":"1",
"percentage":31.6
}
]
}
I tried this:
db.docs3.aggregate({
$unwind:'$attribute'
},
{
$group:{
_id:{
func:"$attribute.functionName",
percen:"$attribute.percentage"
}
}
})
And got this output:
{ "_id" : { "func" : "7", "percen" : 30 } }
{ "_id" : { "func" : "5", "percen" : 23.1 } }
{ "_id" : { "func" : "8", "percen" : 27.8 } }
{ "_id" : { "func" : "6", "percen" : 32.1 } }
{ "_id" : { "func" : "1", "percen" : 31.6 } }
{ "_id" : { "func" : "2", "percen" : 35 } }
{ "_id" : { "func" : "3", "percen" : 7.1 } }
{ "_id" : { "func" : "4", "percen" : 31.6 } }
I tried this:
db.docs3.aggregate({
$unwind:'$attribute'
},
{
$group:{
_id:{
func:"$attribute.functionName",
percen:"$attribute.percentage"
}
}
},
{
$match:{
"attribute.percentage":{
$gt:25
}
}
})
And I got an error.

I'm not sure whether you want a count of
Documents having at least one attribute with percentage > 25
Or
Attributes having percentage > 25
If you are interested in No. 1 then you do not need to unwind the attribute array in order to apply your match. The following will return a count of documents containing at least one attribute with percentage > 25:
db.getCollection('other').aggregate([
{ $match: { 'attribute.percentage': { $gt: 25 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
])
If you are interested in No. 2 then the following will return a count of attributes with percentage > 25:
db.getCollection('other').aggregate([
{ $unwind: '$attribute' },
{ $match: { 'attribute.percentage': { $gt: 25 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
])
For the following documents:
{
"_id" : ObjectId("599e9dbd8fbad926e712f902"),
"sample" : "1",
"attribute" : [
{
"functionName" : "1",
"percentage" : 31.6
}
]
}
{
"_id" : ObjectId("59a54104e7e9cc2109863beb"),
"sample" : "1",
"attribute" : [
{
"functionName" : "2",
"percentage" : 21.2
}
]
}
{
"_id" : ObjectId("59a542c4e7e9cc2109863c45"),
"sample" : "1",
"attribute" : [
{
"functionName" : "2",
"percentage" : 20.2
},
{
"functionName" : "2",
"percentage" : 28.2
},
{
"functionName" : "2",
"percentage" : 35.2
}
]
}
The first command returns count=2, the second command returns count=3.

Use the money sign to get the current value, and use the same field name you used in your $group stage
db.docs3.aggregate({
$unwind:'$attribute'
},
{
$group:{
_id:{
func:"$attribute.functionName",
percen:"$attribute.percentage"
}
}
},
{
$match:{
"$_id.percen":{
$gt:25
}
}
})

Related

MongoDB aggregation query based on multiple fields with similar values

I have documents that look like this:
{
"_id" : "001",
"a" : {
"b" : {
"c" : {
"custId" : "cust1"
},
"d" : {
"custId" : "cust2"
}
}
}
}
{
"_id" : "002",
"a" : {
"b" : {
"c" : {
"custId" : "cust1"
},
"d" : {
"custId" : "cust3"
}
}
}
}
{
"_id" : "003",
"a" : {
"b" : {
"c" : {
"custId" : null
},
"d" : {
"custId" : "cust2"
}
}
}
}
{
"_id" : "004",
"a" : {
"b" : {
"c" : {
"custId" : null
},
"d" : {
"custId" : "cust1"
}
}
}
}
I would like to obtain an aggregation which shows a sorted count of customer ids, ignoring null customer ids, like this:
{
"_id" : "cust1",
"count" : 3,
"records" : [
"001", "002", "004"
]
}
{
"_id" : "cust2",
"count" : 2,
"records" : [
"001", "003"
]
}
{
"_id" : "cust3",
"count" : 1,
"records" : [
"002"
]
}
I think each document needs to be broken down into 1 or 2 customer based arrays than then unwound back into documents, but I have been unable to determine a workable solution.
make an array of custId, $map to iterate loop of b after converting from object to array using $objectToArray
$unwind deconstruct custIds array
$match to filter none null custIds documents
$group by custIds and get count of total records and make unique array of _id using $addToset
db.collection.aggregate([
{
$project: {
custIds: {
$map: {
input: { $objectToArray: "$a.b" },
in: "$$this.v.custId"
}
}
}
},
{ $unwind: "$custIds" },
{ $match: { custIds: { $ne: null } } },
{
$group: {
_id: "$custIds",
count: { $sum: 1 },
records: { $addToSet: "$_id" }
}
}
])
Playground

How to group by multiple object elements

I have sample data like below
[
{
brand:"iphone",
category:"mobile"
},
{
brand:"iphone",
category:"laptop"
},
{
brand:"lenova",
category:"laptop"
}
]
and expecting result as
[
{
brand:"iphone",
count:2
},
{
brand:"lenova",
count:1
},
{
category:"laptop",
count:2
},
{
category:"mobile",
count:1
}
]
Here I want group by same object with multiple fields and get there count. Can any one please let me how to do that in the mongoose.
I am not familiarised with Mongoose. Just tried in Mongoshell
db.getCollection('test').aggregate([
{
$group:{
_id:"$brand",
brand:{$first:"$brand"},
category:{$first:"$category"}
}
},
{$project:{_id:0}}
])
Possible only by using two queries.
Group By Brand
db.getCollection('pages').aggregate([
{
$group: {_id: "$brand", category: { $push: "$category" }}
},
{
$project : {
_id : 0, brand : "$_id", count : {$size : "$category"}
}
},
{ $unwind: { path: "$category", preserveNullAndEmptyArrays: true } }
])
Result:-
/* 1 */
{
"brand" : "lenova",
"count" : 1
}
/* 2 */
{
"brand" : "iphone",
"count" : 2
}
Group By Category
db.getCollection('pages').aggregate([
{
$group: {
_id: "$category", brand: { $push: "$brand" },
}
},
{
$project : {
_id : 0, category : "$_id", count : {$size : "$brand"}
}
},
{ $unwind: { path: "$brand", preserveNullAndEmptyArrays: true } },
])
Result:-
/* 1 */
{
"category" : "laptop",
"count" : 2
}
/* 2 */
{
"category" : "mobile",
"count" : 1
}
Merge them for the required output.
We can use $facet to run parallel aggregation on data.
The following query can get us the expected output:
db.collection.aggregate([
{
$facet:{
"brand_group":[
{
$group:{
"_id":"$brand",
"brand":{
$first:"$brand"
},
"count":{
$sum:1
}
}
},
{
$project:{
"_id":0
}
}
],
"category_group":[
{
$group:{
"_id":"$category",
"category":{
$first:"$category"
},
"count":{
$sum:1
}
}
},
{
$project:{
"_id":0
}
}
]
}
},
{
$project:{
"array":{
$concatArrays:["$brand_group","$category_group"]
}
}
},
{
$unwind:"$array"
},
{
$replaceRoot:{
"newRoot":"$array"
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c2"),
"brand" : "iphone",
"category" : "mobile"
}
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c3"),
"brand" : "iphone",
"category" : "laptop"
}
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c4"),
"brand" : "lenova",
"category" : "laptop"
}
Output:
{ "brand" : "lenova", "count" : 1 }
{ "brand" : "iphone", "count" : 2 }
{ "category" : "laptop", "count" : 2 }
{ "category" : "mobile", "count" : 1 }

How to count occurences of values in a nested array?

I've searched but could not find an answer to my problem. I need to count the occurences of the field "nationalCode". I've got a collection with this sample structure in MongoDB:
{
"_id" : ObjectId("5d7519cc6c17d65d4983f048"),
"origin" : "Base1",
"topic" : [
{
"nationalTopic" : {
"nationalCode" : 26
},
"dateTime" : NumberLong(20120927000000)
},
{
"nationalTopic" : {
"nationalCode" : 132
},
"dateTime" : NumberLong(20120927000000)
},
{
"nationalTopic" : {
"nationalCode" : 26
},
"dateTime" : NumberLong(20120927000000)
},
{
"nationalTopic" : {
"nationalCode" : 26
},
"dateTime" : NumberLong(20121005000000)
}
]
}
I've used the following code (I tried many variations of it, but none of them got me the right results):
db.processos.aggregate(
[
{ "$unwind": "$topic" },
{"$match": {"origin": "Base1"}},
{"$group": { "_id": { nationalCode: "$topic.nationalTopic.nationalCode", "count": { "$sum": 1 }} } }
]
)
I'm expecting something like this:
{
"_id" : {
"nationalCode" : 26,
"count" : 3.0
}
}
/* 2 */
{
"_id" : {
"nationalCode" : 132,
"count" : 1.0
}
}
You should extract the count element from the _id.
The following query worked for me.
db.data.aggregate(
[
{ "$unwind": "$topic" },
{"$match": {"origin": "Base1"}},
{"$group": { _id: { "nationalCode": "$topic.nationalTopic.nationalCode" },
"count": {$sum: 1} }
}
]
)
just do it with $project to change your format
do it like this
MongoDB Enterprise >
db.ggg.aggregate(
[
{$unwind:"$topic"},
{"$match": {"origin": "Base1"}},
{"$group": { "_id": { nationalCode: "$topic.nationalTopic.nationalCode"},
"count": { "$sum": 1 } }},
{$project :{"_id.nationalCode":1,"_id.count":"$count"}}
]
)
here it the result !
{ "_id" : { "nationalCode" : 26, "count" : 3 } }
{ "_id" : { "nationalCode" : 132, "count" : 1 } }

$sum arrays with the same ids in mongodb

The two documents of my collection look like this:
First document
{
"_id" : 2055,
"counervalues" : {
"chcounter" : 3
"bscounter" : 10
}
"attributionvalues" :[
{
"id" : 1
"conversionvalue" : 85.0
"conversioncounter" : 6300.0
},
{
"id" : 2
"conversionvalue" : 25.0
"conversioncounter" : 600
}
}
Second document
{
"_id" : 1046,
"counervalues" : {
"chcounter" : 23
"bscounter" : 46
}
"attributionvalues" :[
{
"id" : 1
"conversionvalue" : 15.0
"conversioncounter" : 275.0
},
{
"id" : 2
"conversionvalue" : 65.0
"conversioncounter" : 12000.0
}
}
Now I want to apply the aggregation framework in order to get a new document which has a result as this:
Result
{
"_id" : 3005,
"counervalues" : {
"chcounter" : 26
"bscounter" : 56
}
"attributionvalues" :[
{
"id" : 1
"conversionvalue" : 100.0
"conversioncounter" : 6575.0
},
{
"id" : 2
"conversionvalue" : 90.0
"conversioncounter" : 12600.0
}
}
I started my aggregation like this:
db.conversion.counters.aggregate({
$match:
{
"_id" : {"$gte" : 1046 , "$lte" : 2055}
}
$group:
{
cvchc: {$sum: "$counervalues.chcounter"}
cvbsc: {$sum: "$counervalues.bscounter"}
}
});
but I have trouble to match the attributionvalues according to their ids and add them.
Anyone has an idea?
Run the following aggregation pipeline, should give you the desired results:
db.conversion.aggregate([
{ "$match": { "_id" : { "$gte" : 1046 , "$lte" : 2055 } } },
{ "$unwind": "$attributionvalues" },
{
"$group": {
"_id": "$attributionvalues.id",
"cvchc": { "$sum": "$counervalues.chcounter" },
"cvbsc": { "$sum": "$counervalues.bscounter" },
"avcv": { "$sum": "$attributionvalues.conversionvalue" },
"avcc": { "$sum": "$attributionvalues.conversioncounter" }
}
},
{
"$group": {
"_id": null,
"chcounter": { "$first": "$cvchc" },
"bscounter" : { "$first": "$cvbsc" },
"attributionvalues": {
"$push": {
"id": "$_id",
"conversionvalue": "$avcv" ,
"conversioncounter": "$avcc"
}
}
}
},
{
"$project": {
"counervalues": {
"chcounter": "$chcounter",
"bscounter": "$bscounter"
},
"attributionvalues": 1
}
}
])

MongoDB nested group?

I'm trying to implement a nested group query in mongodb and I'm getting stuck trying to add the outer group by. Given the below (simplified) data document:
{
"timestamp" : ISODate(),
"category" : "movies",
"term" : "my movie"
}
I'm trying to achieve a list of all categories and within the categories there should be the top number of terms. I would like my output something like this:
[
{ category: "movies",
terms: [ { term: "movie 1", total: 5000 }, { term: "movie 2", total: 200 } ... ]
},
{ category: "sports",
terms: [ { term: "football 1", total: 4000 }, { term: "tennis 2", total: 250 } ... ]
},
]
My 'inner group' is as shown below, and will get the top 5 for all categories:
db.collection.aggregate([
{ $match : { "timestamp": { $gt: ISODate("2014-08-27") } } },
{ $group : { _id : "$term", total : { $sum : 1 } } },
{ $sort : { total : -1 } },
{ $limit: 5 }
]);
// Outputs:
{ "_id" : "movie 1", "total" : 943 }
{ "_id" : "movie 2", "total" : 752 }
How would I go about implementing the 'outer group'?
Additionally sometimes the above aggregate]ion returns a null value (not all documents have a term value). How do I go about ignoring the null values?
thanks in advance
You will need two groups in this case. The first group generates a stream of documents with one document per term and category:
{ $group : {
_id : {
category: "$category",
term: "$term",
},
total: { $sum : 1 }
}
}
A second group will then merge all documents with the same term into one, using the $push operator to merge the categories into an array:
{ $group : {
_id : "$_id.category",
terms: {
$push: {
term:"$_id.term",
total:"$total"
}
}
}
}
Query:
db.getCollection('orders').aggregate([
{$match:{
tipo: {$regex:"[A-Z]+"}
}
},
{$group:
{
_id:{
codigo:"1",
tipo:"$tipo",
},
total:{$sum:1}
}
},
{$group:
{
_id:"$_id.codigo",
tipos:
{
$push:
{
tipo:"$_id.tipo",
total:"$total"
}
},
totalGeneral:{$sum:"$total"}
}
}
]);
Response:
{
"_id" : "1",
"tipos" : [
{
"tipo" : "TIPO_01",
"total" : 13.0
},
{
"tipo" : "TIPO_02",
"total" : 2479.0
},
{
"tipo" : "TIPO_03",
"total" : 12445.0
},
{
"tipo" : "TIPO_04",
"total" : 12445.0
},
{
"tipo" : "TIPO_05",
"total" : 21.0
},
{
"tipo" : "TIPO_06",
"total" : 21590.0
},
{
"tipo" : "TIPO_07",
"total" : 1065.0
},
{
"tipo" : "TIPO_08",
"total" : 562.0
}
],
"totalGeneral" : 50620.0
}