mongoDB Aggregate please.... list Duplicate Total - mongodb

code currently in use
db.events.aggregate([
{ '$match':
{ thingId: 'node_0002',
eventState: 'test',
deviceId: {'$in': ['ARCT-1', 'ARCT-2', 'ARSS-1', 'ARSS-2', 'ARSW-1', 'ARCO-1']},
collectTimeText: {'$gte': '2022-04-01T00:00:00+09:00', '$lte': '2022-04-19T23:59:59+09:00' }}},
{ '$group': {
_id: {'$dateFromString': { dateString: { '$substr': [ '$collectTimeText', 0, 10 ] }}
},
list: { '$addToSet': {'deviceId':'$deviceId'} }},
},
{'$unwind':'$list'},
{ '$group': {
_id: {'$dateToString': {'format': "%Y-%m", date: "$_id"}
},
list: { '$push': '$list' }},
},
{'$sort': { _id: 1 } }], {})
result of using the code
{ _id: '2022-04',
list:
[ { deviceId: 'ARCT-2' },
{ deviceId: 'ARSS-1' },
{ deviceId: 'ARCT-1' },
{ deviceId: 'ARSW-1' },
{ deviceId: 'ARCO-1' },
{ deviceId: 'ARSS-2' },
{ deviceId: 'ARCT-2' },
{ deviceId: 'ARSS-1' },
{ deviceId: 'ARCT-1' },
{ deviceId: 'ARSW-1' },
{ deviceId: 'ARCO-1' },
{ deviceId: 'ARSS-2' },
{ deviceId: 'ARCT-2' },
{ deviceId: 'ARSS-1' },
{ deviceId: 'ARCT-1' },
{ deviceId: 'ARSW-1' },
{ deviceId: 'ARCO-1' },
{ deviceId: 'ARSS-2' } ] }
I want an output like the code below. Help
{ _id: '2022-04',
list:
{ 'ARCO-1': 3,
'ARCT-1': 3,
'ARSS-1': 3,
'ARCT-2': 3,
'ARSS-2': 3,
'ARSW-1': 3 } }
This is the code for generating statistics.
How do I get the results I want?
Did I write the code wrong in the first place?
When I added $project , I got the following result. How to check the count
db.events.aggregate([
{ '$match':
{ thingId: 'node_0002',
eventState: 'test',
deviceId: {'$in': ['ARCT-1', 'ARCT-2', 'ARSS-1', 'ARSS-2', 'ARSW-1', 'ARCO-1']},
collectTimeText: {'$gte': '2022-04-01T00:00:00+09:00', '$lte': '2022-04-19T23:59:59+09:00' }}},
{ '$group': {
_id: {'$dateFromString': { dateString: { '$substr': [ '$collectTimeText', 0, 10 ] }}
},
list: { '$addToSet': {'deviceId':'$deviceId'} }},
},
{'$unwind':'$list'},
{ '$group': {
_id: {'$dateToString': {'format': "%Y-%m", date: "$_id"}
},
list: { '$push': '$list' }},
},
{'$project':{
'list':{
'$arrayToObject':{
'$map': {
'input': '$list',
'as': 'el',
'in': {
'k': '$$el.deviceId',
'v': {'$sum':1}
}
}
}
}
}},
{'$sort': { _id: 1 } }], {})
Below is the result of adding $project.
{ _id: '2022-04',
list:
{ 'ARCO-1': 1,
'ARCT-1': 1,
'ARSS-1': 1,
'ARCT-2': 1,
'ARSS-2': 1,
'ARSW-1': 1 } }
I don't know what to do.... It seems like I'm almost there, but I don't know...
Maybe not all...?

