diff --git a/addons/api/addon/utils/sqlite-query.js b/addons/api/addon/utils/sqlite-query.js index 75ec96c2c..458a657fa 100644 --- a/addons/api/addon/utils/sqlite-query.js +++ b/addons/api/addon/utils/sqlite-query.js @@ -75,6 +75,34 @@ function addFilterConditions({ filters, parameters, conditions }) { continue; } + // When attribute conditions are a series of equals/notEquals + // replace with in/notIn to avoid hitting the sqlite maximum + // expression tree depth. + const firstOperator = Object.keys(filterValueArray[0])[0]; + const allOperatorsEqual = filterValueArray + .flatMap((item) => Object.keys(item)) + .every((op) => op === firstOperator); + if ( + filterValueArray.length > 1 && + (firstOperator === 'equals' || firstOperator === 'notEquals') && + allOperatorsEqual + ) { + const operation = firstOperator === 'equals' ? 'in' : 'notIn'; + const values = filterValueArray + .filter((f) => f) + .map((filterObjValue) => { + let value = Object.values(filterObjValue)[0]; + if (typeOf(value) === 'date') { + value = value.toISOString(); + } + parameters.push(value); + return value; + }); + const filterCondition = `${key}${OPERATORS[operation](values)}`; + conditions.push(parenthetical(filterCondition)); + continue; + } + const filterConditions = filterValueArray .filter((f) => f) .map((filterObjValue) => { @@ -195,6 +223,8 @@ const OPERATORS = { lt: ' < ?', lte: ' <= ?', contains: ' LIKE ?', + in: (values) => ` IN (${values.map(() => '?').join(', ')})`, + notIn: (values) => ` NOT IN (${values.map(() => '?').join(', ')})`, }; // Logical Operators diff --git a/addons/api/tests/unit/utils/sqlite-query-test.js b/addons/api/tests/unit/utils/sqlite-query-test.js index cac353217..57a47cbfe 100644 --- a/addons/api/tests/unit/utils/sqlite-query-test.js +++ b/addons/api/tests/unit/utils/sqlite-query-test.js @@ -150,7 +150,7 @@ module('Unit | Utility | sqlite-query', function (hooks) { }, }, expectedWhereClause: - 'WHERE (id != ? AND id != ?) AND (status = ? OR status = ?) AND (type = ?)', + 'WHERE (id NOT IN (?, ?)) AND (status IN (?, ?)) AND (type = ?)', expectedParams: ['id1', 'id2', 'active', 'pending', 'ssh'], }, }, @@ -271,7 +271,7 @@ module('Unit | Utility | sqlite-query', function (hooks) { sql, ` SELECT * FROM "target" - WHERE (id != ? AND id != ?) AND (status = ? OR status = ?) AND rowid IN (SELECT rowid FROM target_fts WHERE target_fts MATCH ?) + WHERE (id NOT IN (?, ?)) AND (status IN (?, ?)) AND rowid IN (SELECT rowid FROM target_fts WHERE target_fts MATCH ?) ORDER BY created_time DESC`.removeExtraWhiteSpace(), ); assert.deepEqual(parameters, [ @@ -307,7 +307,7 @@ module('Unit | Utility | sqlite-query', function (hooks) { sql, ` SELECT data FROM "target" - WHERE (type = ?) AND (status = ? OR status = ?) AND (created_time >= ?) AND rowid IN (SELECT rowid FROM target_fts WHERE target_fts MATCH ?) + WHERE (type = ?) AND (status IN (?, ?)) AND (created_time >= ?) AND rowid IN (SELECT rowid FROM target_fts WHERE target_fts MATCH ?) ORDER BY name COLLATE NOCASE DESC, name DESC LIMIT ? OFFSET ?`.removeExtraWhiteSpace(), );