mirror of https://github.com/OpenVidu/openvidu.git
70 lines
2.9 KiB
TypeScript
70 lines
2.9 KiB
TypeScript
import type { Document } from 'mongodb';
|
|
|
|
import { EgressSort } from '@openvidu-dashboard/typings';
|
|
|
|
import { prefixRegex } from '../api/search.js';
|
|
import { EGRESS_ID, type EgressesListParams } from './egress-schemas.js';
|
|
|
|
/** Index of a plan; `startedAt_id` and `roomNameLower_startedAt` are read with a sort on `{ startedAt, _id }`. */
|
|
export type EgressesIndex = 'startedAt_id' | 'status_startedAt' | 'roomSid_startedAt' | 'roomNameLower_startedAt';
|
|
|
|
export type EgressesListPlan =
|
|
| { kind: 'id'; filter: Document }
|
|
| {
|
|
kind: 'page';
|
|
/** Scope and residual filters, without the range or the keyset condition. */
|
|
filter: Document;
|
|
hint: EgressesIndex;
|
|
}
|
|
| {
|
|
kind: 'ranked';
|
|
filter: Document;
|
|
hint: EgressesIndex;
|
|
rank: (asOf: Date) => Document;
|
|
};
|
|
|
|
type RankedEgressSort = Exclude<EgressSort, 'startedAt'>;
|
|
|
|
/** Rank of each ranked sort key at `asOf`: an egress in flight ends, and lasts until, `asOf`. */
|
|
const EGRESS_RANKS: Readonly<Record<RankedEgressSort, (asOf: Date) => Document>> = {
|
|
endedAt: (asOf) => ({ $ifNull: ['$endedAt', asOf] }),
|
|
duration: (asOf) => ({ $ifNull: ['$durationMs', { $subtract: [asOf, '$startedAt'] }] })
|
|
};
|
|
|
|
/** Residual filters that never pick the index: `type`, and `status` when another field drives the plan. */
|
|
function residual(params: EgressesListParams, withStatus: boolean): Document {
|
|
return {
|
|
...(withStatus && params.status !== 'all' ? { status: { $in: params.status } } : {}),
|
|
...(params.type === undefined || params.type.length === 0 ? {} : { type: { $in: params.type } })
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Plan of an egresses list request after the filter-to-index table of the design: an egress id is a point lookup; a
|
|
* room drives `{ roomSid, startedAt }` (a room name prefix residual); a room name prefix drives `{ roomNameLower,
|
|
* startedAt }`; a status set drives `{ status, startedAt }` (an `$in` explode merged in `startedAt` order); otherwise
|
|
* `{ startedAt, _id }`. The range is always on `startedAt`; a ranked sort key scans it whole and ranks in memory.
|
|
*/
|
|
export function egressesListPlan(params: EgressesListParams): EgressesListPlan {
|
|
if (params.q !== undefined && EGRESS_ID.test(params.q)) {
|
|
return { kind: 'id', filter: { _id: params.q } };
|
|
}
|
|
|
|
const prefix = params.q === undefined ? {} : { roomNameLower: prefixRegex(params.q) };
|
|
let scope: { filter: Document; hint: EgressesIndex };
|
|
|
|
if (params.room !== undefined) {
|
|
scope = { filter: { roomSid: params.room, ...prefix, ...residual(params, true) }, hint: 'roomSid_startedAt' };
|
|
} else if (params.q !== undefined) {
|
|
scope = { filter: { ...prefix, ...residual(params, true) }, hint: 'roomNameLower_startedAt' };
|
|
} else if (params.status !== 'all') {
|
|
scope = { filter: residual(params, true), hint: 'status_startedAt' };
|
|
} else {
|
|
scope = { filter: residual(params, false), hint: 'startedAt_id' };
|
|
}
|
|
|
|
return params.sort === EgressSort.STARTED_AT
|
|
? { kind: 'page', ...scope }
|
|
: { kind: 'ranked', ...scope, rank: EGRESS_RANKS[params.sort] };
|
|
}
|