From 9879fa49b83d9f6283970952d187897c96f50bdc Mon Sep 17 00:00:00 2001 From: vashjs Date: Tue, 7 Nov 2023 13:04:35 +0000 Subject: [PATCH] =?UTF-8?q?UIPQB-64=20Query=20builder=20can=E2=80=99t=20ed?= =?UTF-8?q?it=20single=20condition=20queries=20without=20AND=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QueryBuilder/helpers/query.js | 111 +++++++++--------- .../QueryBuilder/helpers/query.test.js | 20 ++-- .../QueryBuilder/helpers/selectOptions.js | 2 +- src/constants/operators.js | 2 +- 4 files changed, 69 insertions(+), 66 deletions(-) diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.js b/src/QueryBuilder/QueryBuilder/helpers/query.js index f29b452b..a56c4fab 100644 --- a/src/QueryBuilder/QueryBuilder/helpers/query.js +++ b/src/QueryBuilder/QueryBuilder/helpers/query.js @@ -48,63 +48,66 @@ export const getTransformedValue = (val) => { return val; }; +const escapeRegex = (value) => { + const escapedValue = value.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); + + return `${escapedValue}`; +}; + +const getQueryOperand = (item) => { + let queryOperand = {}; + + const field = item.field.current; + const operator = item.operator.current; + const value = item.value.current; + + switch (operator) { + case OPERATORS.EQUAL: + queryOperand = { [field]: { $eq: value } }; + break; + case OPERATORS.NOT_EQUAL: + queryOperand = { [field]: { $ne: value } }; + break; + case OPERATORS.GREATER_THAN: + queryOperand = { [field]: { $gt: value } }; + break; + case OPERATORS.GREATER_THAN_OR_EQUAL: + queryOperand = { [field]: { $gte: value } }; + break; + case OPERATORS.LESS_THAN: + queryOperand = { [field]: { $lt: value } }; + break; + case OPERATORS.LESS_THAN_OR_EQUAL: + queryOperand = { [field]: { $lte: value } }; + break; + case OPERATORS.IN: + queryOperand = { [field]: { $in: getTransformedValue(value) } }; + break; + case OPERATORS.NOT_IN: + queryOperand = { [field]: { $nin: getTransformedValue(value) } }; + break; + case OPERATORS.STARTS_WITH: + queryOperand = { [field]: { $regex: new RegExp(`^${escapeRegex(value)}`).source } }; + break; + case OPERATORS.CONTAINS: + queryOperand = { [field]: { $regex: new RegExp(escapeRegex(value)).source } }; + break; + default: + break; + } + + return queryOperand; +}; + export const sourceToMongoQuery = (source) => { const query = {}; - const andQuery = []; - let queryItem = {}; - - const escapeRegex = (value) => { - const escapedValue = value.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); - - return `${escapedValue}`; - }; - - source.forEach((item) => { - const field = item.field.current; - const operator = item.operator.current; - const value = item.value.current; - - switch (operator) { - case OPERATORS.EQUAL: - queryItem = { [field]: { $eq: value } }; - break; - case OPERATORS.NOT_EQUAL: - queryItem = { [field]: { $ne: value } }; - break; - case OPERATORS.GREATER_THAN: - queryItem = { [field]: { $gt: value } }; - break; - case OPERATORS.GREATER_THAN_OR_EQUAL: - queryItem = { [field]: { $gte: value } }; - break; - case OPERATORS.LESS_THAN: - queryItem = { [field]: { $lt: value } }; - break; - case OPERATORS.LESS_THAN_OR_EQUAL: - queryItem = { [field]: { $lte: value } }; - break; - case OPERATORS.IN: - queryItem = { [field]: { $in: getTransformedValue(value) } }; - break; - case OPERATORS.NOT_IN: - queryItem = { [field]: { $nin: getTransformedValue(value) } }; - break; - case OPERATORS.STARTS_WITH: - queryItem = { [field]: { $regex: new RegExp(`^${escapeRegex(value)}`).source } }; - break; - case OPERATORS.CONTAINS: - queryItem = { [field]: { $regex: new RegExp(escapeRegex(value)).source } }; - break; - default: - break; - } - - andQuery.push(queryItem); - }); + const boolOperator = source.find(item => Boolean(item.boolean.current))?.boolean.current; + const queryOperands = source.map(getQueryOperand); - // temporary solution, because we should support only $and operator - if (andQuery.length) { - query.$and = andQuery; + if (boolOperator) { + query[boolOperator] = queryOperands; + } else { + return queryOperands[0]; } return query; diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.test.js b/src/QueryBuilder/QueryBuilder/helpers/query.test.js index 54b906a2..d6e23423 100644 --- a/src/QueryBuilder/QueryBuilder/helpers/query.test.js +++ b/src/QueryBuilder/QueryBuilder/helpers/query.test.js @@ -17,62 +17,62 @@ describe('mongoQueryToSource()', () => { const source = [ { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_first_name' }, operator: { options: expect.any(Array), current: OPERATORS.EQUAL }, value: { current: 'value' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_first_name' }, operator: { options: expect.any(Array), current: OPERATORS.NOT_EQUAL }, value: { current: 'value' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_last_name' }, operator: { options: expect.any(Array), current: OPERATORS.GREATER_THAN }, value: { current: 'value' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_last_name' }, operator: { options: expect.any(Array), current: OPERATORS.LESS_THAN }, value: { current: 10 }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_last_name' }, operator: { options: expect.any(Array), current: OPERATORS.GREATER_THAN_OR_EQUAL }, value: { current: 'value' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'languages' }, operator: { options: expect.any(Array), current: OPERATORS.IN }, value: { current: [{ label: 'value', value: 'value' }, { label: 'value2', value: 'value2' }] }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_full_name' }, operator: { options: expect.any(Array), current: OPERATORS.CONTAINS }, value: { current: 'abc' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'languages' }, operator: { options: expect.any(Array), current: OPERATORS.NOT_IN }, value: { current: [{ label: 'value', value: 'value' }, { label: 'value2', value: 'value2' }] }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_id' }, operator: { options: expect.any(Array), current: OPERATORS.NOT_IN }, value: { current: 'value, value2' }, }, { - boolean: { options: booleanOptions, current: 'AND' }, + boolean: { options: booleanOptions, current: '$and' }, field: { options: fieldOptions, current: 'user_id' }, operator: { options: expect.any(Array), current: OPERATORS.IN }, value: { current: 'value, value2' }, diff --git a/src/QueryBuilder/QueryBuilder/helpers/selectOptions.js b/src/QueryBuilder/QueryBuilder/helpers/selectOptions.js index 6337d64a..a1d0a1ee 100644 --- a/src/QueryBuilder/QueryBuilder/helpers/selectOptions.js +++ b/src/QueryBuilder/QueryBuilder/helpers/selectOptions.js @@ -106,7 +106,7 @@ export const getFieldOptions = (options) => { }; export const booleanOptions = [ - { label: 'AND', value: 'AND' }, + { label: 'AND', value: OPERATORS.AND }, ]; export const sourceTemplate = (fieldOptions = []) => ({ diff --git a/src/constants/operators.js b/src/constants/operators.js index 0860c0df..1f64d535 100644 --- a/src/constants/operators.js +++ b/src/constants/operators.js @@ -9,5 +9,5 @@ export const OPERATORS = { NOT_IN: 'not in', CONTAINS: 'contains', STARTS_WITH: 'starts with', - AND: 'AND', + AND: '$and', };