Skip to content

Commit 4cd5782

Browse files
committed
imp(): daily events cursor types
1 parent ffd9040 commit 4cd5782

File tree

3 files changed

+99
-15
lines changed

3 files changed

+99
-15
lines changed

src/models/eventsFactory.js

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,17 @@ const { composeEventPayloadByRepetition } = require('../utils/merge');
3737
* @property {Event} event - one certain event that represents all of the repetitions this day
3838
*/
3939

40+
/**
41+
* @typedef {Object} DailyEventsCursor
42+
* @property {Number} groupingTimestampBound - boundary value of groupingTimestamp field of the last event in the portion
43+
* @property {Number} sortValueBound - boundary value of the field by which events are sorted (count/affectedUsers/lastRepetitionTime) of the last event in the portion
44+
* @property {String} idBound - boundary value of _id field of the last event in the portion
45+
*/
46+
4047
/**
4148
* @typedef {Object} DaylyEventsPortionSchema
4249
* @property {DailyEventSchema[]} dailyEvents - original event of the daily one
43-
* @property {String | null} nextCursor - pointer to the first dailyEvent of the next portion, null if there are no dailyEvents left
50+
* @property {DailyEventsCursor | null} nextCursor - object with boundary values of the first event in the next portion
4451
*/
4552

4653
/**
@@ -171,7 +178,7 @@ class EventsFactory extends Factory {
171178
* Returns events that grouped by day
172179
*
173180
* @param {Number} limit - events count limitations
174-
* @param {String} paginationCursor - pointer to the first daily event to be selected
181+
* @param {DailyEventsCursor} paginationCursor - object that contains boundary values of the last event in the previous portion
175182
* @param {'BY_DATE' | 'BY_COUNT'} sort - events sort order
176183
* @param {EventsFilters} filters - marks by which events should be filtered
177184
* @param {String} search - Search query
@@ -196,10 +203,6 @@ class EventsFactory extends Factory {
196203
throw new Error('Invalid regular expression pattern');
197204
}
198205

199-
const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
200-
201-
limit = this.validateLimit(limit);
202-
203206
switch (sort) {
204207
case 'BY_COUNT':
205208
sort = 'count';
@@ -215,18 +218,55 @@ class EventsFactory extends Factory {
215218
break;
216219
}
217220

221+
const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
222+
223+
limit = this.validateLimit(limit);
224+
218225
const pipeline = [
219226
{
220227
$match: paginationCursor ? {
221-
_id: {
222-
$lte: new ObjectID(paginationCursor),
223-
},
228+
/**
229+
* This condition is used for cursor-based pagination
230+
* We sort result by groupingTimestamp desc, [sort] desc, _id desc
231+
* So we need to fetch documents that are less than the last document of the previous portion (based on all three conditions)
232+
*/
233+
$or: [
234+
{
235+
/**
236+
* If groupingTimestamp is less than the cursors one
237+
* - daily events of the next day
238+
*/
239+
groupingTimestamp: { $lt: paginationCursor.groupingTimestampBound },
240+
},
241+
{
242+
/**
243+
* If groupingTimestamp equals to the cursor one, but [sort] is less than the cursors one
244+
* - daily events of the same day, but with less count/affectedUsers/lastRepetitionTime
245+
*/
246+
$and: [
247+
{ groupingTimestamp: paginationCursor.groupingTimestampBound },
248+
{ [sort]: { $lt: paginationCursor.sortValueBound } },
249+
],
250+
},
251+
{
252+
/**
253+
* If groupingTimestamp and [sort] equals to the cursors ones, but _id is less or equal to the cursors one
254+
* - daily events of the same day with the same count/affectedUsers/lastRepetitionTime, but that were created earlier
255+
*/
256+
$and: [
257+
{ groupingTimestamp: paginationCursor.groupingTimestampBound },
258+
{ [sort]: paginationCursor.sortValueBound },
259+
{ _id: { $lte: new ObjectID(paginationCursor.idBound) } },
260+
],
261+
},
262+
],
224263
} : {},
225264
},
226265
{
227266
$sort: {
228267
groupingTimestamp: -1,
229268
[sort]: -1,
269+
_id: -1,
230270
},
231271
},
232272
];
@@ -297,7 +337,10 @@ class EventsFactory extends Factory {
297337
$unwind: '$event',
298338
},
299339
{
300-
$unwind: '$repetition',
340+
$unwind: {
341+
path: '$repetition',
342+
preserveNullAndEmptyArrays: true,
343+
},
301344
},
302345
{
303346
$match: {
@@ -317,7 +360,13 @@ class EventsFactory extends Factory {
317360
let nextCursor;
318361

319362
if (result.length === limit + 1) {
320-
nextCursor = result.pop()._id;
363+
const nextCursorEvent = result.pop();
364+
365+
nextCursor = {
366+
groupingTimestampBound: nextCursorEvent.groupingTimestamp,
367+
sortValueBound: nextCursorEvent[sort],
368+
idBound: nextCursorEvent._id.toString(),
369+
};
321370
}
322371

323372
const composedResult = result.map(dailyEvent => {
@@ -715,10 +764,19 @@ class EventsFactory extends Factory {
715764
* Compose event with repetition
716765
*
717766
* @param {Event} event - event
718-
* @param {Repetition} repetition - repetition
767+
* @param {Repetition|null} repetition - repetition null
719768
* @returns {Event} event merged with repetition
720769
*/
721770
_composeEventWithRepetition(event, repetition) {
771+
if (!repetition) {
772+
return {
773+
...event,
774+
originalTimestamp: event.timestamp,
775+
originalEventId: event._id,
776+
projectId: this.projectId,
777+
};
778+
}
779+
722780
return {
723781
...event,
724782
_id: repetition._id,

src/resolvers/project.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ module.exports = {
338338
*
339339
* @param {ProjectDBScheme} project - result of parent resolver
340340
* @param {Number} limit - limit for events count
341-
* @param {String} cursor - pointer to the next portion of dailyEvents
341+
* @param {DailyEventsCursor} cursor - object with boundary values of the first event in the next portion
342342
* @param {'BY_DATE' | 'BY_COUNT'} sort - events sort order
343343
* @param {EventsFilters} filters - marks by which events should be filtered
344344
* @param {String} search - search query

src/typeDefs/project.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type DailyEventsPortion {
1818
"""
1919
Pointer to the next portion of dailyEvents, null if there are no events left
2020
"""
21-
nextCursor: String
21+
nextCursor: DailyEventsCursor
2222
2323
"""
2424
List of daily events
@@ -52,6 +52,32 @@ type DailyEvent {
5252
event: Event!
5353
}
5454
55+
"""
56+
Cursor for fetching daily events portion
57+
"""
58+
type DailyEventsCursor {
59+
"""
60+
Grouping timestamp of the first event in the next portion
61+
"""
62+
groupingTimestampBound: Int!
63+
64+
"""
65+
Sort key value of the first event in the next portion
66+
"""
67+
sortValueBound: Int!
68+
69+
"""
70+
ID of the first event of in the next portion
71+
"""
72+
idBound: ID!
73+
}
74+
75+
input DailyEventsCursorInput {
76+
groupingTimestampBound: Int!
77+
sortValueBound: Int!
78+
idBound: ID!
79+
}
80+
5581
"""
5682
Events filters input type
5783
"""
@@ -181,7 +207,7 @@ type Project {
181207
"""
182208
Pointer to the first event of the portion that would be fetched
183209
"""
184-
nextCursor: String
210+
nextCursor: DailyEventsCursorInput
185211
186212
"""
187213
Events sort order

0 commit comments

Comments
 (0)