@tmlmobilidade/interfaces 20251027.1158.39 → 20251028.1440.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AggregationPipeline } from '../../aggregation-pipeline.js';
|
|
2
|
+
import { Ride, RideAcceptanceStatus, RideAnalysisGradeWithNone, RideDelayStatus, RideOperationalStatus, RideSeenStatus, UnixTimestamp } from '@tmlmobilidade/types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize delay statuses.
|
|
5
|
+
*
|
|
6
|
+
* This function generates three aggregation stages:
|
|
7
|
+
* 1. Calculates delay differences (in milliseconds) between scheduled and observed times
|
|
8
|
+
* 2. Categorizes delays into statuses: 'delayed', 'early', 'ontime', or 'none'
|
|
9
|
+
* 3. Removes intermediate calculation fields
|
|
10
|
+
*
|
|
11
|
+
* Delay thresholds:
|
|
12
|
+
* - Delayed: > 5 minutes (300000 ms)
|
|
13
|
+
* - Early: < -1 minute (-60000 ms)
|
|
14
|
+
* - On-time: between -1 minute and 5 minutes
|
|
15
|
+
* - None: missing scheduled or observed time data
|
|
16
|
+
*
|
|
17
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
18
|
+
*/
|
|
19
|
+
export declare function ridesPipelineDelayStatus({ filter }?: {
|
|
20
|
+
filter?: {
|
|
21
|
+
end_delay_status?: RideDelayStatus[];
|
|
22
|
+
start_delay_status?: RideDelayStatus[];
|
|
23
|
+
};
|
|
24
|
+
}): AggregationPipeline<Ride>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize operational statuses.
|
|
27
|
+
*
|
|
28
|
+
* This function generates four aggregation stages:
|
|
29
|
+
* 1. Adds the current timestamp (now) to each document
|
|
30
|
+
* 2. Calculates time differences from last seen and from start time
|
|
31
|
+
* 3. Categorizes operational status: 'scheduled', 'missed', 'running', or 'ended'
|
|
32
|
+
* 4. Removes intermediate calculation fields
|
|
33
|
+
*
|
|
34
|
+
* Operational status logic:
|
|
35
|
+
* - Scheduled: within 10 minutes of start time and never seen
|
|
36
|
+
* - Missed: more than 10 minutes after start time and never seen
|
|
37
|
+
* - Running: last seen within 10 minutes
|
|
38
|
+
* - Ended: default fallback (last seen more than 10 minutes ago)
|
|
39
|
+
*
|
|
40
|
+
* Time thresholds:
|
|
41
|
+
* - Operational window: 10 minutes (600000 ms)
|
|
42
|
+
*
|
|
43
|
+
* @param {number} now - Current timestamp in milliseconds
|
|
44
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
45
|
+
*/
|
|
46
|
+
export declare function ridesPipelineOperationalStatus({ filter }?: {
|
|
47
|
+
filter?: {
|
|
48
|
+
operational_status?: RideOperationalStatus[];
|
|
49
|
+
};
|
|
50
|
+
}): AggregationPipeline<Ride>;
|
|
51
|
+
/**
|
|
52
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize seen statuses.
|
|
53
|
+
*
|
|
54
|
+
* This function generates three aggregation stages:
|
|
55
|
+
* 1. Adds the current timestamp (now) to each document
|
|
56
|
+
* 2. Calculates time difference from last seen to now
|
|
57
|
+
* 3. Categorizes seen status: 'gone', 'seen', or 'unseen'
|
|
58
|
+
* 4. Removes intermediate calculation fields
|
|
59
|
+
*
|
|
60
|
+
* Seen status logic:
|
|
61
|
+
* - Gone: last seen more than 30 seconds ago
|
|
62
|
+
* - Seen: last seen within 30 seconds
|
|
63
|
+
* - Unseen: no last seen time
|
|
64
|
+
*
|
|
65
|
+
* Time thresholds:
|
|
66
|
+
* - Seen window: 30 seconds (30000 ms)
|
|
67
|
+
*
|
|
68
|
+
* @param {number} now - Current timestamp in milliseconds
|
|
69
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
70
|
+
*/
|
|
71
|
+
export declare function ridesPipelineSeenStatus({ filter }?: {
|
|
72
|
+
filter?: {
|
|
73
|
+
seen_status?: RideSeenStatus[];
|
|
74
|
+
};
|
|
75
|
+
}): AggregationPipeline<Ride>;
|
|
76
|
+
interface RidesPipelineFilter {
|
|
77
|
+
acceptance_status?: ('none' | RideAcceptanceStatus)[];
|
|
78
|
+
agency_ids?: string[];
|
|
79
|
+
analysis_ended_at_last_stop_grade?: RideAnalysisGradeWithNone[];
|
|
80
|
+
analysis_expected_apex_validation_interval?: RideAnalysisGradeWithNone[];
|
|
81
|
+
analysis_simple_three_vehicle_events_grade?: RideAnalysisGradeWithNone[];
|
|
82
|
+
analysis_transaction_sequentiality?: RideAnalysisGradeWithNone[];
|
|
83
|
+
date_end: UnixTimestamp;
|
|
84
|
+
date_start: UnixTimestamp;
|
|
85
|
+
delay_statuses?: RideDelayStatus[];
|
|
86
|
+
line_ids?: string[];
|
|
87
|
+
operational_statuses?: RideOperationalStatus[];
|
|
88
|
+
search?: string;
|
|
89
|
+
seen_statuses?: RideSeenStatus[];
|
|
90
|
+
stop_ids?: string[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates MongoDB aggregation pipeline stages to filter and process ride data.
|
|
94
|
+
*
|
|
95
|
+
* This function generates an aggregation pipeline that:
|
|
96
|
+
* 1. Filters rides by scheduled time range (date_start to date_end)
|
|
97
|
+
* 2. Optionally filters by line IDs and agency IDs
|
|
98
|
+
* 3. Optionally searches rides by ID using regex pattern matching
|
|
99
|
+
* 4. Adds acceptance status from ride_acceptances collection via lookup
|
|
100
|
+
* 5. Filters by analysis grades (ended_at_last_stop, expected_apex_validation_interval, simple_three_vehicle_events)
|
|
101
|
+
* 6. Filters by acceptance status (excluding 'none' if present)
|
|
102
|
+
* 7. Applies delay, operational, and seen status filters using dedicated pipeline functions
|
|
103
|
+
*
|
|
104
|
+
* @param {Object} params - Parameters object
|
|
105
|
+
* @param {RidesPipelineFilter} params.filter - Filter criteria for rides
|
|
106
|
+
* @returns {AggregationPipeline<Ride>} Array of MongoDB aggregation pipeline stages
|
|
107
|
+
*/
|
|
108
|
+
export declare function ridesBatchAggregationPipeline({ ...filter }: RidesPipelineFilter): AggregationPipeline<Ride>;
|
|
109
|
+
export {};
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { Dates } from '@tmlmobilidade/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize delay statuses.
|
|
4
|
+
*
|
|
5
|
+
* This function generates three aggregation stages:
|
|
6
|
+
* 1. Calculates delay differences (in milliseconds) between scheduled and observed times
|
|
7
|
+
* 2. Categorizes delays into statuses: 'delayed', 'early', 'ontime', or 'none'
|
|
8
|
+
* 3. Removes intermediate calculation fields
|
|
9
|
+
*
|
|
10
|
+
* Delay thresholds:
|
|
11
|
+
* - Delayed: > 5 minutes (300000 ms)
|
|
12
|
+
* - Early: < -1 minute (-60000 ms)
|
|
13
|
+
* - On-time: between -1 minute and 5 minutes
|
|
14
|
+
* - None: missing scheduled or observed time data
|
|
15
|
+
*
|
|
16
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
17
|
+
*/
|
|
18
|
+
export function ridesPipelineDelayStatus({ filter } = {}) {
|
|
19
|
+
//
|
|
20
|
+
// Delay thresholds in milliseconds
|
|
21
|
+
const DELAY_THRESHOLDS = {
|
|
22
|
+
delayed: 300000, // 5 minutes after scheduled time
|
|
23
|
+
early: -60000, // 1 minute before scheduled time
|
|
24
|
+
};
|
|
25
|
+
const pipeline = [
|
|
26
|
+
// Stage 1: Calculate delay differences in milliseconds
|
|
27
|
+
// Only compute if both scheduled and observed times exist
|
|
28
|
+
{
|
|
29
|
+
$addFields: {
|
|
30
|
+
end_delay_diff: {
|
|
31
|
+
$cond: {
|
|
32
|
+
else: null,
|
|
33
|
+
if: { $and: [
|
|
34
|
+
{ $ifNull: ['$end_time_scheduled', false] },
|
|
35
|
+
{ $ifNull: ['$end_time_observed', false] },
|
|
36
|
+
] },
|
|
37
|
+
then: { $subtract: ['$end_time_observed', '$end_time_scheduled'] },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
start_delay_diff: {
|
|
41
|
+
$cond: {
|
|
42
|
+
else: null,
|
|
43
|
+
if: { $and: [
|
|
44
|
+
{ $ifNull: ['$start_time_scheduled', false] },
|
|
45
|
+
{ $ifNull: ['$start_time_observed', false] },
|
|
46
|
+
] },
|
|
47
|
+
then: { $subtract: ['$start_time_observed', '$start_time_scheduled'] },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
// Stage 2: Categorize delays into status strings
|
|
53
|
+
// Uses switch statement to classify based on delay thresholds
|
|
54
|
+
{
|
|
55
|
+
$addFields: {
|
|
56
|
+
end_delay_status: {
|
|
57
|
+
$switch: {
|
|
58
|
+
branches: [
|
|
59
|
+
// Delayed: > 5 minutes (300000 ms)
|
|
60
|
+
{ case: { $gt: ['$end_delay_diff', DELAY_THRESHOLDS.delayed] }, then: 'delayed' },
|
|
61
|
+
// Early: < -1 minute (-60000 ms)
|
|
62
|
+
{ case: { $lt: ['$end_delay_diff', DELAY_THRESHOLDS.early] }, then: 'early' },
|
|
63
|
+
// On-time: between -1 minute and 5 minutes
|
|
64
|
+
{ case: { $and: [
|
|
65
|
+
{ $gte: ['$end_delay_diff', DELAY_THRESHOLDS.early] },
|
|
66
|
+
{ $lte: ['$end_delay_diff', DELAY_THRESHOLDS.delayed] },
|
|
67
|
+
] }, then: 'ontime' },
|
|
68
|
+
],
|
|
69
|
+
default: 'none',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
start_delay_status: {
|
|
73
|
+
$switch: {
|
|
74
|
+
branches: [
|
|
75
|
+
// Delayed: > 5 minutes (300000 ms)
|
|
76
|
+
{ case: { $gt: ['$start_delay_diff', DELAY_THRESHOLDS.delayed] }, then: 'delayed' },
|
|
77
|
+
// Early: < -1 minute (-60000 ms)
|
|
78
|
+
{ case: { $lt: ['$start_delay_diff', DELAY_THRESHOLDS.early] }, then: 'early' },
|
|
79
|
+
// On-time: between -1 minute and 5 minutes
|
|
80
|
+
{ case: { $and: [
|
|
81
|
+
{ $gte: ['$start_delay_diff', DELAY_THRESHOLDS.early] },
|
|
82
|
+
{ $lte: ['$start_delay_diff', DELAY_THRESHOLDS.delayed] },
|
|
83
|
+
] }, then: 'ontime' },
|
|
84
|
+
],
|
|
85
|
+
default: 'none',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
// Stage 3: Remove intermediate calculation fields
|
|
91
|
+
// These fields were only used for status calculation and are not needed in final output
|
|
92
|
+
{ $project: { end_delay_diff: 0, start_delay_diff: 0 } },
|
|
93
|
+
];
|
|
94
|
+
// Stage 5: Filter by delay status if provided
|
|
95
|
+
if (filter && filter.end_delay_status && filter.end_delay_status.length > 0) {
|
|
96
|
+
pipeline.push({ $match: { end_delay_status: { $in: filter.end_delay_status } } });
|
|
97
|
+
}
|
|
98
|
+
if (filter && filter.start_delay_status && filter.start_delay_status.length > 0) {
|
|
99
|
+
pipeline.push({ $match: { start_delay_status: { $in: filter.start_delay_status } } });
|
|
100
|
+
}
|
|
101
|
+
return pipeline;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize operational statuses.
|
|
105
|
+
*
|
|
106
|
+
* This function generates four aggregation stages:
|
|
107
|
+
* 1. Adds the current timestamp (now) to each document
|
|
108
|
+
* 2. Calculates time differences from last seen and from start time
|
|
109
|
+
* 3. Categorizes operational status: 'scheduled', 'missed', 'running', or 'ended'
|
|
110
|
+
* 4. Removes intermediate calculation fields
|
|
111
|
+
*
|
|
112
|
+
* Operational status logic:
|
|
113
|
+
* - Scheduled: within 10 minutes of start time and never seen
|
|
114
|
+
* - Missed: more than 10 minutes after start time and never seen
|
|
115
|
+
* - Running: last seen within 10 minutes
|
|
116
|
+
* - Ended: default fallback (last seen more than 10 minutes ago)
|
|
117
|
+
*
|
|
118
|
+
* Time thresholds:
|
|
119
|
+
* - Operational window: 10 minutes (600000 ms)
|
|
120
|
+
*
|
|
121
|
+
* @param {number} now - Current timestamp in milliseconds
|
|
122
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
123
|
+
*/
|
|
124
|
+
export function ridesPipelineOperationalStatus({ filter } = {}) {
|
|
125
|
+
//
|
|
126
|
+
// Time thresholds in milliseconds
|
|
127
|
+
const OPERATIONAL_WINDOW = 600000; // 10 minutes
|
|
128
|
+
const now = Dates.now('Europe/Lisbon').unix_timestamp;
|
|
129
|
+
const pipeline = [
|
|
130
|
+
// Stage 1: Add current timestamp to each document
|
|
131
|
+
{ $addFields: { now } },
|
|
132
|
+
// Stage 2: Calculate time differences from last seen and from start time
|
|
133
|
+
// Only compute if the relevant fields exist
|
|
134
|
+
{
|
|
135
|
+
$addFields: {
|
|
136
|
+
milliseconds_from_last_seen_to_now: {
|
|
137
|
+
$cond: {
|
|
138
|
+
else: null,
|
|
139
|
+
if: { $ifNull: ['$seen_last_at', false] },
|
|
140
|
+
then: { $subtract: ['$now', '$seen_last_at'] },
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
milliseconds_from_start_to_now: { $subtract: ['$now', '$start_time_scheduled'] },
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
// Stage 3: Categorize operational status using switch statement
|
|
147
|
+
{
|
|
148
|
+
$addFields: {
|
|
149
|
+
operational_status: {
|
|
150
|
+
$switch: {
|
|
151
|
+
branches: [
|
|
152
|
+
// Scheduled: within 10 minutes of start time and never seen
|
|
153
|
+
{
|
|
154
|
+
case: {
|
|
155
|
+
$and: [
|
|
156
|
+
{ $lte: ['$milliseconds_from_start_to_now', OPERATIONAL_WINDOW] },
|
|
157
|
+
{ $or: [{ $eq: ['$seen_last_at', null] }, { $not: ['$seen_last_at'] }] },
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
then: 'scheduled',
|
|
161
|
+
},
|
|
162
|
+
// Missed: more than 10 minutes after start time and never seen
|
|
163
|
+
{
|
|
164
|
+
case: {
|
|
165
|
+
$and: [
|
|
166
|
+
{ $gt: ['$milliseconds_from_start_to_now', OPERATIONAL_WINDOW] },
|
|
167
|
+
{ $or: [{ $eq: ['$seen_last_at', null] }, { $not: ['$seen_last_at'] }] },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
then: 'missed',
|
|
171
|
+
},
|
|
172
|
+
// Running: last seen within 10 minutes
|
|
173
|
+
{
|
|
174
|
+
case: { $lte: ['$milliseconds_from_last_seen_to_now', OPERATIONAL_WINDOW] },
|
|
175
|
+
then: 'running',
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
default: 'ended',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
// Stage 4: Remove intermediate calculation fields
|
|
184
|
+
// These fields were only used for status calculation and are not needed in final output
|
|
185
|
+
{
|
|
186
|
+
$project: {
|
|
187
|
+
milliseconds_from_last_seen_to_now: 0,
|
|
188
|
+
milliseconds_from_start_to_now: 0,
|
|
189
|
+
now: 0,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
// Stage 5: Filter by operational status if provided
|
|
194
|
+
if (filter && filter.operational_status && filter.operational_status.length > 0) {
|
|
195
|
+
pipeline.push({ $match: { operational_status: { $in: filter.operational_status } } });
|
|
196
|
+
}
|
|
197
|
+
return pipeline;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Creates MongoDB aggregation pipeline stages to calculate and categorize seen statuses.
|
|
201
|
+
*
|
|
202
|
+
* This function generates three aggregation stages:
|
|
203
|
+
* 1. Adds the current timestamp (now) to each document
|
|
204
|
+
* 2. Calculates time difference from last seen to now
|
|
205
|
+
* 3. Categorizes seen status: 'gone', 'seen', or 'unseen'
|
|
206
|
+
* 4. Removes intermediate calculation fields
|
|
207
|
+
*
|
|
208
|
+
* Seen status logic:
|
|
209
|
+
* - Gone: last seen more than 30 seconds ago
|
|
210
|
+
* - Seen: last seen within 30 seconds
|
|
211
|
+
* - Unseen: no last seen time
|
|
212
|
+
*
|
|
213
|
+
* Time thresholds:
|
|
214
|
+
* - Seen window: 30 seconds (30000 ms)
|
|
215
|
+
*
|
|
216
|
+
* @param {number} now - Current timestamp in milliseconds
|
|
217
|
+
* @returns {Array} Array of MongoDB aggregation pipeline stages
|
|
218
|
+
*/
|
|
219
|
+
export function ridesPipelineSeenStatus({ filter } = {}) {
|
|
220
|
+
//
|
|
221
|
+
// Time thresholds in milliseconds
|
|
222
|
+
const SEEN_WINDOW = 30000; // 30 seconds
|
|
223
|
+
const now = Dates.now('Europe/Lisbon').unix_timestamp;
|
|
224
|
+
const pipeline = [
|
|
225
|
+
// Stage 1: Add current timestamp to each document
|
|
226
|
+
{ $addFields: { now } },
|
|
227
|
+
// Stage 2: Calculate time difference from last seen to now
|
|
228
|
+
// Only compute if the last seen time exists
|
|
229
|
+
{
|
|
230
|
+
$addFields: {
|
|
231
|
+
milliseconds_from_last_seen_to_now: {
|
|
232
|
+
$cond: {
|
|
233
|
+
else: null,
|
|
234
|
+
if: { $ifNull: ['$seen_last_at', false] },
|
|
235
|
+
then: { $subtract: ['$now', '$seen_last_at'] },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
// Stage 3: Categorize seen status using switch statement
|
|
241
|
+
{
|
|
242
|
+
$addFields: {
|
|
243
|
+
seen_status: {
|
|
244
|
+
$switch: {
|
|
245
|
+
branches: [
|
|
246
|
+
{ case: { $eq: ['$seen_last_at', null] }, then: 'unseen' },
|
|
247
|
+
{ case: { $lte: ['$milliseconds_from_last_seen_to_now', SEEN_WINDOW] }, then: 'seen' },
|
|
248
|
+
],
|
|
249
|
+
default: 'gone',
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
// Stage 4: Remove intermediate calculation fields
|
|
255
|
+
// These fields were only used for status calculation and are not needed in final output
|
|
256
|
+
{
|
|
257
|
+
$project: {
|
|
258
|
+
milliseconds_from_last_seen_to_now: 0,
|
|
259
|
+
now: 0,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
// Stage 5: Filter by seen status if provided
|
|
264
|
+
if (filter && filter.seen_status && filter.seen_status.length > 0) {
|
|
265
|
+
pipeline.push({ $match: { seen_status: { $in: filter.seen_status } } });
|
|
266
|
+
}
|
|
267
|
+
return pipeline;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Creates MongoDB aggregation pipeline stages to filter and process ride data.
|
|
271
|
+
*
|
|
272
|
+
* This function generates an aggregation pipeline that:
|
|
273
|
+
* 1. Filters rides by scheduled time range (date_start to date_end)
|
|
274
|
+
* 2. Optionally filters by line IDs and agency IDs
|
|
275
|
+
* 3. Optionally searches rides by ID using regex pattern matching
|
|
276
|
+
* 4. Adds acceptance status from ride_acceptances collection via lookup
|
|
277
|
+
* 5. Filters by analysis grades (ended_at_last_stop, expected_apex_validation_interval, simple_three_vehicle_events)
|
|
278
|
+
* 6. Filters by acceptance status (excluding 'none' if present)
|
|
279
|
+
* 7. Applies delay, operational, and seen status filters using dedicated pipeline functions
|
|
280
|
+
*
|
|
281
|
+
* @param {Object} params - Parameters object
|
|
282
|
+
* @param {RidesPipelineFilter} params.filter - Filter criteria for rides
|
|
283
|
+
* @returns {AggregationPipeline<Ride>} Array of MongoDB aggregation pipeline stages
|
|
284
|
+
*/
|
|
285
|
+
export function ridesBatchAggregationPipeline({ ...filter }) {
|
|
286
|
+
const pipeline = [];
|
|
287
|
+
// Stage 1: Filter by scheduled time range
|
|
288
|
+
pipeline.push({ $match: { start_time_scheduled: { $gte: filter.date_start, $lte: filter.date_end } } });
|
|
289
|
+
// Stage 2: Filter by line IDs if provided
|
|
290
|
+
if (filter.line_ids)
|
|
291
|
+
pipeline.push({ $match: { line_id: { $in: filter.line_ids.map(id => Number(id)) } } });
|
|
292
|
+
// Stage 3: Filter by agency IDs if provided
|
|
293
|
+
if (filter.agency_ids)
|
|
294
|
+
pipeline.push({ $match: { agency_id: { $in: filter.agency_ids } } });
|
|
295
|
+
// Stage 4: Search by ride ID if provided
|
|
296
|
+
// Uses regex pattern matching with case-insensitive option
|
|
297
|
+
if (filter.search) {
|
|
298
|
+
const keywords = filter.search.split(/\s+/).map(v => v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
299
|
+
const pattern = keywords.map(k => `(?=.*${k})`).join('') + '.*';
|
|
300
|
+
pipeline.push({
|
|
301
|
+
$match: { _id: { $options: 'i', $regex: pattern } },
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// Stage 5: Add acceptance status from ride_acceptances collection
|
|
305
|
+
// Lookup joins acceptance data, unwinds to flatten, adds status field, and removes intermediate data
|
|
306
|
+
pipeline.push({ $lookup: { as: 'acceptance', foreignField: 'ride_id', from: 'ride_acceptances', localField: '_id' } }, { $unwind: { path: '$acceptance', preserveNullAndEmptyArrays: true } }, { $addFields: { acceptance_status: { $ifNull: ['$acceptance.acceptance_status', null] } } }, { $project: { acceptance: 0 } });
|
|
307
|
+
// Stage 6: Filter by analysis grades
|
|
308
|
+
// Maps filter fields to their corresponding analysis paths in the document
|
|
309
|
+
const analysisFilters = [
|
|
310
|
+
{ field: 'analysis_ended_at_last_stop_grade', path: 'analysis.ENDED_AT_LAST_STOP.grade' },
|
|
311
|
+
{ field: 'analysis_expected_apex_validation_interval', path: 'analysis.EXPECTED_APEX_VALIDATION_INTERVAL.grade' },
|
|
312
|
+
{ field: 'analysis_simple_three_vehicle_events_grade', path: 'analysis.SIMPLE_THREE_VEHICLE_EVENTS.grade' },
|
|
313
|
+
];
|
|
314
|
+
analysisFilters.forEach(({ field, path }) => filter[field] && pipeline.push({ $match: { [path]: { $in: filter[field] } } }));
|
|
315
|
+
// Stage 7: Filter by acceptance status
|
|
316
|
+
// Only applies filter if acceptance_status is provided and doesn't include 'none'
|
|
317
|
+
if (filter.acceptance_status && filter.acceptance_status.length > 0 && !filter.acceptance_status.includes('none')) {
|
|
318
|
+
pipeline.push({ $match: { acceptance_status: { $exists: true } } }, { $match: { acceptance_status: { $in: filter.acceptance_status } } });
|
|
319
|
+
}
|
|
320
|
+
// Stage 8: Apply status filters using dedicated pipeline functions
|
|
321
|
+
// These functions add calculated status fields and filter by them
|
|
322
|
+
pipeline.push(...ridesPipelineDelayStatus({ filter: { end_delay_status: filter.delay_statuses?.map(status => status), start_delay_status: filter.delay_statuses?.map(status => status) } }));
|
|
323
|
+
pipeline.push(...ridesPipelineOperationalStatus({ filter: { operational_status: filter.operational_statuses?.map(status => status) } }));
|
|
324
|
+
pipeline.push(...ridesPipelineSeenStatus({ filter: { seen_status: filter.seen_statuses?.map(status => status) } }));
|
|
325
|
+
return pipeline;
|
|
326
|
+
}
|
|
@@ -26,13 +26,6 @@ declare class StopsClass extends MongoCollectionClass<Stop, CreateStopDto, Updat
|
|
|
26
26
|
__brand: "UnixTimestamp";
|
|
27
27
|
};
|
|
28
28
|
name: string;
|
|
29
|
-
is_locked: boolean;
|
|
30
|
-
has_bench: "unknown" | "yes" | "no";
|
|
31
|
-
has_network_map: "unknown" | "yes" | "no";
|
|
32
|
-
has_schedules: "unknown" | "yes" | "no";
|
|
33
|
-
has_shelter: "unknown" | "yes" | "no";
|
|
34
|
-
has_stop_sign: "unknown" | "yes" | "no";
|
|
35
|
-
municipality_id: string;
|
|
36
29
|
comments: ({
|
|
37
30
|
created_at: number & {
|
|
38
31
|
__brand: "UnixTimestamp";
|
|
@@ -73,9 +66,16 @@ declare class StopsClass extends MongoCollectionClass<Stop, CreateStopDto, Updat
|
|
|
73
66
|
created_by?: string | undefined;
|
|
74
67
|
updated_by?: string | undefined;
|
|
75
68
|
})[];
|
|
69
|
+
is_locked: boolean;
|
|
70
|
+
operational_status: "active" | "inactive" | "provisional" | "seasonal" | "voided";
|
|
71
|
+
has_bench: "unknown" | "yes" | "no";
|
|
72
|
+
has_network_map: "unknown" | "yes" | "no";
|
|
73
|
+
has_schedules: "unknown" | "yes" | "no";
|
|
74
|
+
has_shelter: "unknown" | "yes" | "no";
|
|
75
|
+
has_stop_sign: "unknown" | "yes" | "no";
|
|
76
|
+
municipality_id: string;
|
|
76
77
|
is_archived: boolean;
|
|
77
78
|
jurisdiction: "unknown" | "ip" | "municipality" | "other";
|
|
78
|
-
operational_status: "active" | "inactive" | "provisional" | "seasonal" | "voided";
|
|
79
79
|
district_id: string;
|
|
80
80
|
latitude: number;
|
|
81
81
|
longitude: number;
|
|
@@ -125,13 +125,6 @@ declare class StopsClass extends MongoCollectionClass<Stop, CreateStopDto, Updat
|
|
|
125
125
|
__brand: "UnixTimestamp";
|
|
126
126
|
};
|
|
127
127
|
name: string;
|
|
128
|
-
is_locked: boolean;
|
|
129
|
-
has_bench: "unknown" | "yes" | "no";
|
|
130
|
-
has_network_map: "unknown" | "yes" | "no";
|
|
131
|
-
has_schedules: "unknown" | "yes" | "no";
|
|
132
|
-
has_shelter: "unknown" | "yes" | "no";
|
|
133
|
-
has_stop_sign: "unknown" | "yes" | "no";
|
|
134
|
-
municipality_id: string;
|
|
135
128
|
comments: ({
|
|
136
129
|
created_at: number & {
|
|
137
130
|
__brand: "UnixTimestamp";
|
|
@@ -172,9 +165,16 @@ declare class StopsClass extends MongoCollectionClass<Stop, CreateStopDto, Updat
|
|
|
172
165
|
created_by?: string | undefined;
|
|
173
166
|
updated_by?: string | undefined;
|
|
174
167
|
})[];
|
|
168
|
+
is_locked: boolean;
|
|
169
|
+
operational_status: "active" | "inactive" | "provisional" | "seasonal" | "voided";
|
|
170
|
+
has_bench: "unknown" | "yes" | "no";
|
|
171
|
+
has_network_map: "unknown" | "yes" | "no";
|
|
172
|
+
has_schedules: "unknown" | "yes" | "no";
|
|
173
|
+
has_shelter: "unknown" | "yes" | "no";
|
|
174
|
+
has_stop_sign: "unknown" | "yes" | "no";
|
|
175
|
+
municipality_id: string;
|
|
175
176
|
is_archived: boolean;
|
|
176
177
|
jurisdiction: "unknown" | "ip" | "municipality" | "other";
|
|
177
|
-
operational_status: "active" | "inactive" | "provisional" | "seasonal" | "voided";
|
|
178
178
|
district_id: string;
|
|
179
179
|
latitude: number;
|
|
180
180
|
longitude: number;
|
package/package.json
CHANGED