From cabeb8fc7b37e1628ee0b13951cee80d8faa3bba Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 21 Jan 2019 23:11:17 +0100 Subject: [PATCH] make use of projections if the projected data is used by an IndexNode's condition (#8001) --- .../RocksDBEngine/RocksDBOptimizerRules.cpp | 24 ++++++++++++----- arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp | 1 - ...reduce-extraction-to-projection-rocksdb.js | 27 ++++++++++++++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/arangod/RocksDBEngine/RocksDBOptimizerRules.cpp b/arangod/RocksDBEngine/RocksDBOptimizerRules.cpp index 43b5fa2c19..3347ac967c 100644 --- a/arangod/RocksDBEngine/RocksDBOptimizerRules.cpp +++ b/arangod/RocksDBEngine/RocksDBOptimizerRules.cpp @@ -91,7 +91,7 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule( ExecutionNode* current = n->getFirstParent(); while (current != nullptr) { bool doRegularCheck = false; - + if (current->getType() == EN::REMOVE) { RemoveNode const* removeNode = ExecutionNode::castTo(current); if (removeNode->inVariable() == v) { @@ -145,6 +145,22 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule( attributes.emplace(it.attributePath[0]); } } + } else if (current->getType() == EN::INDEX) { + Condition const* condition = ExecutionNode::castTo(current)->condition(); + + if (condition != nullptr && condition->root() != nullptr) { + AstNode const* node = condition->root(); + vars.clear(); + current->getVariablesUsedHere(vars); + + if (vars.find(v) != vars.end()) { + if (!Ast::getReferencedAttributes(node, v, attributes)) { + stop = true; + break; + } + optimize = true; + } + } } else { // all other node types mandate a check doRegularCheck = true; @@ -170,12 +186,8 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule( // projections are currently limited (arbitrarily to 5 attributes) if (optimize && !stop && !attributes.empty() && attributes.size() <= 5) { - std::vector r; - for (auto& it : attributes) { - r.emplace_back(std::move(it)); - } // store projections in DocumentProducingNode - e->projections(std::move(r)); + e->projections(std::move(attributes)); if (n->getType() == ExecutionNode::INDEX) { // need to update _indexCoversProjections value in an IndexNode diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp index d7435cea6d..9255791118 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp @@ -288,7 +288,6 @@ bool RocksDBPrimaryIndexInIterator::nextCovering(DocumentCallback const& cb, siz } while (limit > 0) { - // TODO: prevent copying of the value into result, as we don't need it here! LocalDocumentId documentId = _index->lookupKey(_trx, StringRef(*_iterator)); if (documentId.isSet()) { cb(documentId, *_iterator); diff --git a/tests/js/server/aql/aql-optimizer-rule-reduce-extraction-to-projection-rocksdb.js b/tests/js/server/aql/aql-optimizer-rule-reduce-extraction-to-projection-rocksdb.js index 5ccf1e8517..38bf4ef0b2 100644 --- a/tests/js/server/aql/aql-optimizer-rule-reduce-extraction-to-projection-rocksdb.js +++ b/tests/js/server/aql/aql-optimizer-rule-reduce-extraction-to-projection-rocksdb.js @@ -228,15 +228,36 @@ function optimizerRuleTestSuite () { result = AQL_EXECUTE(query[0], { "@cn" : cn }); assertEqual(query[1], result.json); }); + }, + + testJoin : function () { + c.ensureIndex({ type: "skiplist", fields: ["value"] }); + + let queries = [ + "FOR doc1 IN @@cn FOR doc2 IN @@cn FILTER doc1.value == doc2._key RETURN [doc1._key, doc2._key]", + "FOR doc1 IN @@cn FOR doc2 IN @@cn FILTER doc1.value == doc2._key RETURN [doc1.value, doc2._key]", + ]; + + queries.forEach(function(query) { + let result = AQL_EXPLAIN(query, { "@cn" : cn }); + assertNotEqual(-1, result.plan.rules.indexOf(ruleName), query); + + let found = 0; + result.plan.nodes.filter(function(node) { + return node.type === 'IndexNode' || node.type === 'EnumerateCollectionNode'; + }).forEach(function(node) { + assertNotEqual([], node.projections); + ++found; + }); + + assertEqual(2, found); + }); } }; } -//////////////////////////////////////////////////////////////////////////////// /// @brief executes the test suite -//////////////////////////////////////////////////////////////////////////////// - jsunity.run(optimizerRuleTestSuite); return jsunity.done();