(payload: Partial<Item>[], aggregate: Aggregate = {})
| 283 | } |
| 284 | |
| 285 | async processAggregates(payload: Partial<Item>[], aggregate: Aggregate = {}) { |
| 286 | /** |
| 287 | * Build access path with -> delimiter |
| 288 | * |
| 289 | * input: { min: [ 'date', 'datetime', 'timestamp' ] } |
| 290 | * output: [ 'min->date', 'min->datetime', 'min->timestamp' ] |
| 291 | */ |
| 292 | const aggregateKeys = Object.entries(aggregate).reduce<string[]>((acc, [key, values]) => { |
| 293 | acc.push(...values.map((value) => `${key}->${value}`)); |
| 294 | return acc; |
| 295 | }, []); |
| 296 | |
| 297 | const fieldEntries = this.schema.collections[this.collection]!.fields; |
| 298 | |
| 299 | /** |
| 300 | * Expand -> delimited keys in the payload to the equivalent expanded object |
| 301 | * |
| 302 | * before: { "min->date": "2025-04-09", "min->datetime": "2025-04-08T12:00:00", "min->timestamp": "2025-04-17T23:18:00.000Z" } |
| 303 | * after: { "min": { "date": "2025-04-09", "datetime": "2025-04-08T12:00:00", "timestamp": "2025-04-17T23:18:00.000Z" } } |
| 304 | */ |
| 305 | if (aggregateKeys.length) { |
| 306 | for (const item of payload) { |
| 307 | for (const key of aggregateKeys) { |
| 308 | // Ignore keys that do not follow the `A->B` naming like count=* |
| 309 | if (key in item === false) continue; |
| 310 | |
| 311 | const [operation, fieldName] = key.split('->') as [string, string]; |
| 312 | |
| 313 | const aggregateResult = { [fieldName]: item[key] }; |
| 314 | |
| 315 | if (fieldEntries[fieldName]?.special?.length > 0) { |
| 316 | const newValue = await this.processField( |
| 317 | fieldEntries[fieldName], |
| 318 | aggregateResult, |
| 319 | 'read', |
| 320 | this.accountability, |
| 321 | ); |
| 322 | |
| 323 | if (newValue !== undefined) aggregateResult[fieldName] = newValue; |
| 324 | } |
| 325 | |
| 326 | if (!isPlainObject(item[operation])) item[operation] = {}; |
| 327 | |
| 328 | item[operation][fieldName] = aggregateResult[fieldName]; |
| 329 | delete item[key]; |
| 330 | } |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | async processField( |
| 336 | field: SchemaOverview['collections'][string]['fields'][string], |
no test coverage detected