elastic-muto
Version:
Easy expressive search queries for Elasticsearch
109 lines (101 loc) • 3.15 kB
JavaScript
;
var debug = require('debug')('elastic-muto');
var isNil = require('lodash.isnil');
var isEmpty = require('lodash.isempty');
var isString = require('lodash.isstring');
var isFunction = require('lodash.isfunction');
var cosmiconfig = require('cosmiconfig');
var mutoParser = require('./muto-parser');
var qryBuilderDef = require('./query-builder-def');
var explorer = cosmiconfig('muto', {
packageProp: false,
rc: false,
js: 'muto.config.js',
argv: false,
sync: true
});
var qryBuilder = Object.assign({}, qryBuilderDef);
try {
debug('Loading user config using cosmiconfig(sync)');
var result = explorer.load('.');
if (!isNil(result)) {
debug('Successfully parsed config -', result);
Object.assign(qryBuilder, result.config);
} else {
debug('muto.config.js not found');
}
} catch (err) {
console.error('Failed to parse config!');
console.error(err);
}
/**
* Parses given expression and generates an `elastic-builder` query object.
*
* @example
* // Pass expression as string
* const qry = muto.parse(
* '["discount"] is false or (["psngr_cnt"] > 81 and ["booking_mode"] contains "Airport")'
* )
*
* // OR
* // Pass conditions using helper classes
* const qry = muto.parse(
* muto.where()
* .or(muto.cn('discount').is(false))
* .or(
* muto.where()
* .and(muto.cn('psngr_cnt', 'gt', 81))
* .and('["booking_mode"] contains "Airport"')
* )
* );
*
* qry.toJSON()
* {
* "bool": {
* "should": [
* { "term": { "discount": false } },
* {
* "bool": {
* "must": [
* {
* "range": { "psngr_cnt": { "gt": 81 } }
* },
* {
* "match": { "booking_mode": "Airport" }
* }
* ]
* }
* }
* ]
* }
* }
*
* @param {string|Where|Condition} expr
* @param {Array} [notAnalysedFields]
* @returns {Object} `elastic-builder` `BoolQuery` object generated by
* parsing the expression
*/
module.exports = function parse(expr, notAnalysedFields) {
if (isEmpty(expr)) {
throw new Error('Expression cannot be empty!');
}
var strExpr = void 0;
if (isString(expr)) strExpr = expr;else if (isFunction(expr.build)) strExpr = expr.build();else strExpr = expr.toString();
debug("Parsing expression '%s'", strExpr);
/*
Wrap expression in ().
This handles the edge case of
(ConditionA and ConditionB) or (ConditionC and ConditionD)
Was not able to handle in PEG.js due to
GrammarError: Possible infinite loop when parsing (left recursion: Expression -> Expression).
(expr) will be parsed the same as ((expr))
So even if expression is already wrapped, it is okay.
*/
try {
return mutoParser.parse(`(${strExpr})`, Object.assign({ notAnalysedFields: new Set(notAnalysedFields), debug }, qryBuilder));
} catch (err) {
console.error('Failed to parse expression', strExpr);
err.expression = strExpr;
throw err;
}
};