find list of users based on multiple condition using aggregation - mongodb

I want to know how to use aggregation to perform this.
find userId list who perform add to cart and item view event more then 2 times
Collection name event.
{
_id:1,
name:"Add To Cart",
userId:1
}
{
_id:2,
name:"Searched",
userId:2
}
{
_id:3,
name:"Add To Cart",
userId:1
}
{
_id:4,
name:"Item View",
userId:1
}
{
_id:5,
name:"Add To Cart",
userId:2
}
{
_id:6,
name:"Item View",
userId:1
}
my query-
db.getCollection("event").aggregate([ {$match:{$or:[{"name":"Add to
Cart"},{"name":"Item Viewed"}]}},
{$group:{_id: {userId:"$userId",name:"$name"},count:{$sum:1}}},
{$match:{count:{$gte:2}}},
{$group: {_id:"$_id.userId",event:{$push:"$_id.name"}}} ,
{$project:{size:{$size:"$event"}}},
{$match:{size:{$gte:2}}}
]).pretty();
output expected -from above collection
{userId:1}
I want only those userId who perform Add to cart and Item view event(both) more then 2 times.

Here is Full Query ,
db.Test.aggregate(
// Pipeline
[
{
$group: {
"_id": {
"userId": "$userId",
"name": "$name"
},
"count": {
"$sum": 1
}
}
},
{
$match: {
"count": {
"$gte": 2
}
}
},
{
$group: {
"_id": "$_id.userId",
"event": {
"$push": "$_id.name"
}
}
},
{
$project: {
"size": {
"$size": "$event"
}
}
},
{
$match: {
"size": {
"$gte": 2
}
}
},
]
);
You will get your userId in _id field.

Related

MongoDB Query - Get frequency map of an array

[
{
"_id": ObjectId("id-1"),
"tests": [
{
"category": "cat1",
"status": "status1",
},
{
"category": "cat1",
"status": "status2",
},
{
"category": "cat2",
"status": "status2",
},
],
},
{
"_id": ObjectId("id-2"),
"tests": [
{
"category": "cat2",
"status": "status1",
},
{
"category": "cat1",
"status": "status1",
},
{
"category": "cat1",
"status": "status2",
},
],
}
]
I have the above collection, my intention is to generate the below result. Please note that the statuses and categories are dynamic.
[
{
"id" : id-1,
"status": {
"status1": count,
"status2": count
},
"category": {
"cat1": count of it,
"cat2": count of it
}
},
{
"id" : id-2,
"status": {
"status1": count of it,
"status2": count of it
},
"category": {
"cat1": count of it,
"cat2": count of it
}
}
]
What I've attempted to do till now, is
Unwinded tests field, then
{
"$group": {
"_id": {
"id": "$_id",
"testStatus": "$tests.status"
},
"val": {
"$sum": 1
}
}
},
{
"$group": {
"_id": {
"id": "$_id.id",
},
"resGroup": {
"$addToSet": {
k: "$_id.testStatus",
v: "$val"
}
}
}
},
{
"$project": {
"_id": "$_id.id",
"statusGroup": {
"$arrayToObject": "$resGroup"
}
}
}
I've done the same for the category field and used $facet to run multiple aggregations.
But, am unable to fetch the result in the required format.
Any help on this will be appreciated.
Thanks
MongoDB Version: 3.4
$map to iterate loop of tests array and convert the object to an array using $objectToArray
$unwind deconstruct tests array
$unwind again deconstruct tests array because it's a nested array
$group by _id, k, and v and get the total count
$group by _id and k and construct the array of the status field in items
$arrayToObject convert items key-value array to an object
$group by _id and construct the array of items
$arrayToObject convert items array to object
db.collection.aggregate([
{
$project: {
tests: {
$map: {
input: "$tests",
in: { $objectToArray: "$$this" }
}
}
}
},
{ $unwind: "$tests" },
{ $unwind: "$tests" },
{
$group: {
_id: {
_id: "$_id",
k: "$tests.k",
v: "$tests.v"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: {
_id: "$_id._id",
k: "$_id.k"
},
items: {
$push: {
k: "$_id.v",
v: "$count"
}
}
}
},
{
$group: {
_id: "$_id._id",
items: {
$push: {
k: "$_id.k",
v: { $arrayToObject: "$items" }
}
}
}
},
{ $project: { items: { $arrayToObject: "$items" } } }
])
Playground

total of all groups totals using mongodb

i did this Aggregate pipeline , and i want add a field contains the Global Total of all groups total.
{ "$match": query },
{ "$sort": cursor.sort },
{ "$group": {
_id: { key:"$paymentFromId"},
items: {
$push: {
_id:"$_id",
value:"$value",
transaction:"$transaction",
paymentMethod:"$paymentMethod",
createdAt:"$createdAt",
...
}
},
count:{$sum:1},
total:{$sum:"$value"}
}}
{
//i want to get
...project groups , goupsTotal , groupsCount
}
,{
"$skip":cursor.skip
},{
"$limit":cursor.limit
},
])
you need to use $facet (avaialble from MongoDB 3.4) to apply multiple pipelines on the same set of docs
first pipeline: skip and limit docs
second pipeline: calculate total of all groups
{ "$match": query },
{ "$sort": cursor.sort },
{ "$group": {
_id: { key:"$paymentFromId"},
items: {
$push: "$$CURRENT"
},
count:{$sum:1},
total:{$sum:"$value"}
}
},
{
$facet: {
docs: [
{ $skip:cursor.skip },
{ $limit:cursor.limit }
],
overall: [
{$group: {
_id: null,
groupsTotal: {$sum: '$total'},
groupsCount:{ $sum: '$count'}
}
}
]
}
the final output will be
{
docs: [ .... ], // array of {_id, items, count, total}
overall: { } // object with properties groupsTotal, groupsCount
}
PS: I've replaced the items in the third pipe stage with $$CURRENT which adds the whole document for the sake of simplicity, if you need custom properties then specify them.
i did it in this way , project the $group result in new field doc and $sum the sub totals.
{
$project: {
"doc": {
"_id": "$_id",
"total": "$total",
"items":"$items",
"count":"$count"
}
}
},{
$group: {
"_id": null,
"globalTotal": {
$sum: "$doc.total"
},
"result": {
$push: "$doc"
}
}
},
{
$project: {
"result": 1,
//paging "result": {$slice: [ "$result", cursor.skip,cursor.limit ] },
"_id": 0,
"globalTotal": 1
}
}
the output
[
{
globalTotal: 121500,
result: [ [group1], [group2], [group3], ... ]
}
]

$push with $group in mongo aggregation

I wrote this query but its not exactly i want how to use push operator for expected result-
Is it not possible to use push with addFields and project pipeline.
db.getCollection("event").aggregate([ {$match:{"name":"Add
to Cart"}}, {$group:{"_id":"browser",count:{$sum:1}}}]);
output:
{_id:chrome.count:3}
{_id:firefox,count:1}
{_id:edge,count:1}
expect output:
{
browser:[
{name:"chrome",count:3},
{name:"firefox",count:1},
{name:"egde",count:1}
]
}
my collection:
{
_id:1,
name:"Add to Cart"
"browser":"chrome"
}
{
_id:2,
name:"Searched",
"browser":"chrome"
}
{
_id:3,
name:"Add To Cart",
"browser":"edge"
}
{
_id:4,
name:"Item View",
"browser":"chrome"
}
{
_id:5,
name:"Add To Cart",
"browser":"Firefox"
}
You need to use one more $group stage here
db.collection.aggregate([
{ "$group": {
"_id": "$browser",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"browser": {
"$push": {
"name": "$_id",
"count": "$count"
}
}
}},
{ "$project": { "_id": 0 }}
])

How to get top level elements as well as one level down array elements aggregate in one mongo query?

I have 2 mongo aggregate queries that work well separately -
db.transfer_orders.aggregate([
{
$match: {
"request_timestamp": { $gte: ISODate("2017-10-00T00:00:00.000Z") },
"request_timestamp": { $lt: ISODate("2017-10-12T00:00:00.000Z") },
"purpose": "POSITIONING"
}
},
{
$group: {
_id: null,
to_count: { $sum: 1 },
qty: { $sum: "$quantity" }
}
},
{
$project: {
_id: 0,
"to_count": "$to_count",
"qty": "$qty"
}
}
])
and
db.transfer_orders.aggregate([
{
$match: {
"request_timestamp": { $gte: ISODate("2017-10-00T00:00:00.000Z") },
"request_timestamp": { $lt: ISODate("2017-10-12T00:00:00.000Z") },
"purpose": "POSITIONING"
}
},
{
$unwind: "$adjustments"
},
{
$group: {
_id: null,
totalChangeQty: { $sum: "$adjustments.change_in_quantity"}
}
},
{
$project: {
_id: 0,
"adjusted_quantity": "$totalChangeQty"
}
}
])
The first query returns aggregate data of elements at the top level of the document, { "to_count" : 7810, "qty" : 19470 }
The second query returns aggregate data of elements at one level below the top level for the "adjustments" array - { "adjusted_quantity" : -960 }
Is there a way to write this as one query that will return both sets of data since the match criteria is the same for both?
The following aggregate operation should suffice since it has a pipeline after the $match step that introduces the new field adjusted_quantity. This is made possible using the $sum which returns the sum of the specified list of expressions for each document.
Once it reaches the $group stage, you can retain the value using the $sum operator.
db.transfer_orders.aggregate([
{
"$match": {
"request_timestamp": { "$gte": ISODate("2017-10-00T00:00:00.000Z") },
"request_timestamp": { "$lt": ISODate("2017-10-12T00:00:00.000Z") },
"purpose": "POSITIONING"
}
},
{
"$addFields": {
"adjusted_quantity": {
"$sum": "$adjustments.change_in_quantity"
}
}
},
{
"$group": {
"_id": null,
"to_count": { "$sum": 1 },
"qty": { "$sum": "$quantity" },
"adjusted_quantity": { "$sum": "$adjusted_quantity" }
}
}
])

What is wrong with this MongoDB Aggregate?

This is my sample document.I have many such documents where Cpe.Srno is a key and Cpe.date keeps getting updated using an Api
{
"_id": "8NH5rNCz47fyoo3z2",
"Cpe": {
"Srno": "GZDBDt2NmpMv54j",
"date": {
"$date": "2016-12-04T14:51:26.452Z"
}
},
"serialNumber": 4.703961341e+09,
"Device": {
"object_id": 1,
"terminal_id": 3,
"Info": {
"FirmwareVersion": {
"value": "5.9.2"
},
"Model": {
"value": "lunus Rustic HRK paxton - 989"
},
"DeviceSerialNumber": {
"value": 3.830919496e+09
},
"BatteryType": {
"value": "Handcrafted lithium battery"
},
"SavedPriority": {
"value": 7
}
}
}
}
I am trying to group by Cpe.Srno and get the latest document using Cpe.date.So i get distinct Cpe.Srno and only the latest documents.
I am stuck at the group stage.This is my code
db.AllDevices.aggregate([
{ $sort: { "Cpe.date": -1 } },
{
$group: {
_id: "$Cpe.Srno",
"latest": { $first: "$Cpe.date" }
}
},
])
and this is my result
{ "_id" : "qst3pnS3EQi8uHb", "latest" : ISODate("2016-12-04T14:51:26.487Z") }
{ "_id" : "Ur45SS1I3Yaji2p", "latest" : ISODate("2016-12-04T14:51:26.513Z") }
{ "_id" : "9ZZXVVEAQ5pA9Ax", "latest" : ISODate("2016-12-04T14:51:26.518Z") }
I need to get the whole data,all the fields(I only get two fields).I checked out $push ,$addToSet.They don't seem to work for me.
Where am I going wrong or am I missing something.
Tried $match after $group
db.AllDevices.aggregate([
{ $sort: { "Cpe.date": -1 } },
{
$group: {
_id: "$Cpe.Srno",
"latest": { $first: "$Cpe.date" }
}
},
{
$project: {
Srno: "$_id",
datetag: "$latest",
_id: 0
}
},
{ $match: { "Cpe.Srno": "$Srno", "Cpe.date": "$datetag" } }
])
Doesnt work.
You can make use of $$ROOT to push the whole document followed by project with $arrayElemAt to pick the latest.
aggregate([{
$sort: {
"Cpe.date": -1
}
}, {
$group: {
_id: "$Cpe.Srno",
"fields": {
$push: "$$ROOT"
}
}
}, {
$project: {
"latest": {
$arrayElemAt: ["$fields", 0]
}
}
}])