Mongoose: Calculate the number of grouped documents - node.js

I have this mongoose query:
const filter_stage = {
$match: {
category: "LOGIN",
},
};
const active_users_group_stage = {
$group: {
_id: "$user",
number_of_logins: { $sum: 1 },
},
};
const pipeline = [filter_stage, active_users_group_stage];
const active_users_stats = await History.aggregate(pipeline);
response.active_users_stats = active_users_stats;
const number_of_active_users = active_users_stats.length;
This is the History model:
const HistorySchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users",
},
category: {
type: String,
enum: ["LOGIN","LOGOUT"],
required: true,
},
date: {
type: Date,
default: Date.now,
},
});
And it returns something like this:
[
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 45
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 36
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 26
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 18
},
{
"_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number_of_logins": 18
},
]
Instead of calculating the number of active users like this:
const number_of_active_users = active_users_stats.length;
I would like to calculate it inside the aggregate pipeline.
Is this possible?

You need $group all with _id: null to sum all active_users and add all documents into the users array as the last stage.
{
$group: {
_id: null,
number_of_active_users: {
$sum: 1
},
users: {
$push: "$$ROOT"
}
}
}
As the active_users_stats result will return an array with only one document,
...
const all_group_stage = {
$group: {
_id: null,
number_of_active_users: {
$sum: 1
},
users: {
$push: "$$ROOT"
}
},
};
const pipeline = [filter_stage, active_users_group_stage, all_group_stage];
const active_users_stats = await History.aggregate(pipeline);
response.active_users_stats = active_users_stats[0].users;
const number_of_active_users = active_users_stats[0].number_of_active_users;

Related

How can I query data between date range in array mongoose using express

I'm trying to query for multiple dates in date ranges in mongoose.
member: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Member",
},
point: {
type: Array,
},
recommended: [
{
member: {
type: mongoose.Schema.Types.ObjectId,
ref: "Member",
},
money: { type: Number },
date: {
type: Date,
default: new Date(),
},
},
],
matching: {
type: Array,
},
this is my model
{ const { date } = req.body;
let start = new Date(date[0]);
let end = new Date(date[1]); }
let result = await Income.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(req.params.id),
recommended: { $elemMatch: { $gt: start, $lt: end } },
},
},
{
$project: {
recommended: {
$filter: {
input: "$recommended",
as: "item",
cond: {
$and: [
{ $gt: ["$$item.date", start] },
{ $lt: ["$$item.date", end] },
],
},
},
},
},
},
]);
I send date from react antd date picker date: ["2022-06-20", "2022-06-25"]
this is controller
I don't understand why it's won't work what is the problem it's no error
api return empty array

How to aggregate with many conditions on MongoDB. Double $lookup etc

How to display "hardest category" based on in which "study" size of notLearnedWords was the highest. MongoDB Aggregation
I have these 3 models:
Study
WordSet
Category
Study model has reference into WordSet, then WordSet has reference into Category.
And based on Studies i'm displaying statistics.
How i can display "The hardest category" based on size of "notLearnedWords" was the highest?
I don't know on which place i should start with that querying.
For now i display "hardestCategory" as element that is most used.
I think that condition would look something like this:
{ $max: { $size: '$notLearnedWords' } } // size of the study with most notLearnedWords
I would achieve a response like this:
"stats": [
{
"_id": null,
"numberOfStudies": 4,
"averageStudyTime": 82.5,
"allStudyTime": 330,
"longestStudy": 120,
"allLearnedWords": 8
"hardestCategory": "Work" // only this field is missing
}
]
I've tried to do it like this:
const stats = await Study.aggregate([
{ $match: { user: new ObjectID(currentUserId) } },
{
$lookup: {
from: 'users',
localField: 'user',
foreignField: '_id',
as: 'currentUser',
},
},
{
$lookup: {
from: 'wordsets',
let: { wordSetId: '$learnedWordSet' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$wordSetId'] } } },
{
$project: {
_id: 0,
category: 1,
},
},
{ $unwind: '$category' },
{
$group: {
_id: '$category',
count: { $sum: 1 },
},
},
{ $sort: { count: -1 } },
{ $limit: 1 },
{
$lookup: {
from: 'categories',
localField: '_id',
foreignField: '_id',
as: 'category',
},
},
{
$project: {
_id: 0,
category: { $arrayElemAt: ['$category.name', 0] },
},
},
],
as: 'wordSet',
},
},
{
$group: {
_id: null,
numberOfStudies: { $sum: 1 },
averageStudyTime: { $avg: '$studyTime' },
allStudyTime: { $sum: '$studyTime' },
longestStudy: { $max: '$studyTime' },
allLearnedWords: {
$sum: { $size: '$learnedWords' },
},
hardestCategory: {
$first: {
$first: '$wordSet.category',
},
},
studyWithMostNotLearnedWords: { $max: { $size: '$notLearnedWords' } },
},
},
]);
Study
const studySchema = new mongoose.Schema({
name: {
type: String,
},
studyTime: {
type: Number,
},
learnedWords: [String],
notLearnedWords: [String],
learnedWordSet: {
type: mongoose.Schema.Types.ObjectId,
ref: 'WordSet',
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
});
WordSet
const wordSetSchema = new mongoose.Schema({
name: {
type: String,
},
category: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Category',
required: true,
},
],
},
});
Category
const categorySchema = new mongoose.Schema({
name: {
type: String,
},
});