$group twice and then $arrayToObject
db.collection.aggregate([
{
$group: {
_id: {
date: "$date",
deviceId: "$deviceId"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id.date",
list: {
$push: {
k: "$_id.deviceId",
v: "$count"
}
}
}
},
{
$set: {
list: {
$arrayToObject: "$list"
}
}
}
])
mongoplayground

Related

How to get this pipeline to return exactly one document?

I am running the following aggregation pipeline:
const agg = [
{
'$match': {
'aaa': 'bbb'
}
}, {
'$group': {
'_id': '',
'total': {
'$sum': '$num'
}
}
}
];
My problem is, when $match matches nothing, the pipeline returns 0 documents. How do I get the pipeline to always return 1 document?
In MongoDB version 6.0 you can do it like this one:
db.collection.aggregate([
{ $match: { aaa: 'bbb' } },
{
$group: {
_id: null,
total: { $sum: "$num" }
}
},
{
$densify: {
field: "total",
range: { step: 1, bounds: [0, 0] }
}
},
{ $set: { _id: { $cond: [{ $eq: [{ $type: "$_id" }, "missing"] }, MaxKey, "$_id"] } } },
{ $sort: { _id: 1 } },
{ $limit: 1 }
])
In version < 6.0 you can try this one:
db.collection.aggregate([
{
$facet: {
data: [
{ $match: { aaa: 'bbb' } },
{ $group: { _id: null, total: { $sum: "$num" } } }
],
default: [
{ $limit: 1 },
{ $group: { _id: null, total: { $sum: 0 } } },
{ $set: { _id: MaxKey } }
]
}
},
{ $replaceWith: { $mergeObjects: [{ $first: "$default" }, { $first: "$data" }] } },
])
Or this one:
db.collection.aggregate([
{ $match: { aaa: 'bbb' } },
{ $group: { _id: null, total: { $sum: "$num" } } },
{
$unionWith: {
coll: "collection",
pipeline: [
{ $limit: 1 },
{ $set: { _id: MaxKey, total: 0 } },
{ $project: { _id: 1, total: 1 } }
]
}
},
{ $sort: { _id: 1 } },
{ $limit: 1 }
])

MongoDB match computed value

I've created an aggregate query but for some reason it doesn't seem to work for custom fields created in the aggregation pipeline.
return this.repository.mongo().aggregate([
{
$match: { q1_avg: { $regex: baseQuery['value'], $options: 'i' } }, // NOT WORKING
},
{
$group: {
_id: '$product_sku',
id: { $first: "$_id" },
product_name: { $first: '$product_name' },
product_category: { $first: '$product_category' },
product_sku: { $first: '$product_sku' },
q1_cnt: { $sum: 1 },
q1_votes: { $push: "$final_rating" }
},
},
{
$facet: {
pagination: [ { $count: 'total' } ],
data: [
{
$project: {
_id: 1,
id: 1,
product_name: 1,
product_category: 1,
product_sku: 1,
q1_cnt: 1,
q1_votes: {
$filter: {
input: '$q1_votes',
as: 'item',
cond: { $ne: ['$$item', null] }
}
},
},
},
{
$set: {
q1_avg: { $round: [ { $avg: '$q1_votes' }, 2 ] },
}
},
{ $unset: ['q1_votes'] },
{ $skip: skip },
{ $limit: limit },
{ $sort: sortList }
]
}
},
{ $unwind : "$pagination" },
]).next();
q1_avg value is an integer and as far as I know, regex only works with strings. Could that be the reason

mongoose complex aggregation pipeline question

I am trying to finish up a data aggregation pipeline and having issues getting the data into the correct format. I'm not even sure if this is possible to do in one pipeline.
The original data looks like this:
[
{
answers: {
'question1': 'a',
'question2': 'c',
'question3': ['a','b'],
'question4': 1
},
createdAt: 2022-03-04T07:30:40.517Z,
},
{
answers: {
'question1': 'b',
'question2': 'c',
'question3': ['a','c']
'question4': 2
},
createdAt: 2022-03-04T07:30:40.518Z,
}
]
I've got my pipeline so far with this:
{ $project: {
"answers": { $objectToArray: "$answers" },
"date": { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" }}
}},
{ $unwind: "$answers" },
{ $unwind: "$answers.v" },
{
$group: {
_id: { answers : "$answers", date: "$date"},
c: { $sum: 1 }}
},
and the data now looks like this:
{
_id: {
answers: { k: 'q3', v: 'b' },
date: '2022-03-04'
},
count: 1
},
{
_id: {
answers: { k: 'q3', v: 'a' },
date: '2022-03-04'
},
count: 2
},
{
_id: {
answers: { k: 'q4', v: 1 },
date: '2022-03-04'
},
count: 1
},
{
_id: {
answers: { k: 'q1', v: 'b' },
date: '2022-03-04'
},
count: 1
},
{
_id: {
answers: { k: 'q4', v: 2 },
date: '2022-03-04'
},
count: 1
},
{
_id: {
answers: { k: 'q2', v: 'c' },
date: '2022-03-04'
},
count: 2
},
{
_id: {
answers: { k: 'q3', v: 'c' },
date: '2022-03-04'
},
count: 1
},
{
_id: {
answers: { k: 'q1', v: 'a' },
date: '2022-03-04'
},
count: 1
}
I would like to get a result that looks something like this:
{
'dates': [
{
'date': '2022-03-04',
'q1': { 'a': 1, 'b': 1 }
'q2': { 'c': 2 },
'q3': { 'a': 2, 'b': 1, 'c': 1 },
'q4': { '1': 1, '2': 1 }
}
]
'totals': { // this would be the totals across all the dates
'q1': { 'a': 1, 'b': 1 }
'q2': { 'c': 2 },
'q3': { 'a': 2, 'b': 1, 'c': 1 },
'q4': { '1': 1, '2': 1 }
}
}
any help would be greatly appreciated, even if I can't get both the totals and breakdown in 1 query.
here is the mongoplaygroud I've been working on
Not that simple. An important stage you have to use is $facet in order to get totals and dates
Maybe with $setWindowFields the aggregation pipeline could be a little simpler, but that a quick guess.
db.collection.aggregate([
{
$project: {
_id: 0,
answers: { $objectToArray: "$answers" },
date: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } }
}
},
{ $unwind: "$answers" },
{ $unwind: "$answers.v" },
{
$group: {
_id: {
answer: "$answers.v",
question: "$answers.k",
date: "$date"
},
count: { $sum: 1 }
}
},
{
$facet: {
dates: [
{
$group: {
_id: { question: "$_id.question", date: "$_id.date" },
count: {
$push: {
k: { $toString: "$_id.answer" },
v: "$count"
}
}
}
},
{
$group: {
_id: "$_id.date",
count: {
$push: {
k: "$_id.question",
v: { $arrayToObject: "$count" }
}
}
}
},
{
$replaceWith: {
$mergeObjects: [
{ date: "$_id" },
{ $arrayToObject: "$count" }
]
}
}
],
totals: [
{
$group: {
_id: { answer: "$_id.answer", question: "$_id.question" },
v: { $push: "$count" }
}
},
{
$group: {
_id: "$_id.question",
count: {
$push: {
k: { $toString: "$_id.answer" },
v: { $sum: "$v" }
}
}
}
},
{
$project: {
_id: 0,
k: "$_id",
v: { $arrayToObject: "$count" }
}
}
]
}
},
{ $set: { totals: { $arrayToObject: "$totals" } } }
])
Mongo Playground

