Skip to content

Instantly share code, notes, and snippets.

@brendanmckenzie
Created January 19, 2020 21:32
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save brendanmckenzie/473e2ce12cd26ba499aba37dfb9b24c1 to your computer and use it in GitHub Desktop.
const config: Config = {
fields: {
amount: {
type: "number",
aliases: ["amt", "<implicit>"],
queryField: (exp: any) => ({
ledgerTransactionsByPostingId: {
some: {
amountValue: exp
}
}
}),
parseValue: (value: any) => {
const num = parseFloat(value);
if (isNumber(num)) {
return num;
}
return null;
}
},
account: {
type: "string",
fuzzy: true,
aliases: ["acct", "<implicit>"],
queryField: (exp: any) => ({
ledgerTransactionsByPostingId: {
some: {
account: {
name: exp
}
}
}
}),
parseValue: (value: any) => value
},
date: {
type: "date",
aliases: ["dt"],
queryField: (exp: any) => ({
date: exp
}),
parseValue: (value: any) => {
const num = moment(value);
if (num.isValid()) {
return num.format("YYYY-MM-DD");
}
return null;
}
},
notes: {
type: "string",
aliases: ["<implicit>"],
fuzzy: true,
queryField: (exp: any) => ({
notes: exp
}),
parseValue: (value: any) => value
}
},
operators: {
AND: "and",
OR: "or",
NOT: "not",
default: "and"
}
};
import parser from "lucene-query-parser";
export type Config = {
fields: { [key: string]: any };
operators: { [key: string]: string };
};
const findField = (config: Config, name: string): any[] => {
if (config.fields[name]) {
return [config.fields[name]];
}
return Object.keys(config.fields)
.filter(k => config.fields[k].aliases.indexOf(name) !== -1)
.map(k => config.fields[k]);
};
const handleExp = (config: Config, exp: any): any => {
if (exp.operator) {
return handleNodeExp(config, exp);
} else if (exp.term_min) {
return handleRangeExp(config, exp);
} else if (exp.term) {
return handleFieldExp(config, exp);
}
return null;
};
const handleNodeExp = (config: Config, exp: any): any => {
const { left, operator, right } = exp;
if (left && right) {
const leftExp = handleExp(config, left);
const rightExp = handleExp(config, right);
if (leftExp && rightExp) {
return {
[config.operators[operator]
? config.operators[operator]
: config.operators.default]: [
handleExp(config, left),
handleExp(config, right)
]
};
} else if (leftExp) {
return leftExp;
} else if (rightExp) {
return rightExp;
}
} else if (left) {
return handleExp(config, left);
} else if (right) {
return handleExp(config, right);
}
return null;
};
const handleFieldExp = (config: Config, exp: any): any => {
const ret = findField(config, exp.field)
.map(fieldDef => {
const value = fieldDef.parseValue(exp.term);
if (value) {
if (fieldDef.fuzzy) {
return fieldDef.queryField({ likeInsensitive: `%${value}%` });
} else {
return fieldDef.queryField({ equalTo: value });
}
}
return null;
})
.filter(a => !!a);
switch (ret.length) {
case 0:
return null;
case 1:
return ret[0];
default:
return {
or: ret
};
}
};
const handleRangeExp = (config: Config, exp: any): any => {
const ret = findField(config, exp.field)
.map(fieldDef => {
const value1 = fieldDef.parseValue(exp.term_min);
const value2 = fieldDef.parseValue(exp.term_max);
if (value1 && value2) {
return fieldDef.queryField({
[exp.inclusive_min ? "greaterThanOrEqualTo" : "greaterThan"]: value1,
[exp.inclusive_max ? "lessThanOrEqualTo" : "lessThan"]: value2
});
}
return null;
})
.filter(a => !!a);
switch (ret.length) {
case 0:
return null;
case 1:
return ret[0];
default:
return {
or: ret
};
}
};
export const parseQuery = (config: Config, input: string) => {
const q = parser.parse(input);
return handleNodeExp(config, q);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment