odata-filter-builder
Version:
OData Filter Builder
610 lines (510 loc) • 19.9 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.ODataFilterBuilder = {}));
}(this, (function (exports) { 'use strict';
var canonicalFunctions = /*#__PURE__*/Object.freeze({
__proto__: null,
get canonicalFunction () { return canonicalFunction; },
get contains () { return contains; },
get startsWith () { return startsWith; },
get endsWith () { return endsWith; },
get toLower () { return toLower; },
get toUpper () { return toUpper; },
get trim () { return trim; },
get substring () { return substring; },
get concat () { return concat; },
get length () { return length; },
get indexOf () { return indexOf; }
});
/**
* Reduce source with new rule and/or condition
* @param {Object} source - Source rule
* @param {Object|string} rule - Rule to add
* @param {string} [condition] - Condition for rule to add(and/or)
* @returns {Object} updated rule
* @private
*/
function reduceSourceWithRule(source, rule, condition) {
if (rule) {
if (condition && source.condition !== condition) {
// if source rules condition different from rule condition
// update source condition
source = {
condition: condition,
// if has more then one rules
// regroup source rules tree
rules: source.rules.length > 1 ? [source] : source.rules
};
} // add new rule
source.rules.push(rule);
}
return source;
}
function inputRuleToString(rule) {
if (typeof rule === 'function') {
rule = rule(new ODataFilterBuilder());
}
return rule && rule.toString();
}
function joinRulesWithCondition(rules, condition) {
return rules.map(function (r) {
return sourceRuleToString(r, true);
}).join(" " + condition + " ");
}
function sourceRuleToString(rule, wrapInParenthesis) {
if (wrapInParenthesis === void 0) {
wrapInParenthesis = false;
}
if (typeof rule !== 'string') {
// if child rules more then one join child rules by condition
// and wrap in brackets every child rule
rule = rule.rules.length === 1 ? sourceRuleToString(rule.rules[0]) : joinRulesWithCondition(rule.rules, rule.condition);
}
return wrapInParenthesis ? "(" + rule + ")" : rule;
}
function inputFieldToString(field) {
return typeof field === 'function' ? field(canonicalFunctions) : field;
}
function isString(value) {
return typeof value === 'string';
}
function isDate(value) {
return typeof value === 'object' && Object.prototype.toString.call(value) === '[object Date]';
}
function normaliseValue(value) {
if (isString(value)) {
return "'" + value + "'";
}
if (isDate(value)) {
return value.toISOString();
}
return value;
}
function canonicalFunction(functionName, field, values, normaliseValues, reverse) {
if (normaliseValues === void 0) {
normaliseValues = true;
}
if (reverse === void 0) {
reverse = false;
}
// make sure that field is string
field = inputFieldToString(field);
if (typeof values === 'undefined') {
values = [];
} else if (!Array.isArray(values)) {
values = [values];
}
if (values.length === 0) {
return functionName + "(" + field + ")";
}
if (normaliseValues) {
values = values.map(normaliseValue);
}
var functionArgs = !reverse ? [field].concat(values) : [].concat(values, [field]);
return functionName + "(" + functionArgs.join(', ') + ")";
}
function contains(field, value) {
return canonicalFunction('contains', field, value);
}
function startsWith(field, value) {
return canonicalFunction('startswith', field, value);
}
function endsWith(field, value) {
return canonicalFunction('endswith', field, value);
}
/**
* The tolower function returns the input parameter string value with all uppercase characters converted to lowercase.
* @example
* f().eq(x => x.toLower('CompanyName'), 'alfreds futterkiste')
* // tolower(CompanyName) eq 'alfreds futterkiste'
* @param {string|InputFieldExpression} field - Field
* @returns {string} A function string
*/
function toLower(field) {
return canonicalFunction('tolower', field);
}
/**
* The toupper function returns the input parameter string value with all lowercase characters converted to uppercase.
* @example
* f().eq(x => x.toUpper('CompanyName'), 'ALFREDS FUTTERKISTE')
* // toupper(CompanyName) eq 'ALFREDS FUTTERKISTE'
* @param {string|InputFieldExpression} field - Field
* @returns {string} A function string
*/
function toUpper(field) {
return canonicalFunction('toupper', field);
}
/**
* The trim function returns the input parameter string value with all leading and trailing whitespace characters, removed.
* @example
* f().eq(x => x.trim('CompanyName'), 'CompanyName')
* // trim(CompanyName) eq CompanyName
* @param {string|InputFieldExpression} field - Field
* @returns {string} A function string
*/
function trim(field) {
return canonicalFunction('trim', field);
}
/**
* @example
* f().eq(f.functions.substring('CompanyName', 1), 'lfreds Futterkiste');
* f().eq(x => x.substring('CompanyName', 1), 'lfreds Futterkiste');
* // substring(CompanyName, 1) eq 'lfreds Futterkiste'
*
* @example
* f().eq(x => x.substring('CompanyName', 1, 2), 'lf').toString();
* f().eq(f.functions.substring('CompanyName', 1, 2), 'lf')
* // substring(CompanyName, 1, 2) eq 'lf'
*
* @param {string|InputFieldExpression} field - The first function parameter
* @param {...number} values - Second or second and third function parameters
*
* @returns {string} A function string
*/
function substring(field) {
for (var _len = arguments.length, values = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
values[_key - 1] = arguments[_key];
}
return canonicalFunction('substring', field, values);
}
/**
* @param {string|InputFieldExpression} field - The first function parameter
* @param {string} value - The second function parameter
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @example
* f().eq(x => x.concat(y => y.concat('City',', '), 'Country', false), 'Berlin, Germany');
* // concat(concat(City, ', '), 'Country') eq 'Berlin, Germany'
* @returns {string} A function string
*/
function concat(field, value, normaliseValue) {
return canonicalFunction('concat', field, [value], normaliseValue);
}
/**
* The length function returns the number of characters in the parameter value.
* @example
* f().eq(x => x.length('CompanyName'), 19)
* // length(CompanyName) eq 19
* @param {string|InputFieldExpression} field - Field
* @returns {string} A function string
*/
function length(field) {
return canonicalFunction('length', field);
}
/**
* The indexof function returns the zero-based character position of the first occurrence of the second parameter value in the first parameter value.
* @example
* f().eq(f.functions.indexOf('CompanyName', 'lfreds'), 1)
* f().eq(x => x.indexOf('CompanyName', 'lfreds'), 1)
* // indexof(CompanyName,'lfreds') eq 1
*
* @param {string|InputFieldExpression} field - The first function parameter
* @param {string} value - The second function parameter
*
* @returns {string} A function string
*/
function indexOf(field, value) {
return canonicalFunction('indexof', field, [value]);
}
function not(rule) {
var ruleString = inputRuleToString(rule);
if (ruleString) {
return "not (" + ruleString + ")";
}
}
function compare(field, operator, value, normaliseValue$1) {
if (normaliseValue$1 === void 0) {
normaliseValue$1 = true;
}
// make sure that field is string
field = inputFieldToString(field);
if (normaliseValue$1) {
value = normaliseValue(value);
}
return field + " " + operator + " " + value;
}
function compareMap(field, operator, values, normaliseValues) {
if (normaliseValues === void 0) {
normaliseValues = true;
}
if (!values) {
return [];
} // make sure that field is string
field = inputFieldToString(field);
if (!Array.isArray(values)) {
return [compare(field, operator, values, normaliseValues)];
}
return values.map(function (value) {
return compare(field, operator, value, normaliseValues);
});
}
function eq(field, value, normaliseValue) {
return compare(field, 'eq', value, normaliseValue);
}
function ne(field, value, normaliseValue) {
return compare(field, 'ne', value, normaliseValue);
}
function gt(field, value, normaliseValue) {
return compare(field, 'gt', value, normaliseValue);
}
function ge(field, value, normaliseValue) {
return compare(field, 'ge', value, normaliseValue);
}
function lt(field, value, normaliseValue) {
return compare(field, 'lt', value, normaliseValue);
}
function le(field, value, normaliseValue) {
return compare(field, 'le', value, normaliseValue);
}
function joinRules(rules, condition) {
return rules.join(" " + condition + " ");
}
function compareIn(field, values, normaliseValues) {
return joinRules(compareMap(field, 'eq', values, normaliseValues), 'or');
}
function compareAll(objectValue, normaliseValues) {
var keys = Object.keys(objectValue);
var rules = keys.filter(function (k) {
return typeof objectValue[k] !== 'undefined';
}).map(function (field) {
var value = objectValue[field];
if (Array.isArray(value)) {
return "(" + compareIn(field, value, normaliseValues) + ")";
} else {
return eq(field, value, normaliseValues);
}
});
return joinRules(rules, 'and');
}
function compareNotIn(field, values, normaliseValues) {
// return joinRules(compareMap(field, 'ne', values, normaliseValues), 'and')
return not(compareIn(field, values, normaliseValues));
}
var ODataFilterBuilder =
/*#__PURE__*/
function () {
function ODataFilterBuilder(condition) {
if (condition === void 0) {
condition = 'and';
}
if (!(this instanceof ODataFilterBuilder)) {
return new ODataFilterBuilder(condition);
}
this._condition = condition;
this._source = {
condition: condition,
rules: []
};
}
/**
* The 'add' method adds new filter rule with AND or OR condition
* if condition not provided. Source condition is used (AND by default)
* @this {ODataFilterBuilder}
* @param {string|ODataFilterBuilder|InputRuleExpression} rule - Rule to add
* @param {string} [condition] - Condition for rule to add(and/or)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
* @private
*/
var _proto = ODataFilterBuilder.prototype;
_proto._add = function _add(rule, condition) {
if (condition === void 0) {
condition = this._condition;
}
// NOTE: if condition not provider, source condition uses
this._source = reduceSourceWithRule(this._source, inputRuleToString(rule), condition);
return this;
}
/*
* Logical Operators
*/
/**
* Logical And
* @param {string|ODataFilterBuilder|InputRuleExpression} rule - Rule to add
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.and = function and(rule) {
return this._add(rule, 'and');
}
/**
* Logical Or
* @param {string|ODataFilterBuilder|InputRuleExpression} rule - Rule to add
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.or = function or(rule) {
return this._add(rule, 'or');
}
/**
* Logical Negation
* @param {string|ODataFilterBuilder|InputRuleExpression} rule - Rule to add
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.not = function not$1(rule) {
return this._add(not(rule));
}
/**
* Equal
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.eq = function eq$1(field, value, normaliseValue) {
return this._add(eq(field, value, normaliseValue));
}
/**
* Not Equal
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.ne = function ne$1(field, value, normaliseValue) {
return this._add(ne(field, value, normaliseValue));
}
/**
* Greater Than
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.gt = function gt$1(field, value, normaliseValue) {
return this._add(gt(field, value, normaliseValue));
}
/**
* Greater than or Equal
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.ge = function ge$1(field, value, normaliseValue) {
return this._add(ge(field, value, normaliseValue));
}
/**
* Less Than
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.lt = function lt$1(field, value, normaliseValue) {
return this._add(lt(field, value, normaliseValue));
}
/**
* Less than or Equal
* @param {string|InputFieldExpression} field - Field to compare
* @param {string|number|*} value - A value to compare with
* @param {boolean} [normaliseValue=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.le = function le$1(field, value, normaliseValue) {
return this._add(le(field, value, normaliseValue));
}
/**
* @param {string|InputFieldExpression} field - Field to compare
* @param {string[]|string} values - Values to compare with
* @param {boolean} [normaliseValues=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto["in"] = function _in(field, values, normaliseValues) {
return this._add(compareIn(field, values, normaliseValues));
}
/**
* @param {any} objectValue - Object with property and value to compare all in "and" - Loops through property keys
* @param {boolean} [normaliseValues=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.compareAll = function compareAll$1(objectValue, normaliseValues) {
return this._add(compareAll(objectValue, normaliseValues));
}
/**
* @param {string|InputFieldExpression} field - Field to compare
* @param {Array} values - Values to compare with
* @param {boolean} [normaliseValues=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.notIn = function notIn(field, values, normaliseValues) {
return this._add(compareNotIn(field, values, normaliseValues));
} // Canonical Functions
/**
* The contains function returns true if the second parameter string value is a substring of the first parameter string value.
* @param {string|InputFieldExpression} field - Field to compare
* @param {string} value - Value to compare
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.contains = function contains$1(field, value) {
return this._add(contains(field, value));
}
/**
* The startswith function returns true if the first parameter string value starts with the second parameter string value.
* @param {string|InputFieldExpression} field - Field to compare
* @param {string} value - Value to compare
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.startsWith = function startsWith$1(field, value) {
return this._add(startsWith(field, value));
}
/**
* The endswith function returns true if the first parameter string value ends with the second parameter string value.
* @param {string|InputFieldExpression} field - Field to compare
* @param {string} value - Value to compare
* @returns {ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.endsWith = function endsWith$1(field, value) {
return this._add(endsWith(field, value));
}
/**
* Custom function
* @param {string} functionName - Name of generated function
* @param {string|InputFieldExpression} field - The first function parameter
* @param {string|number|Array} values - The second function parameter
* @param {boolean} [normaliseValues=true] - Convert string "value" to "'value'" or not. (Convert by default)
* @param {boolean} [reverse=false] - Swap field and value params in output. (Don't swap by default)
* @returns {*|ODataFilterBuilder} The {@link ODataFilterBuilder} instance
*/
;
_proto.fn = function fn(functionName, field, values, normaliseValues, reverse) {
return this._add(canonicalFunction(functionName, field, values, normaliseValues, reverse));
};
_proto.isEmpty = function isEmpty() {
return this._source.rules.length === 0;
}
/**
* Convert filter builder instance to string
* @this {ODataFilterBuilder}
* @returns {string} A source string representation
*/
;
_proto.toString = function toString() {
return sourceRuleToString(this._source);
};
return ODataFilterBuilder;
}();
ODataFilterBuilder.and = function () {
return new ODataFilterBuilder('and');
};
ODataFilterBuilder.or = function () {
return new ODataFilterBuilder('or');
};
ODataFilterBuilder.functions = canonicalFunctions;
exports.ODataFilterBuilder = ODataFilterBuilder;
exports.canonicalFunctions = canonicalFunctions;
exports.default = ODataFilterBuilder;
Object.defineProperty(exports, '__esModule', { value: true });
})));