@openhps/core
Version:
Open Hybrid Positioning System - Core component
137 lines • 4.45 kB
JavaScript
/**
* 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;
}
}