Nested populate match query - mongodb

Here is an example of the data I am working with:
// group
{
name: 'Group 1',
phases: [ {name: 'Period 1', startDate: "some date", endDate: "someDate", schedules: [{startTime: '1:00', endTime: '2:00', schedule: 'mongoIdRef'}]}, { name: 'Period 2', startDate: "some date", endDate: "someDate", schedules: []}, { name: 'Period 3', startDate: "some date", endDate: "someDate", schedules: []}, { name: 'Period 4', startDate: "some date", endDate: "someDate", schedules: [] } ]
}
// schedule
{
condition: 'active',
tz: 'US/Mountain',
daysOfWeek: [{startTime: 9, endTime: 22, days: [2,3,4,5]}, {startTime: 2, endTime: 5, days: [2,5,6]}]
}
The group's nested "schedule" property needs to get populated. I am doing this successfully here:
Group.statics.findForDate = function (date) {
date = moment(date);
return this.findForDate({
phases: {
$elemMatch: {
startDate: {$lte: date},
endDate: {$gte: date }
}
}
}).populate( {
{
path: 'phases.schedules.schedule',
match: {'daysOfWeek': { $elemMatch: {days: date.day() } } }
}
})
}
However, the match doesn't match properly. I want to return only groups that have a schedule that has the date.day() (which is a number 0-6) in the days array. How can I get .match to do this? Is there another approach?

Related

How to find data by funcion in mongoose?

I have a collection in mongodb using mongoose packages like below
[
{
_id: new ObjectId("62ae97b6be08b688f93f2c07"),
reportId: '1',
method: 'A1',
category: 'B2',
date: '2022-06-19',
time: '22:55',
emergency: 'normal',
__v: 0
},
{
_id: new ObjectId("62ae97b6be08b688f93f2c08"),
reportId: '2',
method: 'A3',
category: 'B5',
date: '2022-06-18',
time: '23:05',
emergency: 'normal',
__v: 0
},
{
_id: new ObjectId("62ae97b6be08b688f93f2c09"),
reportId: '3',
method: 'A5',
category: 'B1',
date: '2022-06-19',
time: '23:55',
emergency: 'urgent',
__v: 0
}
]
and I want to filter this data, and here is my find function()
const options = [
{ method: { $in: ['A1','A2'] } },
{ emergency: { $in: data.emergency } },
{ category: { $in: data.category } }
];
const response = await Report.find({ $or: options,});
Until now, it works perfectly, but I still got one more filter: the date and time (They are all int type String).
I want to search for the range date and time between last night after 23 o'clock to 23 o'clock tonight.
But I have no idea how to write the query, please help me figure it out, thanks!!!
Here is my testing query:
date: {
$where: function () {
const yesterday = moment().subtract(1, 'days').format('YYYYMMDD') + '2300';
const date = moment(this.date).format('YYYYMMDD') + this.time.replace(':', '');
const today = moment().format('YYYYMMDD') + '2300';
return yesterday < date && date <= today;
},
},
I would recommend you to first save dates in a format that supports comparators (lt, gt, etc.) such as a Timestamp or even a plain number. That way you can use MongoDB filters like this:
let filter = {
date: { $gt: new Date(..), $lt: new Date(..) }
}
// This can be executed directly in the Mongo shell.
{
created_on: {
$gt: ISODate("1972-12-12"),
$lt: ISODate('2022-06-019')
}
}
But if you insist on using strings, then you would need to filter all your documents, and then parse each one to do your comparison.

Strapi mongodb project specific fields in embedded documents

