I have trouble to fill the fields of group in .populate({}), I tried many times but I could not understand the problem:
Company.find({
_id: req.body.idCompany,
})
.populate({
path: 'listAgencys',
model: 'Agency',
match: {
idClient: req.body.idClient,
createdAt: {
$gte: new Date(req.body.startDate),
$lte: new Date(req.body.endDate)
}
},
group: {
_id: {
subscriptionType: '$subscriptionName',
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
month: {
$let: {
vars: {
monthsInString: [, 'Jan.', 'Fev.', 'Mars', ......]
},
in: {
$arrayElemAt: ['$$monthsInString', { $month: '$createdAt' }]
}
}
}
},
countLines: { $sum: 1 },
} ,
group: {
_id: "$_id.subscriptionType",
info: {
$push: {
year: "$_id.year",
month: "$_id.month",
allLines: "$countLines",
}
},
}
})
.exec();
SCHEMAS:
const companySchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
trim: true,
},
listAgencys: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Agency",
}
]
});
module.exports = mongoose.model("Company", companySchema);
--
const agencySchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
idClient: {
type: Number,
trim: true,
//unique: true,
},
adress: {
type: String,
trim: true,
lowercase: true,
},
createdAt: {
type: Date,
default: Date.now()
},
listOfSubscribers: [{
numberSubscriber: {
type: Number,
},
numberPhone: {
type: Number,
},
subscriptionName: {
type: String
},
}],
});
module.exports = mongoose.model("Agency", agencySchema);
Example of parameters;
{
"idCompany": "5c71ba1c1376b034f8dbceb6",
"startDate":"2019-02-23",
"endDate":"2019-03-31",
"idClient" : "021378009"
}
I want to display the number of subscribers per month and subscriptionType according to idAgency and idCompany that will be passed in parameter.
Edit1 with aggregate:
Company.aggregate(
[
{ $unwind: "$listAgencys" },
{
$match: {
_id: req.body.idCompany,
idClient: req.body.idClient,
createdAt: {
"istAgencys.createdAt": {
$gte: new Date(req.body.startDate),
$lte: new Date(req.body.endDate)
}
}
},
{
$group: {
_id: {
subscriptionType: '$listAgencys.subscriptionName',
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
month: {
$let: {
vars: {
monthsInString: [, 'Jan.', 'Fev.', 'Mars', ......]
},
in: {
$arrayElemAt: ['$$monthsInString', { $month: '$createdAt' }]
}
}
}
}
countLines: { $sum: 1 },
} ,
{
$group: {
_id: "$_id.subscriptionType",
info: {
$push: {
year: "$_id.year",
month: "$_id.month",
allLines: "$countLines",
}
}
}
}
])
Result of edit1:
{
success: true,
datalines : []
}
Example of output:
{
"success": true,
"datalines": [
{
"idClient": 0213400892124
{
"_id": {
"subscriptionType": "ADL",
"year": 2019,
"month" : "Fev."
},
"allLines": 3,
},
{
"_id": {
"subscriptionType": "Lines",
"year": 2019,
"month" : "Jan."
},
"allLines": 10,
},
{
"_id": {
"subscriptionType": "Others",
"year": 2019,
"month" : "Mars"
},
"allLines": 35,
}
},
{
"idClient": 78450012365
.........
}
]
}
thank you in advance,
Related
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,
},
});
I want to get the business information with businessId as a reference. However, I can't get the correct data because the previous developer did not use ObjectId type on the model. Now what I want to do is convert the businessId type to objectId withough altering the model, it would be easy if I do it but the old data will be affected, which is not good. Please see below for the model
const scanHistory = new Schema({
businessId: { type: String },
domains: [
{
domainId: { type: String },
scanType: { type: String },
status: { type: String },
reportUrl: { type: String },
scanStart: { type: Date, default: Date.now },
scanFinish: { type: Date, default: Date.now }
}
],
scanStart: { type: Date, default: Date.now },
scanStatus: { type: String },
scanType: { type: String }
});
This is my aggregate query
.collection("scanhistories")
.aggregate([
{
$addFields: {
businessObjId: {
$convert: {
input: "businessId",
to: "objectId",
onError: "Could not convert to type ObjectId."
}
}
}
},
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $max: "$businessId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
{
$lookup: {
from: "businesses",
as: "businessInfo",
localField: "businessObjId",
foreignField: "_id"
}
},
{
$project: {
_id: 1,
businessObjId: 1,
primaryDomain: { $arrayElemAt: ["$businessInfo.primaryDomain", 0] },
businessName: { $arrayElemAt: ["$businessInfo.businessName", 0] },
frequency: { $arrayElemAt: ["$businessInfo.scanFrequency", 0] },
scanStatus: 1,
domains: 1
}
},
{
$match: {
scanStatus: { $in: ["running", "undef"] },
domains: { $exists: true }
}
}
])
.toArray();
for (let x = 0; x < history.length; x++) {
console.log(history[x]);
}
Now the output is like this which is not the one I expected.
{ _id: 5de09321bdb7cc07b7595de4,
businessObjId: 5de09321bdb7cc07b7595de4,
scanStatus: 'undef',
domains:
[ { _id: 5dfa626300007c243c1528b3,
domainId: '5de09321bdb7cc07b7595de5',
scanType: 'scheduled',
status: 'running',
reportUrl: '',
scanStart: 2019-12-18T17:31:14.754Z,
scanFinish: 2019-12-18T17:31:14.754Z } ] }
The expected result should have been with the lookup businessInfo that I wanted
{ _id: 5de09321bdb7cc07b7595de4,
businessObjId: 5de09321bdb7cc07b7595de4,
scanStatus: 'undef',
domains:
[ { _id: 5dfa626300007c243c1528b3,
domainId: '5de09321bdb7cc07b7595de5',
scanType: 'scheduled',
status: 'running',
reportUrl: '',
scanStart: 2019-12-18T17:31:14.754Z,
scanFinish: 2019-12-18T17:31:14.754Z } ],
primaryDomain: "mydomainxxx.xy",
businessName: "The biz",
scanFrequency: "daily"
}
Can you help me? I am really new to MongoDB and my background is PHP/SQL so all advises will be much appreciated. Thank you!
So I have found the solution for this. It was just a single mistake. :(
So on the aggregate code above where the group pipeline is. I made a mistake here.
Previous code above
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $max: "$businessId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
Correct one
I change this part here:
businessObjId: { $first: "$businessObjId" }
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $first: "$businessObjId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
I have a query (aggregate) and I need to calculate a totalizer for some fields (value and comissionValue) and count how many registers this query have.
My query(aggregate)
let findTerm = {
store: req.body.store,
status: {
$in: resultStatusServices
}
}
if (req.body.start) {
findTerm.scheduleStart = {
$lte: req.body.start
};
}
if (req.body.period) {
findTerm.scheduleEnd = {
$gte: req.body.period
};
}
Schedule.aggregate([{
$match: findTerm
},
{
$project: {
"employee.name": 1,
"customer.name": 1,
"service.name": 1,
"value": 1,
"scheduleDate": 1,
"scheduleStart": 1,
"scheduleEnd": 1,
"comissionValue": 1,
"status": 1,
"paymentMethod": 1
}
},
{
$group:{
_id: {
"employee.name" : "$employee.name",
"customer.name" : "$customer.name",
"service.name": "$service.name",
"value": "$value",
"scheduleDate": "$scheduleDate",
"scheduleStart" :"$scheduleStart",
"scheduleEnd": "$scheduleEnd",
"comissionValue" : "$comissionValue",
"status" : "$value",
"paymentMethod" : "$paymentMethod"
},
}
},
{
$match: findTerm
},
{
$group: {
_id: {
id: "$store"
},
totalValue: {
$sum: "$value"
},
totalServices: {
"$sum": 1
},
totalComission: {
$sum: "$comissionValue"
},
count: {
$sum: 1
}
}
},
{
$sort: sort
},
{
$skip: req.body.limit * req.body.page
},
{
$limit: req.body.limit
}
Schedule (model)
store: {
type: String,
required: true
},
customer: {
id: {
type: String
},
name: {
type: String
},
avatar: String,
phone: {
type: String
},
email: { type: String },
doc: {
type: String
},
},
employee: {
id: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: String,
},
service: {
id: {
type: String
},
name: {
type: String,
required: true
},
filters: [String]
},
info: {
channel: {
type: String,
required: true,
default: 'app'
},
id: String,
name: String
},
scheduleDate: {
type: String,
required: true
},
scheduleStart: {
type: String,
required: true
},
scheduleEnd: {
type: String,
required: true
},
value: {
type: Number
},
comissionType: {
type: String,
default: '$'
},
comissionValue: {
type: Number,
default: 0
},
status: {
type: Number,
required: true
},
observation: String,
paymentMethod: {
type: Number,
default: 0
},
What I'am trying to do as a result of this query:
[
0:{
comissionValue: 14
customer: {name: "Marcelo"}
employee: {name: "Andy"}
paymentMethod: 0
scheduleDate: "2019-01-01"
scheduleEnd: "2019-01-01 09:30"
scheduleStart: "2019-01-01 09:00"
service: {name: "Barber"}
status: 2
value: 20
_id: "5c26275ffe046d25a07cb466"}
1: {...}
2: {...}
...
],[totalizers: { count: 2, totalServices: 50, totalComission:65}]
How can i do this, how can i make this totalizers?
You can use $facet to accomplish this type of query since it allows you to run various aggregations on the same set of input documents, without needing to retrieve the input documents multiple times. The first facet can have the query pipeline with the sort and limit and the other facet will yield the aggregate sums (totalizers).
For instance the following aggregate operation will give you the desired result:
Schedule.aggregate([
{ '$match': findTerm },
{ '$facet': {
'data': [
{ '$project': {
'employee.name': 1,
'customer.name': 1,
'service.name': 1,
'value': 1,
'scheduleDate': 1,
'scheduleStart': 1,
'scheduleEnd': 1,
'comissionValue': 1,
'status': 1,
'paymentMethod': 1
} },
{ '$sort': sort },
{ '$skip': req.body.limit * req.body.page },
{ '$limit': req.body.limit }
],
'totalizer': [
{ '$group': {
'_id': '$store',
'count': { '$sum': 1 },
'totalValue': { '$sum': '$value' },
'totalComission': { '$sum': '$comissionValue' }
} },
{ '$group': {
'_id': null,
'storesCount': {
'$push': {
'store': '$_id',
'count': '$count'
}
},
'totalValue': { '$sum': '$totalValue' },
'totalServices': { '$sum': '$count' },
'totalComission': { '$sum': '$totalComission' }
} }
]
} }
]).exec((err, results) => {
if (err) handleError(err);
console.log(results[0]);
})
I'm making an in app messaging system in which I have to show the list of conversations with their last message and the unread count. My schema is as follows--
var schema = new Schema({
senderID: {
type: Schema.Types.ObjectId,
ref: 'Member'
},
receiversID: [{
type: Schema.Types.ObjectId,
ref: 'Member'
}],
content: {
type: String,
default: ''
},
isRead: {
type: Boolean,
default: false,
},
createdAt: {
type: Number,
default: Date.now
}
});
I did this initially to get all the conversations with their last message --
messageModel.aggregate(
[{ $match: { senderID: userId } },
{ $unwind: '$receiversID' },
{ $sort: { createdAt: -1 } },
{ $group: { _id: '$receiversID', unreadCount: { $sum: { $cond: [{ $eq: ["$isRead", false] }, 1, 0] } }, senderID: { $first: '$senderID' }, receiversID: { $first: '$receiversID' }, content: { $first: '$content' } } },
{ $skip: pagingData.pageSize * (pagingData.pageIndex - 1) },
{ $limit: pagingData.pageSize }
], function (err, docs) {
resolve(docs);
}
);
But it doesn't shows the messages if you are a receiver. I want to show the conversation whether you are receiver or sender.
i use something like this:
{
'$or': [
{
'$and': [
{
'receiversID': userId
}, {
'senderID': toUserId
}
]
}, {
'$and': [
{
'receiversID': toUserId
}, {
'senderID': userId
}
]
},
],
}
Sale.aggregate({
$match: filter
}, {
$group: {
"_id": {
"store_id": "$store_id",
//"created_on": { $dateToString: { format: "%Y-%m-%d", date: "$strBillDate" } }
},
month: {
$month: "$strBillDate"
},
store_id: {
$first: "$store_id"
},
strBillAmt: {
$sum: "$strBillAmt"
},
strBillNumber: {
$sum: 1
}
}
})
Instead of date, I need to group sales in months, how to group sales in months in nodejs
I used a projection first in the aggregate chain to extract monthly and yearly values and did the grouping afterwards:
<doc-name>.aggregate([
{ $project:
{ _id: 1,
year: { $year: "$date" },
month: { $month: "$date"},
amount: 1
}
},
{ $group:
{ _id: { year: "$year", month: "$month" },
sum: { $sum: "$amount" }
}
}])
I also tried with your model:
var testSchema = mongoose.Schema({
store_id: { type: String },
strBillNumber: { type: String },
strBillDate: { type: Date },
strBillAmt: { type: Number }
});
var Test = mongoose.model("Test", testSchema);
Create some test data:
var test = new Test({
store_id: "1",
strBillNumber: "123",
strBillDate: new Date("2016-04-02"),
strBillAmt: 25
});
var test2 = new Test({
store_id: "1",
strBillNumber: "124",
strBillDate: new Date("2016-04-01"),
strBillAmt: 41
});
var test3 = new Test({
store_id: "3",
strBillNumber: "125",
strBillDate: new Date("2016-05-13"),
strBillAmt: 77
});
Run the query:
Test.aggregate([
{ $project:
{ store_id: 1,
yearBillDate: { $year: "$strBillDate" },
monthBillDate: { $month: "$strBillDate" },
strBillAmt: 1
}
},
{ $group:
{ _id: {yearBillDate: "$yearBillDate", monthBillDate:"$monthBillDate"},
sum: { $sum: "$strBillAmt" }
}
}
], function(err, result) {
console.log(err, result)});
And got a reasonable result: