@comodinx/query-parser
Version:
@comodinx/query-parser is a module for parser query parameters on ORM object (sequelize).
185 lines (156 loc) • 3.52 kB
JavaScript
const { Mappers, Parser } = require("@comodinx/query-filters");
const {
find,
isArray,
isBoolean,
isEmpty,
isNumber,
isObject,
isString,
map,
merge,
reduce,
startsWith
} = require("lodash");
const { literal, Op, where } = require("sequelize");
//
// constants
//
const trues = ["true", "1", "on"];
const falses = ["false", "0", "off"];
const booleans = [...trues, ...falses];
// operator mappers
const mapOperator = {
ee: Op.eq, // Exact Equal
eq: Op.eq,
ne: Op.ne,
gt: Op.gt,
ge: Op.gte,
lt: Op.lt,
le: Op.lte,
li: Op.like,
nl: Op.notLike,
in: Op.in,
ni: Op.notIn,
be: Op.between,
nb: Op.notBetween,
is: Op.is,
no: Op.not
};
// filter separator
const defaultSeparator = ",";
const key = "[A-Za-z0-9_.]+";
const valueLikeParse = /\*/g;
// eslint-disable-next-line no-useless-escape
const valueLikeFormat = /\%/g;
// Json keys
const defaultJsonKeys = ["metadata"];
// Extends mappers on @comodinx/query-filters
Mappers.Sequelize = mapOperator;
//
// helpers
//
const isNumberString = (value) => {
if (isNumber(value)) {
return true;
}
return !isEmpty(value) && !isNaN(Number(value));
};
const isBooleanString = (value) => {
return !isEmpty(value) && booleans.includes(String(value).toLowerCase());
};
const mapValueParse = (value, operator) => {
// exact equal
if (operator.trim() === "ee") {
return literal(value);
}
// is or is not
if (operator.trim() === "is" || operator.trim() === "no") {
if (value === "null" || value === "undefined") {
return null;
}
return value;
}
if (isArray(value)) {
return map(value, (value) => mapValueParse(value, operator));
}
if (isNumberString(value)) {
return Number(value);
}
if (isBoolean(value)) {
return value;
}
if (isBooleanString(value)) {
return trues.includes(String(value).toLowerCase());
}
if (isString(value)) {
return value.replace(valueLikeParse, "%");
}
return value;
};
const mapValueFormat = (value, operator) => {
if (isArray(value)) {
return `[${value.map((value) => mapValueFormat(value, operator)).join(";")}]`;
}
if (isObject(value)) {
return JSON.stringify(value);
}
if (isString(value)) {
return value.replace(valueLikeFormat, "*");
}
return value;
};
const createParser = (options = {}) => {
return new Parser(
merge(
{},
{
separator: defaultSeparator,
mapValueFormat,
mapValueParse,
mapOperator,
key
},
options
)
);
};
//
// source code
//
const filters = (query, options = {}) => {
// Save last parser instance.
const parser = (filters.lastParser = createParser(
merge(
{},
{
separator: (query && query.filtersSeparator) || defaultSeparator
},
options || {}
)
));
if (!query || !query.filters || isEmpty(query.filters)) {
return;
}
const parsed = parser.parse(query.filters);
if (!parsed) {
return;
}
const jsonKeys = options.jsonKeys || defaultJsonKeys;
return reduce(
parsed,
(carry, condition, key) => {
const jsonKey = find(jsonKeys, (jsonKey) => startsWith(key, `${jsonKey}.`));
if (jsonKey) {
carry[Op.and] = carry[Op.and] || [];
carry[Op.and].push(where(literal(`${jsonKey}->"$${key.replace(jsonKey, "")}"`), condition));
} else {
carry[key] = condition;
}
return carry;
},
{}
);
};
filters.createParser = createParser;
module.exports = filters;