@@ -149,8 +149,11 @@ class EventsFactory extends Factory {
149149 * @param {Number } limit - events count limitations
150150 * @param {Number } skip - certain number of documents to skip
151151 * @param {'BY_DATE' | 'BY_COUNT' } sort - events sort order
152- * @param {EventsFilters } filters - marks by which events should be filtered
153- * @param {String } search - Search query
152+ * @param {Object } filters - filter object
153+ * @param {Object.<string, boolean> } [filters.marks] - mark filters: e.g. { starred: true, ignored: false }
154+ * @param {string|number } [filters.dateFrom] - filter start date (ISO or timestamp)
155+ * @param {string|number } [filters.dateTo] - filter end date (ISO or timestamp)
156+ * @param {String } search - search query
154157 *
155158 * @return {RecentEventSchema[] }
156159 */
@@ -165,9 +168,6 @@ class EventsFactory extends Factory {
165168 throw new Error ( 'Search parameter must be a string' ) ;
166169 }
167170
168- /**
169- * Check if pattern is safe RegExp
170- */
171171 if ( ! safe ( search ) ) {
172172 throw new Error ( 'Invalid regular expression pattern' ) ;
173173 }
@@ -231,13 +231,41 @@ class EventsFactory extends Factory {
231231 }
232232 : { } ;
233233
234- const matchFilter = filters
235- ? Object . fromEntries (
236- Object
237- . entries ( filters )
238- . map ( ( [ mark , exists ] ) => [ `event.marks.${ mark } ` , { $exists : exists } ] )
239- )
240- : { } ;
234+ const matchFilter = {
235+ ...searchFilter ,
236+ } ;
237+
238+ // Filter by marks (event.marks.{key})
239+ if ( filters . marks && typeof filters . marks === 'object' ) {
240+ for ( const [ mark , exists ] of Object . entries ( filters . marks ) ) {
241+ matchFilter [ `event.marks.${ mark } ` ] = { $exists : exists } ;
242+ }
243+ }
244+
245+ // Filter by date (groupingTimestamp)
246+ if ( filters . dateFrom || filters . dateTo ) {
247+ matchFilter . groupingTimestamp = { } ;
248+
249+ if ( filters . dateFrom ) {
250+ const from = typeof filters . dateFrom === 'string'
251+ ? Math . floor ( new Date ( filters . dateFrom ) . getTime ( ) / 1000 )
252+ : filters . dateFrom ;
253+
254+ matchFilter . groupingTimestamp . $gte = from ;
255+ }
256+
257+ if ( filters . dateTo ) {
258+ const to = typeof filters . dateTo === 'string'
259+ ? Math . floor ( new Date ( filters . dateTo ) . getTime ( ) / 1000 )
260+ : filters . dateTo ;
261+
262+ matchFilter . groupingTimestamp . $lte = to ;
263+ }
264+
265+ if ( Object . keys ( matchFilter . groupingTimestamp ) . length === 0 ) {
266+ delete matchFilter . groupingTimestamp ;
267+ }
268+ }
241269
242270 pipeline . push (
243271 {
@@ -252,10 +280,7 @@ class EventsFactory extends Factory {
252280 $unwind : '$event' ,
253281 } ,
254282 {
255- $match : {
256- ...matchFilter ,
257- ...searchFilter ,
258- } ,
283+ $match : matchFilter ,
259284 } ,
260285 { $skip : skip } ,
261286 { $limit : limit } ,
@@ -272,17 +297,8 @@ class EventsFactory extends Factory {
272297 ) ;
273298
274299 const cursor = this . getCollection ( this . TYPES . DAILY_EVENTS ) . aggregate ( pipeline ) ;
275-
276300 const result = ( await cursor . toArray ( ) ) . shift ( ) ;
277301
278- /**
279- * aggregation can return empty array so that
280- * result can be undefined
281- *
282- * for that we check result existence
283- *
284- * extra field `projectId` needs to satisfy GraphQL query
285- */
286302 if ( result && result . events ) {
287303 result . events . forEach ( event => {
288304 event . projectId = this . projectId ;
0 commit comments