Created
January 19, 2020 21:32
Star
You must be signed in to star a gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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