I'm trying to select especific fields from this document
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
cover: {
_id: 60d72c815e57e6015e38a4c7,
name: 'project4.png',
alternativeText: '',
caption: '',
hash: 'project4_3e8c906bdf',
ext: '.png',
mime: 'image/png',
size: 540.02,
width: 1895,
height: 883,
url: '/uploads/project4_3e8c906bdf.png',
formats: [Object],
provider: 'local',
related: [Array],
createdAt: 2021-06-26T13:32:49.389Z,
updatedAt: 2021-06-28T01:43:57.469Z,
__v: 0,
created_by: '60d69246c6f06d00f64312b0',
updated_by: '60d69246c6f06d00f64312b0',
id: '60d72c815e57e6015e38a4c7'
},
id: '60d9295db34f1c0144c9b8d4'
}
and I want get something like this:
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
cover: { url: '/uploads/project4_3e8c906bdf.png'}
id: '60d9295db34f1c0144c9b8d4'
}
I'm using Strapi with Mongodb and y was trying with the following code:
const requiredData = {
title: 1,
slug: 1,
date: 1,
category: 1,
cover: { url: 1 }
}
const populateData = [
{
path: 'category',
select: ['title', 'slug']
}
]
let editorsPick = await strapi.query('posts')
.model.find({ editorsPick: true}, requiredData)
.limit(3)
.populate(populateData)
But I only have this:
{
_id: 60d9295db34f1c0144c9b8d4,
title: 'Este es el titulo',
slug: 'este-es-el-titulo',
date: '2021-06-27',
category: {
_id: 60d92921b34f1c0144c9b8d2,
title: 'Categoria 1',
slug: 'categoria-1',
id: '60d92921b34f1c0144c9b8d2'
},
id: '60d9295db34f1c0144c9b8d4'
}
(Without cover image), and then try with:
let editorsPick = await strapi.query('posts')
.model.aggregate([
{"$match": { editorsPick: true}},
{"$project": requiredData}
])
But I got an empty array []
Please I need a little help, and sorry for my english

Group by and retain original fields

I see in mongodb aggregations, specially in $group we can use accumulator to create new fields. But i want the old keys
Suppose i have this data
[
{ name: "My Plan 101", billingCycle: 'day', amount: 1, credits: 100, price: 7 },
{ name: "My Plan 102", billingCycle: 'day', amount: 1, credits: 150, price: 10 },
{ name: "My Plan 103", billingCycle: 'day', amount: 2, credits: 150, price: 15 },
{ name: "My Plan 104", billingCycle: 'month', amount: 3, credits: 150, price: 15 },
{ name: "My Plan 105", billingCycle: 'month', amount: 3, credits: 200, price: 20 },
]
Then the aggregation should be like
[
'day': {
'1': [{ name: 'My Plan 101' }, { name: 'My Plan 102' }],
'2': [{ name: 'My Plan 103' }]
},
'month': {
'3': [{ name: 'My Plan 104' }, { name: 'My Plan 105' }]
}
]
I tried a lot with mongodb aggregation but couldn't solve it so I used lodash for this.
let plans = await Plan.find()
plans = _.groupBy(plans, 'billingCycle');
for (const billingCyle in plans) {
let $plans = plans[billingCyle];
plans[billingCyle] = _.groupBy($plans, "amount")
}
console.log(plans)
The above snipped has solved my problem

Find events within a month and year

Let's say I have a event schema below
eventSchema = new Schema({
name: { type: String },
startDate: {type: Date },
endDate: {type: Date }
});
And I have a set of data:
{ name: 'Event 1'
startDate: '2017-06-23 09:58:03.759Z'
endDate: '2017-07-02 09:58:03.759Z'
},
{ name: 'Event 2'
startDate: '2017-07-05 09:58:03.759Z'
endDate: '2017-07-10 09:58:03.759Z'
}
{ name: 'Event 3'
startDate: '2017-07-23 09:58:03.759Z'
endDate: '2017-08-01 09:58:03.759Z'
}
{ name: 'Event 4'
startDate: '2017-08-03 09:58:03.759Z'
endDate: '2017-08-05 09:58:03.759Z'
}
How to write a query to find the events that occurred within 2017-07-01 to 2017-07-31?
Which means I need to get the data set of Event 1, 2 and 3.
Here is what I done so far:
Event.find({
$and: [
{ startDate: { $gte: start } },
{ endDate: { $lte: end } },
]
}, (err, events) => {
if (err) return next(err);;
res.send({ success: true, events: events });
});

MongoDb Aggregate per subdocument

I have these collections, stats and items. Stats has one item as subdocument:
var ItemSchema = new Schema({
type: String,
created_at: Date,
updated_at: {
type: Date,
default: Date.now()
}
});
var StatsSchema = new Schema({
item: {
type: Schema.ObjectId,
ref: 'Item'
},
url: String,
date: Date,
action: String,
hourly: Number
});
I'd like to aggregate Stats grouping by item.type. Is it possible?
I tried something like this but without luck:
db.stats.aggregate(
{ $project: { _id: 1, hourly: 1, action: 1, item: 1, type: '$item.type' } },
{ $group: { _id: { action: '$action', type: '$item.type' }, total: { $sum: '$hourly' } } }
)
You should not need the $project part of the pipeline.
You should be get what you need from the $group stage
db.stats.aggregate({ $group: { _id: "$item.type" , total: { $sum: '$hourly' } } });