I've an analytics API written using MongoDB.
This is my sessions model
const sessionSchema = new Schema(
{
user: { id: Number, name: String, email: String },
},
{ timestamps: true },
);
I want to get the unique users count by the date.
for the date 2019-10-24 there maybe 10 sessions from two users (id 1, id 2)
and
for the date 2019-10-25 there maybe 20 sessions from two users (id 3, id 8)
So my expected results is
2019-10-24 2 users
2019-10-25 2 users
I tried this
db.Session.aggregate([
{
$group: {
_id: { user: '$user.id', day: { $dayOfYear: '$createdAt' } },
count: { $sum: 1 },
},
},
])
and this doesn't seem to work.
My createdAt field's type is Date (eg:- createdAt: 2019-10-16T13:11:17.935Z) That is why I used $dayOfYear: '$createdAt'
db.Session.aggregate([
{
$project: {
date: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } }
}
},
{
$group: {
_id: "$date" ,
count: { $sum: 1 }
}
}
]);
At first group according to createdAt, as we want final result according to createdAt.
In this $group stage just push the userIds into the array, use $addToSet to keep it unique.
Then get the count of userIds with $size operator in $project stage.
You get the result.
Here is the query.
(convert the date as you want, its just a format, and you have done this, so I am skipping this task).
db.sessions.aggregate({$match:{}},
{$group:{_id: "$createdAt", userId : {$addToSet: "$user.id"}}},
{$project:{_id: 1, noOfUsers:{$size:"$userId"}}}).pretty()
Hope this helps!
Related
I have a problem,
I do a group on my database and the problem is the $$ROOT can exceed the memory limit, so I would like to have the X first elements.
this is my group:
$group: {
_id: {
DOG_RACE: '$DOG_RACE',
FOOD: '$FOOD'
},
total: {
$count: {}
},
_raw: {
$push: '$$ROOT'
}
}
the _raw contain all dogs grouped and there can be millions.
I tried to replace _raw: {} with an array with a slice but I got this error:
The field '_raw' must be an accumulator object
Can someone please have the syntaxe to do it ?
Thanks
EDIT
No this is what I'm trying:
$group: {
_id: {
DOG_RACE: '$DOG_RACE',
FOOD: '$FOOD'
},
total: {
$count: {}
},
_raw: {
$firstN: {
input: '$$ROOT',
n: 10
}
}
}
And I'm still getting an error with MongoServerError: unknown group operator '$function'
same with $function
also my mongodb database after running mongod is v6,0,1
I have the following aggregation code which returns all the students for each school in my school ID list. For each student there is an academic year ID that looks like this: ObjectId("5ede4682341e8426f1cf6285")
return await this.studentSchoolModel.aggregate([
{
$match: {
school: { $in: schoolIDs },
},
},
{
$group: {
_id: '$school',
academic_years: '$academic_year',
total_students: { $sum: 1 },
},
},
]);
If I try to group by the school, it works, but if I try to group by the school and academic year I get the following error:
MongoError: The field 'academic_year' must be an accumulator object
Does anyone know what I'm doing wrong? I want the output for each school to look like this:
_id: 12345678 //schoolID
academic_years: {
_id: 1111111 //academic year ID
total_students: 5 //number of students in that year
},
{
_id: 2222222 //academic year ID
total_students: 8 //number of students in that year
}
You can use $group
first group is to group by school and academic year. The second group is to group by school
Here is the code
db.collection.aggregate([
{
"$group": {
"_id": { sc: "$school", ay: "$academic_year" },
total: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.sc",
total_students: {
$push: {
_id: "$_id.ay",
total_students: "$total"
}
}
}
}
])
Working Mongo playground
Technologies: Mongoose, NodeJS, MongoDB
Question
The following document will save on the MongoDB cluster every 10 seconds.
{
"_id": "6003fafc04cb3727e40812b2",
"currentRound": 39300,
"current": 4.131929,
"voltage": 245.855,
"power": 956.5797,
"frequency": 50,
"totalPower": 1167.862,
"importPower": 1167.862,
"exportPower": 0,
"powerFactor": 0.998356,
"rssi": -59,
"deviceId": "EC:FA:BC:63:02:C1",
"slaveId": 201,
"timestamp": 1610873596543,
"__v": 0
}
There is two types of slave ids (101, 201) documents saving on the same collection and each slave id is having a specific device id. I want to retrieve the most latest and oldest 101 and 201 containing document by using yesterday's timestamps, starting 0:00 AM to 11:59 PM.
Attempt
I have tried the following solution. but distinct('slaveId') returns only the distinct specific field attribute only.
const latestPGStats = await PGStat
.find({
deviceId: { $in: deviceIds },
timestamp: { $lte: endTimestamp, $gte: startTimestamp }
})
.sort({ timestamp: -1 })
.distinct('slaveId')
.limit(2);
I have seen some peoples suggest using mongo aggregation. but I don't have knowledge about that domain.
You can try aggregate(),
$match your conditions
$facet to separate results, first is latest and second is oldest
$sort by timestamp in descending order
$group by slaveId and get $first document in latest and $last document in oldest
$limit` to get single document
const latestPGStats = await PGStat.aggregate([
{
$match: {
deviceId: { $in: deviceIds },
timestamp: { $lte: endTimestamp, $gte: startTimestamp }
}
},
{ $sort: { timestamp: -1 } },
{
$facet: {
latest: [
{
$group: {
_id: "$slaveId",
root: { $first: "$$ROOT" }
}
},
{ $limit: 1 }
],
oldest: [
{
$group: {
_id: "$slaveId",
root: { $last: "$$ROOT" }
}
},
{ $limit: 1 }
]
}
}
])
My schema:
export const MessagesAllSchema = new Schema({
senderName: {type: String, required: true},
senderId: {type: String, required: true},
content: String,
date: {type: Date, default: Date.now()},
roomId: Schema.Types.ObjectId,
});
My query:
AllMessages.find(
{roomId: [roomId1, roomId2]},
(err, messages) => {
console.log(messages);
},
).sort({date: -1});
My code return
My code returns several messages from room 1 and room 2.
I want to achieve
I want to my code return one message for room 1 and one message for room 2. If I apply .limi(2) I got a 2 message for room 1, but I want to get one message per room.
It is not possible with find(), You can try aggregate() method,
$match roomId in array of roomIds
$group by roomId and get first message form multiple grouped messages in root variable
$replceWith to replace root object in root
$sort by date in descending order
$limit 2 documents only
const messages = await AllMessages.aggregate([
{
$match: {
roomId: { $in: [roomId1, roomId2] }
}
},
{
$group: {
_id: "$roomId",
root: { $first: "$$ROOT" }
}
},
{ $replaceWith: "$root" },
{ $sort: { date: -1 } },
{ $limit: 2 }
]).exec();
console.log(messages);
Playground
I want to populate a date range picker display with highlighted cells where data exists in my database. I thus need to reduce my collection to an array of dates where records exist e.g.
// collection
[{
timestamp: ISODate("2020-01-28T20:42:00.000Z"),
data: 1,
},{
timestamp: ISODate("2020-01-28T18:42:00.000Z"),
data: 10,
},{
timestamp: ISODate("2020-01-28T15:42:00.000Z"),
data: 100,
},{
timestamp: ISODate("2020-01-25T15:42:00.000Z"),
data: 1000,
},{
timestamp: ISODate("2020-01-17T15:42:00.000Z"),
data: 10000,
}]
reduces to:
['2020-01-28', '2020-01-25', '2020-01-17']
The nature of the data stored in my database means that if any data exists on a given date, lots of data exists on that date. It is therefore slow to query the entire collection for a given date range and then reduce the result.
Is there a fast(er) way to query a collection to return the distinct set of dates on which data exists?
As I know you can only get json format result from mongodb query.
I could get the following result, which can be easily converted to the string array in javascript code:
[
{
"_id": "20200125"
},
{
"_id": "20200117"
},
{
"_id": "20200128"
}
]
I used $dateToString aggregation operator inside $project stage.
db.collection.aggregate([
{
$project: {
_id: 0,
date: {
$dateToString: {
format: "%Y%m%d",
date: "$timestamp"
}
}
}
},
{
$group: {
_id: "$date"
}
}
])
Playground