$lookup with condition in mongoose

I have 2 schemas, this is parent collection schema:
const TimesheetSchema = Schema({
managersComment: {
type: String,
},
weekNum: {
type: Number,
},
year: {
type: Number,
},
user: { type: Schema.Types.ObjectId, ref: userModel },
status: {
type: String,
enum: ["Saved", "Submitted", "Approved", "Rejected"],
},
data: [{ type: Schema.Types.ObjectId, ref: TimesheetIndividualData }]
});
This is child collection schema
const TimesheetDataSchema = new Schema(
{
workingDate: {
type: Date,
},
dayVal: {
type: Number,
},
user: { type: Schema.Types.ObjectId, ref: userModel },
parentId: { type: String }
},
{ timestamps: true }
);
In TimesheetDataSchema parentId is basically the _id from TimesheetSchema.
Now i need to run a query which return docs from TimesheetDataSchema, but only the docs in which parentId(ObjectId) of TimesheetSchema has status Approved.
I am trying to do $lookup, but currently no success. Please help.
EDIT: Based upon #ashh suggestion tried this: but getting empty array.
const result = await TimesheetIndividualData.aggregate([
{
"$lookup": {
"from": "timesheetModel",
"let": { "parentId": "$parentId" },
"pipeline": [
{ "$match": { "status": "Approved", "$expr": { "$eq": ["$weekNum", "$parentId"] } } },
],
"as": "timesheet"
}
},
{ "$match": { "timesheet": { "$ne": [] } } }
])
You can use below aggregation
const result = await db.TimesheetDataSchema.aggregate([
{ "$lookup": {
"from": "TimesheetSchema",
"let": { "parentId": "$parentId" },
"pipeline": [
{ "$match": { "status": "approved", "$expr": { "$eq": ["$_id", "$$parentId"] }}},
],
"as": "timesheet"
}},
{ "$match": { "timesheet": { "$ne": [] }} }
])
But I would prefer two queries for better performance here
const timesheets = (await db.TimesheetSchema.find({ status: "approved" }, { _id: 1 })).map(({ _id }) => _id)
const result = await db.TimesheetDataSchema.find({ parentId: { $in: timesheets } })

Mongoose Populate return empty array with joining and generating children from other table