MongoDB get size of unwinded array

I'm trying to return size of 'orders' and sum of 'item' values for each 'order' for each order from documents like the example document:
orders: [
{
order_id: 1,
items: [
{
item_id: 1,
value:100
},
{
item_id: 2,
value:200
}
]
},
{
order_id: 2,
items: [
{
item_id: 3,
value:300
},
{
item_id: 4,
value:400
}
]
}
]
I'm using following aggregation to return them, everything works fine except I can't get size of 'orders' array because after unwind, 'orders' array is turned into an object and I can't call $size on it since it is an object now.
db.users.aggregate([
{
$unwind: "$orders"
},
{
$project: {
_id: 0,
total_values: {
$reduce: {
input: "$orders.items",
initialValue: 0,
in: { $add: ["$$value", "$$this.value"] }
}
},
order_count: {$size: '$orders'}, //I get 'The argument to $size must be an array, but was of type: object' error
}
},
])
the result I expected is:
{order_count:2, total_values:1000} //For example document
{order_count:3, total_values:1500}
{order_count:5, total_values:2500}
I found a way to get the results that I wanted. Here is the code
db.users.aggregate([
{
$project: {
_id: 1, orders: 1, order_count: { $size: '$orders' }
}
},
{ $unwind: '$orders' },
{
$project: {
_id: '$_id', items: '$orders.items', order_count: '$order_count'
}
},
{ $unwind: '$items' },
{
$project: {
_id: '$_id', sum: { $sum: '$items.value' }, order_count: '$order_count'
}
},
{
$group: {
_id: { _id: '$_id', order_count: '$order_count' }, total_values: { $sum: '$sum' }
}
},
])
output:
{ _id: { _id: ObjectId("5dffc33002ef525620ef09f1"), order_count: 2 }, total_values: 1000 }
{ _id: { _id: ObjectId("5dffc33002ef525620ef09f2"), order_count: 3 }, total_values: 1500 }

Select top 3 per field after group MongoDB

I have a collection with fields like "servicereqesttype", "zipcode", "date"
I want to fing the 3 most common "servicerequesttype" per zipcode for a specific day.
db.event.aggregate([
{
$match: {
creationdate: "2011-01-01"
}
},
{
$project: {
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype"
}
},
{
$group: {
_id: {
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype"
},
zipcode: {
$first: "$zipcode"
},
servicerequesttype: {
$first: "$servicerequesttype"
},
count: {$sum: 1}
}
},
{
$sort: {
"zipcode": -1,
"count": -1
}
},
{
$project: {
_id: 0,
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype",
count: "$count"
}
}
])
now all I have to is to select only 3 per zipcode and I need some help, maybe I have to use $bucket or $map...
db.event.aggregate([
{
$match: {
creationdate: "2011-01-01"
}
},
{
$project: {
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype"
}
},
{
$group: {
_id: {
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype"
},
zipcode: {
$first: "$zipcode"
},
servicerequesttype: {
$first: "$servicerequesttype"
},
count: {$sum: 1}
}
},
{
$sort: {
"zipcode": -1,
"count": -1
}
},
{
$project: {
_id: 0,
zipcode: "$zipcode",
servicerequesttype: "$servicerequesttype",
count: "$count",
arrayOfTypes: "$array1",
arrayOfIncidents: "$array2"
}
},
{
$group: {
_id: "$zipcode",
arrayOfTypes: {
$push: {type: "$servicerequesttype", count: "$count"}
}
}
},
{
$project: {
_id: "$_id",
array: {
$slice: ["$arrayOfTypes", 3]
}
}
},
{
$sort: {
"_id": -1
}
}
])