diff --git a/arangod/Indexes/Index.cpp b/arangod/Indexes/Index.cpp index 8eeb2e9eb7..9b6e6e7cc4 100644 --- a/arangod/Indexes/Index.cpp +++ b/arangod/Indexes/Index.cpp @@ -508,6 +508,10 @@ bool Index::canUseConditionPart (triagens::aql::AstNode const* access, triagens::aql::Variable const* reference) const { if (_sparse) { + if (op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_NIN) { + return false; + } + if (op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_IN && other->type == triagens::aql::NODE_TYPE_EXPANSION) { // value IN a.b @@ -525,15 +529,23 @@ bool Index::canUseConditionPart (triagens::aql::AstNode const* access, return false; } - if (other->isNullValue()) { + if (op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_LT || + op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_LE) { + // < and <= are not supported with sparse indexes as this may include null values return false; } - TRI_ASSERT_EXPENSIVE(other->isConstant()); + if (other->isNullValue() && + (op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_EQ || + op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_GE)) { + // == and >= null are not supported with sparse indexes for the same reason + return false; + } if (op->type == triagens::aql::NODE_TYPE_OPERATOR_BINARY_IN && other->type == triagens::aql::NODE_TYPE_ARRAY) { size_t const n = other->numMembers(); + for (size_t i = 0; i < n; ++i) { if (other->getMemberUnchecked(i)->isNullValue()) { return false; diff --git a/arangod/Indexes/SkiplistIndex.cpp b/arangod/Indexes/SkiplistIndex.cpp index bdad13b5cb..f3a2d4bdf8 100644 --- a/arangod/Indexes/SkiplistIndex.cpp +++ b/arangod/Indexes/SkiplistIndex.cpp @@ -1203,7 +1203,12 @@ bool SkiplistIndex::supportsFilterCondition (triagens::aql::AstNode const* node, return true; } - if (attributesCovered > 0) { + if (attributesCovered > 0 && + (! _sparse || (_sparse && attributesCovered == _fields.size()))) { + // if the condition contains at least one index attribute and is not sparse, + // or the index is sparse and all attributes are covered by the condition, + // then it can be used (note: additional checks for condition parts in + // sparse indexes are contained in Index::canUseConditionPart) estimatedItems = static_cast((std::max)(static_cast(estimatedCost * values), static_cast(1))); estimatedCost *= static_cast(values); return true; diff --git a/js/server/tests/aql-optimizer-indexes.js b/js/server/tests/aql-optimizer-indexes.js index b5c2bfc0b6..8ce949b4e0 100644 --- a/js/server/tests/aql-optimizer-indexes.js +++ b/js/server/tests/aql-optimizer-indexes.js @@ -2464,34 +2464,22 @@ function optimizerIndexesTestSuite () { c.ensureSkiplist("value2", "value3", { sparse: true }); var queries = [ - ["FOR i IN " + c.name() + " FILTER i.value2 == 2 RETURN i.value", true], - ["FOR i IN " + c.name() + " FILTER i.value3 == 2 RETURN i.value", false] + "FOR i IN " + c.name() + " FILTER i.value2 == 2 RETURN i.value", + "FOR i IN " + c.name() + " FILTER i.value3 == 2 RETURN i.value" ]; queries.forEach(function(query) { - var plan = AQL_EXPLAIN(query[0]).plan; + var plan = AQL_EXPLAIN(query).plan; var nodeTypes = plan.nodes.map(function(node) { return node.type; }); - var results; - - if (query[1]) { - // Can use Index - assertNotEqual(-1, nodeTypes.indexOf("IndexNode"), query[0]); - results = AQL_EXECUTE(query[0]); - assertEqual([ 2 ], results.json, query[0]); - assertTrue(results.stats.scannedIndex > 0); - assertEqual(0, results.stats.scannedFull); - } - else { - // Cannot use Index - assertEqual(-1, nodeTypes.indexOf("IndexNode"), query[0]); - results = AQL_EXECUTE(query[0]); - assertEqual([ 2 ], results.json, query[0]); - assertTrue(results.stats.scannedFull > 0); - assertEqual(0, results.stats.scannedIndex); - } + // Cannot use Index + assertEqual(-1, nodeTypes.indexOf("IndexNode"), query); + var results = AQL_EXECUTE(query); + assertEqual([ 2 ], results.json, query); + assertTrue(results.stats.scannedFull > 0); + assertEqual(0, results.stats.scannedIndex); }); },