@coocoon/react-awesome-query-builder
Version:
User-friendly query builder for React. Demo: https://ukrbublik.github.io/react-awesome-query-builder
371 lines (302 loc) • 11.6 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.elasticSearchFormat = elasticSearchFormat;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _ruleUtils = require("../utils/ruleUtils");
var _defaultUtils = require("../utils/defaultUtils");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
/**
* Converts a string representation of top_left and bottom_right cords to
* a ES geo_point required for query
*
* @param {string} geoPointString - comma separated string of lat/lon coods
* @returns {{top_left: {lon: number, lat: number}, bottom_right: {lon: number, lat: number}}} - ES geoPoint formatted object
* @private
*/
function buildEsGeoPoint(geoPointString) {
if (geoPointString == null) {
return null;
}
var coordsNumberArray = geoPointString.split(",").map(Number);
return {
top_left: {
lat: coordsNumberArray[0],
lon: coordsNumberArray[1]
},
bottom_right: {
lat: coordsNumberArray[2],
lon: coordsNumberArray[3]
}
};
}
/**
* Converts a dateTime string from the query builder to a ES range formatted object
*
* @param {string} dateTime - dateTime formatted string
* @param {string} operator - query builder operator type, see constants.js and query builder docs
* @returns {{lt: string}|{lte: string}|{gte: string}|{gte: string, lte: string}|undefined} - ES range query parameter
*
* @private
*/
function buildEsRangeParameters(value, operator) {
// -- if value is greater than 1 then we assume this is a between operator : BUG this is wrong, a selectable list can have multiple values
if (value.length > 1) {
return {
gte: "".concat(value[0]),
lte: "".concat(value[1])
};
} // -- if value is only one we assume this is a date time query for a specific day
var dateTime = value[0]; //TODO: Rethink about this part, what if someone adds a new type of opperator
//todo: move this logic into config
switch (operator) {
case "on_date": //todo: not used
case "not_on_date":
case "equal":
case "select_equals":
case "not_equal":
return {
gte: "".concat(dateTime, "||/d"),
lte: "".concat(dateTime, "||+1d")
};
case "less_or_equal":
return {
lte: "".concat(dateTime)
};
case "greater_or_equal":
return {
gte: "".concat(dateTime)
};
case "less":
return {
lt: "".concat(dateTime)
};
case "greater":
return {
gte: "".concat(dateTime)
};
default:
return undefined;
}
}
/**
* Builds the DSL parameters for a Wildcard query
*
* @param {string} value - The match value
* @returns {{value: string}} - The value = value parameter surrounded with * on each end
* @private
*/
function buildEsWildcardParameters(value) {
return {
value: "*" + value + "*"
};
}
/**
* Takes the match type string from awesome query builder like 'greater_or_equal' and
* returns the ES occurrence required for bool queries
*
* @param {string} combinator - query group type or rule condition
* @returns {string} - ES occurrence type. See constants.js
* @private
*/
function determineOccurrence(combinator) {
//todo: move into config, like mongoConj
switch (combinator) {
case "AND":
return "must";
// -- AND
case "OR":
return "should";
// -- OR
case "NOT":
return "must_not";
// -- NOT AND
default:
return undefined;
}
}
/**
* Determines what field to query off of given the operator type
*
* @param {string} fieldDataType - The type of data
* @param {string} fullFieldName - A '.' separated string containing the property lineage (including self)
* @param {string} queryType - The query type
* @returns {string|*} - will be either the fullFieldName or fullFieldName.keyword
* @private
*/
//todo: not used
function determineQueryField(fieldDataType, fullFieldName, queryType) {
if (fieldDataType === "boolean") {
return fullFieldName;
}
switch (queryType) {
case "term":
case "wildcard":
return "".concat(fullFieldName, ".keyword");
case "geo_bounding_box":
case "range":
case "match":
return fullFieldName;
default:
console.error("Can't determine query field for query type ".concat(queryType));
return null;
}
}
function buildRegexpParameters(value) {
return {
value: value
};
}
function determineField(fieldName, config) {
//todo: ElasticSearchTextField - not used
//return config.fields[fieldName].ElasticSearchTextField || fieldName;
return fieldName;
}
function buildParameters(queryType, value, operator, fieldName, config) {
var textField = determineField(fieldName, config);
switch (queryType) {
case "filter":
//todo: elasticSearchScript - not used
return {
script: config.operators[operator].elasticSearchScript(fieldName, value)
};
case "exists":
return {
field: fieldName
};
case "match":
return (0, _defineProperty2["default"])({}, textField, value[0]);
case "term":
return (0, _defineProperty2["default"])({}, fieldName, value[0]);
//todo: not used
// need to add geo type into RAQB or remove this code
case "geo_bounding_box":
return (0, _defineProperty2["default"])({}, fieldName, buildEsGeoPoint(value[0]));
case "range":
return (0, _defineProperty2["default"])({}, fieldName, buildEsRangeParameters(value, operator));
case "wildcard":
return (0, _defineProperty2["default"])({}, fieldName, buildEsWildcardParameters(value[0]));
case "regexp":
return (0, _defineProperty2["default"])({}, fieldName, buildRegexpParameters(value[0]));
default:
return undefined;
}
}
/**
* Handles the building of the group portion of the DSL
*
* @param {string} fieldName - The name of the field you are building a rule for
* @param {string} fieldDataType - The type of data this field holds
* @param {string} value - The value of this rule
* @param {string} operator - The condition on how the value is matched
* @returns {object} - The ES rule
* @private
*/
function buildEsRule(fieldName, value, operator, config, valueSrc) {
if (!fieldName || !operator || value == undefined) return undefined; // rule is not fully entered
var op = operator;
var opConfig = config.operators[op];
if (!opConfig) return undefined; // unknown operator
var _opConfig = opConfig,
elasticSearchQueryType = _opConfig.elasticSearchQueryType; // not
var not = false;
if (!elasticSearchQueryType && opConfig.reversedOp) {
not = true;
op = opConfig.reversedOp;
opConfig = config.operators[op];
var _opConfig2 = opConfig;
elasticSearchQueryType = _opConfig2.elasticSearchQueryType;
} // handle if value 0 has multiple values like a select in a array
var widget = (0, _ruleUtils.getWidgetForFieldOp)(config, fieldName, op, valueSrc);
var widgetConfig = config.widgets[widget];
var elasticSearchFormatValue = widgetConfig.elasticSearchFormatValue;
/** In most cases the queryType will be static however in some casese (like between) the query type will change
* based on the data type. i.e. a between time will be different than between number, date, letters etc... */
var queryType;
if (typeof elasticSearchQueryType === "function") {
queryType = elasticSearchQueryType(widget);
} else {
queryType = elasticSearchQueryType;
}
if (!queryType) {
// Not supported
return undefined;
}
/** If a widget has a rule on how to format that data then use that otherwise use default way of determineing search parameters
* */
var parameters;
if (typeof elasticSearchFormatValue === "function") {
parameters = elasticSearchFormatValue(queryType, value, op, fieldName, config);
} else {
parameters = buildParameters(queryType, value, op, fieldName, config);
}
if (not) {
return {
bool: {
must_not: (0, _defineProperty2["default"])({}, queryType, _objectSpread({}, parameters))
}
};
} else {
return (0, _defineProperty2["default"])({}, queryType, _objectSpread({}, parameters));
}
}
/**
* Handles the building of the group portion of the DSL
*
* @param {object} children - The contents of the group
* @param {string} conjunction - The way the contents of the group are joined together i.e. AND OR
* @param {Function} recursiveFxn - The recursive fxn to build the contents of the groups children
* @private
* @returns {object} - The ES group
*/
function buildEsGroup(children, conjunction, recursiveFxn, config) {
if (!children || !children.size) return undefined;
var childrenArray = children.valueSeq().toArray();
var occurrence = determineOccurrence(conjunction);
var result = childrenArray.map(function (c) {
return recursiveFxn(c, config);
}).filter(function (v) {
return v !== undefined;
});
if (!result.length) return undefined;
var resultFlat = result.flat(Infinity);
return {
bool: (0, _defineProperty2["default"])({}, occurrence, resultFlat)
};
}
function elasticSearchFormat(tree, config) {
// -- format the es dsl here
if (!tree) return undefined;
var type = tree.get("type");
var properties = tree.get("properties") || new Map();
if (type === "rule" && properties.get("field")) {
var _properties$get, _properties$get2;
// -- field is null when a new blank rule is added
var operator = properties.get("operator");
var field = properties.get("field");
var value = properties.get("value").toJS();
var _valueType = (_properties$get = properties.get("valueType")) === null || _properties$get === void 0 ? void 0 : _properties$get.get(0);
var valueSrc = (_properties$get2 = properties.get("valueSrc")) === null || _properties$get2 === void 0 ? void 0 : _properties$get2.get(0);
if (valueSrc === "func") {
// -- elastic search doesn't support functions (that is post processing)
return;
}
if (value && Array.isArray(value[0])) {
//TODO : Handle case where the value has multiple values such as in the case of a list
return value[0].map(function (val) {
return buildEsRule(field, [val], operator, config, valueSrc);
});
} else {
return buildEsRule(field, value, operator, config, valueSrc);
}
}
if (type === "group" || type === "rule_group") {
var conjunction = properties.get("conjunction");
if (!conjunction) conjunction = (0, _defaultUtils.defaultConjunction)(config);
var children = tree.get("children1");
return buildEsGroup(children, conjunction, elasticSearchFormat, config);
}
}