node-cosmos
Version:
A light weight azure cosmosdb client aiming at ease of use for creating REST API. Supports json filter, sort and offset/limit
96 lines (76 loc) • 3.04 kB
text/typescript
import { FilterResult, Json, _formatKey } from "./Condition";
import { SubQueryExpression } from "./SubQueryExpression";
/**
* class and interfaces represents WHERE expressions. e.g. count > 10, lastName != "Banks", CONTAINS(lastName, "an").
*/
import randomstring from "randomstring";
export interface Expression {
toFilterResult: () => FilterResult;
}
export const EXPRESSION_PATTERN = /(.+)\s(STARTSWITH|ENDSWITH|CONTAINS|ARRAY_CONTAINS|LIKE|=|!=|<|<=|>|>=)\s*$/;
export const SUB_QUERY_EXPRESSION_PATTERN = /(.+)\s(ARRAY_CONTAINS_ANY|ARRAY_CONTAINS_ALL)\s*(.*)$/;
const BINARY_OPERATOR_PATTERN = /^\s*(LIKE|IN|=|!=|<|<=|>|>=)\s*$/;
export const parse = (key: string, value: Json): Expression => {
let match = EXPRESSION_PATTERN.exec(key);
// if filter contains expression
if (match) {
// "count >": 10, get "count" part
const newKey = match[1] || key;
const exp = new SimpleExpression(newKey, value);
// get ">" part
exp.operator = match[2] || "=";
exp.type = BINARY_OPERATOR_PATTERN.test(exp.operator)
? "BINARY_OPERATOR"
: "BINARY_FUNCTION";
return exp;
}
// if this is a subquery
match = SUB_QUERY_EXPRESSION_PATTERN.exec(key);
if (match) {
const joinKey: string = match[1];
const filterKey: string = match[3];
const operator: string = match[2];
return new SubQueryExpression(joinKey, filterKey, value, operator);
}
// finally the default key / value expression
const exp = new SimpleExpression(key, value);
// special case for {"lastName%" : "Banks"}
// we recommend {"lastName LIKE" : "Banks%"}, but leave this for backwards compatibility.
if (key.match(/.+%$/)) {
exp.key = key.replace("%", "");
exp.operator = "STARTSWITH";
exp.type = "BINARY_FUNCTION";
}
return exp;
};
export class SimpleExpression implements Expression {
key: string;
value: Json;
type: "BINARY_OPERATOR" | "BINARY_FUNCTION" = "BINARY_OPERATOR";
operator = "=";
constructor(key: string, value: Json) {
this.key = key;
this.value = value;
}
generateSuffix(): string {
return randomstring.generate(7);
}
public toFilterResult(): FilterResult {
const result: FilterResult = { queries: [], params: [] };
const paramName = `@${this.key.replace(/[.%]/g, "__")}_${this.generateSuffix()}`;
const k = _formatKey(this.key);
const v = this.value;
if (Array.isArray(v)) {
// ex. filter: '{"id":["ID001", "ID002"]}'
result.queries.push(`ARRAY_CONTAINS(${paramName}, ${k})`);
} else {
if (this.type === "BINARY_OPERATOR") {
result.queries.push(`${k} ${this.operator} ${paramName}`);
} else {
result.queries.push(`${this.operator}(${k}, ${paramName})`);
}
}
result.params.push({ name: paramName, value: v });
return result;
}
}