I have looked at all of the cases on StackOverflow but I could not fix this error:
You can find the whole code here :
Link to SandBox
I have 2 collections: "pars" for parents and "chil" for children
pars:
[{
"_id": {
"$oid": "607091fa48ea8376b9398ba7"
},
"name": "ParA"
},{
"_id": {
"$oid": "6070920748ea8376b9398ba8"
},
"name": "ParB"
}]
chil:
[{
"_id": {
"$oid": "6070921648ea8376b9398ba9"
},
"name": "Chil1",
"parRef": {
"$ref": "chils",
"$id": {
"$oid": "6070920748ea8376b9398ba8"
}
},
"parId": "6070920748ea8376b9398ba8"
},{
"_id": {
"$oid": "6070922648ea8376b9398baa"
},
"name": "Chil2",
"parRef": {
"$ref": "chils",
"$id": {
"$oid": "607091fa48ea8376b9398ba7"
}
},
"parId": "607091fa48ea8376b9398ba7"
},{
"_id": {
"$oid": "6070923348ea8376b9398bab"
},
"name": "Chil3",
"parRef": {
"$ref": "chils",
"$id": {
"$oid": "607091fa48ea8376b9398ba7"
}
},
"parId": "607091fa48ea8376b9398ba7"
}]
I am using populate with mongoose but it returns an empty array
Code
const Schema = mongoose.Schema;
const getParChil = async () => {
const schemaPar = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
chils: [
{
type: Schema.Types.ObjectId,
ref: "modelChil"
}
]
},
{ collection: "pars" }
);
const schemaChil = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
parId: { type: Schema.Types.ObjectId, ref: "modelPar" }
},
{ collection: "chils" }
);
const modelChil = mongoose.model("modelChil", schemaChil);
const modelPar = mongoose.model("modelPar", schemaPar);
const res = await modelPar.find({}).populate("chil");
console.log("result", res);
};
getParChil();
response:
[
{ chils: [], _id: 607091fa48ea8376b9398ba7, name: 'ParA' },
{ chils: [], _id: 6070920748ea8376b9398ba8, name: 'ParB' }
]
Please advise. I would like to join these two tables and get the result like this:
expected behaviour
[
{
_id: 607091fa48ea8376b9398ba7,
name:"ParA",
chils:[{"name":"Chil2"},{"name":"Chil3"}]
},
{
_id: 6070920748ea8376b9398ba8,
name:"ParB",
chils:[{"name":"Chil1"}]
}
]
You should reference the model name and not the collection name:
const schemaPar = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
chils: [
{
type: Schema.Types.ObjectId,
ref: "Chil", // change made here
},
],
},
{ collection: "pars" }
);
const schemaChil = new Schema(
{
_id: Schema.Types.ObjectId,
name: String,
parId: {
type: Schema.Types.ObjectId,
ref: "Par" // change made here
},
},
{ collection: "chils" }
);
modelChil=mongoose.model("Chil", schemaChil);
modelPar=mongoose.model("Par", schemaPar);

Find duplicate enteries from collection node js?

I want to find out duplicate entries on the basis of field " name " and in the result, I want to fetch " name " and " _id ".
I have tried using aggregate function and it is returning all the result from database as null:
here is my code:
ModelRestaurant.aggregate((
// { "$group": { "_id": "$name", "count": { "$sum": 1 } } },
// { "$match": { "_id": { "$ne": null }, "count": { "$gt": 1 } } },
// { "$project": { "name": "$_id", "_id": 0 } }
{ $group: { "_id": "$name", "name": { $first: "$name" }, "count": { $sum: 1 } } },
{ $match: { "count": { $gt: 1 } } },
{ $project: { "name": 1, "_id": 0 } },
{ $group: { "_id": null, "duplicateNames": { $push: "$name" } } },
{ $project: { "_id": 0, "duplicateNames": 1 } }
), function (err, result) {
if (result) {
console.log(result)
}
})
from above commented code it is giving me all id but now it is giving me null values and all the result json of 6000 with null value.
here is the field of data in collection:
My Schema is :
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var autoIncrement = require('mongoose-auto-increment');
var ModelRestaurant = new mongoose.Schema({
name: String,
ownerName: String, // to be removed by checking code
ownerID: Number,
ownerEmail: String, // to be removed by checking code
city: String,
zipCode: String,
image: [String],
restaurantType: String,
address: String,
landmark: String,
isAddedByCustomer: {
type: Boolean,
default: false
},
location: {
lat: String,
long: String
}
},
{
timestamps: true
});
ModelRestaurant.plugin(autoIncrement.plugin, 'Restaurants');
ModelRestaurant.plugin(uniqueValidator);
mongoose.model('Restaurants', ModelRestaurant);
The following code should print all the duplicates:
db.ModelRestaurant.aggregate([
{$group:{"_id":"$name","name":{$first:"$name"},"count":{$sum:1}}},
{$match:{"count":{$gt:1}}}
])

Resources