UNPKG

datum-focus

Version:

Data shape, model, metadata, JSON, JSON Schema, GraphQL, MongoDB query and aggregations, iterator generators

210 lines (185 loc) 4.97 kB
import { isArray } from '../../array'; import { isNumber } from '../../number'; import { isString } from '../../string'; import { containsOperation, createQueryOperation, createTester, EqualsOperation, NamedBaseOperation, Options, Query, QueryOperation, Tester } from "./core"; import { comparable, isFunction, Key } from "./utils"; // https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ class $ElemMatch extends NamedBaseOperation<Query<any>> { private _queryOperation!: QueryOperation<any>; init() { this._queryOperation = createQueryOperation( this.params, this.owneryQuery, this.options ); } reset() { super.reset(); this._queryOperation.reset(); } next(item: any) { if (isArray(item)) { for (let i = 0, { length } = item; i < length; i++) { // reset query operation since item being tested needs to pass _all_ query // operations for it to be a success this._queryOperation.reset(); // check item this._queryOperation.next(item[i], i, item); this.keep = this.keep || this._queryOperation.keep; } this.done = true; } else { this.done = false; this.keep = false; } } } export class $Size extends NamedBaseOperation<any> { init() { } next(item: any) { if (isArray(item) && item.length === this.params) { this.done = true; this.keep = true; } // if (parent && parent.length === this.params) { // this.done = true; // this.keep = true; // } } } class $In extends NamedBaseOperation<any> { private _testers!: Tester[]; init() { this._testers = this.params.map((value: any) => { if (containsOperation(value)) { throw new Error( `cannot nest $ under ${this.constructor.name.toLowerCase()}` ); } return createTester(value, this.options.compare); }); } next(item: any, key: Key, owner: any) { let done = false; let success = false; for (let i = 0, { length } = this._testers; i < length; i++) { const test = this._testers[i]; if (test(item)) { done = true; success = true; break; } } this.keep = success; this.done = done; } } class $Nin extends $In { next(item: any, key: Key, owner: any) { super.next(item, key, owner); this.keep = !this.keep; } } class $Exists extends NamedBaseOperation<boolean> { next(item: any, key: Key, owner: any) { if (owner.hasOwnProperty(key) === this.params) { this.done = true; this.keep = true; } } } export const $elemMatch = ( params: any, owneryQuery: Query<any>, options: Options, name: string ) => new $ElemMatch(params, owneryQuery, options, name); export const $nin = ( params: any, owneryQuery: Query<any>, options: Options, name: string ) => new $Nin(params, owneryQuery, options, name); export const $in = ( params: any, owneryQuery: Query<any>, options: Options, name: string ) => new $In(params, owneryQuery, options, name); export const $mod = ( [mod, equalsValue]: number[], owneryQuery: Query<any>, options: Options ) => new EqualsOperation( (b: any) => <number>comparable(b) % mod === equalsValue, owneryQuery, options ); export const $exists = ( params: boolean, owneryQuery: Query<any>, options: Options, name: string ) => new $Exists(params, owneryQuery, options, name); export const $regex = ( pattern: string, owneryQuery: Query<any>, options: Options ) => new EqualsOperation( new RegExp(pattern, owneryQuery.$options), owneryQuery, options ); const typeAliases: Record<string, (v: any) => boolean> = { number: isNumber, string: isString, bool: (v: any) => typeof v === "boolean", array: isArray, null: (v: any) => v === null, timestamp: (v: any) => v instanceof Date }; export const $type = ( clazz: Function | string, owneryQuery: Query<any>, options: Options ) => new EqualsOperation( (b: any) => { if (isString(clazz)) { if (!typeAliases[clazz]) { throw new Error(`Type alias does not exist`); } return typeAliases[clazz](b); } return b != null ? b instanceof clazz || b.constructor === clazz : false; }, owneryQuery, options ); export const $size = ( params: number, ownerQuery: Query<any>, options: Options ) => new $Size(params, ownerQuery, options, "$size"); export const $options = () => null; export const $where = ( params: string | Function, ownerQuery: Query<any>, options: Options ) => { let test: any; if (isFunction(params)) { test = params; } else if (!process.env.CSP_ENABLED) { test = new Function("obj", "return " + params); } else { throw new Error( `In CSP mode, sift does not support strings in "$where" condition` ); } return new EqualsOperation((b: any) => test.bind(b)(b), ownerQuery, options); };