UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

137 lines 4.45 kB
/** * Query evaluator for {@link FilterQuery}s with {@link MemoryDataService}. */ export class MemoryQueryEvaluator { static isRegexQuery(query) { return Object.prototype.toString.call(query) === '[object RegExp]'; } static evaluateComponent(object, key, query) { let result = true; const value = object[key]; if (key.startsWith('$')) { result = result && MemoryQueryEvaluator.evaluateOp(key, object, query); } else if (key.includes('.')) { result = result && MemoryQueryEvaluator.evaluatePath(object, key, query); } else if (MemoryQueryEvaluator.isRegexQuery(query)) { result = result && value.match(query) ? true : false; } else if (typeof query === 'object') { result = result && MemoryQueryEvaluator.evaluateSelector(value, query); } else { result = result && value === query; } return result; } static evaluate(object, query) { let result = true; if (query) { for (const key of Object.keys(query)) { result = result && MemoryQueryEvaluator.evaluateComponent(object, key, query[key]); } } return result; } static getValueFromPath(object, path) { // https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-and-arays-by-string-path let o = object; path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties path = path.replace(/^\./, ''); // strip a leading dot const a = path.split('.'); for (let i = 0, n = a.length; i < n; ++i) { const k = a[i]; if (!o) { return undefined; } else if (k in o) { if (i < n - 1) { o = o[k]; } else { return [o, o[k], k]; } } else { return undefined; } } } static evaluatePath(object, path, query) { const data = MemoryQueryEvaluator.getValueFromPath(object, path); if (!data) { return false; } return MemoryQueryEvaluator.evaluateComponent(data[0], data[2], query); } static evaluateSelector(value, subquery) { let result = true; for (const selector of Object.keys(subquery)) { result = result && MemoryQueryEvaluator.evaluateComparisonSelector(selector, value, subquery); result = result && MemoryQueryEvaluator.evaluateArraySelector(selector, value, subquery); } return result; } static evaluateComparisonSelector(selector, value, subquery) { let result = true; switch (selector) { case '$gt': result = result && value > subquery[selector]; break; case '$gte': result = result && value >= subquery[selector]; break; case '$lt': result = result && value < subquery[selector]; break; case '$lte': result = result && value <= subquery[selector]; break; case '$eq': result = result && value === subquery[selector]; break; } return result; } static evaluateArraySelector(selector, value, subquery) { let result = true; switch (selector) { case '$in': result = result && Array.from(value).includes(subquery[selector]); break; case '$nin': result = result && !Array.from(value).includes(subquery[selector]); break; case '$elemMatch': result = false; if (value instanceof Array) { Array.from(value).forEach(element => { if (element['key'] && element['value']) { result = result || MemoryQueryEvaluator.evaluate(element['value'], subquery[selector]); } else { result = result || MemoryQueryEvaluator.evaluate(element, subquery[selector]); } }); } else if (value instanceof Map) { value.forEach(element => { result = result || MemoryQueryEvaluator.evaluate(element, subquery[selector]); }); } result = result && result; break; } return result; } static evaluateOp(key, object, subquery) { let result; switch (key) { case '$and': result = true; for (const query of subquery) { result = result && MemoryQueryEvaluator.evaluate(object, query); } break; case '$or': result = false; for (const query of subquery) { result = result || MemoryQueryEvaluator.evaluate(object, query); } break; } return result; } }