diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index cb138a5ef2..62c69f424c 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -121,10 +121,42 @@ int triagens::aql::sortInValuesRule (Optimizer* opt, // calculation. sorting the IN values will not provide a benefit here continue; } + + AstNode const* originalNode = static_cast(setter)->expression()->node(); + TRI_ASSERT(originalNode != nullptr); + + AstNode const* testNode = originalNode; + + if (originalNode->type == NODE_TYPE_FCALL && + static_cast(originalNode->getData())->externalName == "NOOPT") { + // bypass NOOPT(...) + TRI_ASSERT(originalNode->numMembers() == 1); + auto args = originalNode->getMember(0); + + if (args->numMembers() > 0) { + testNode = args->getMember(0); + } + } + + if (testNode->type == NODE_TYPE_VALUE || + testNode->type == NODE_TYPE_OBJECT) { + // not really usable... + continue; + } + + if (testNode->type == NODE_TYPE_ARRAY && + testNode->numMembers() < 8) { + continue; + } + + if (testNode->isSorted()) { + // already sorted + continue; + } auto ast = plan->getAst(); auto args = ast->createNodeArray(); - args->addMember(static_cast(setter)->expression()->node()); + args->addMember(originalNode); auto sorted = ast->createNodeFunctionCall("SORTED_UNIQUE", args); auto outVar = ast->variables()->createTemporaryVariable(); @@ -3831,7 +3863,7 @@ int triagens::aql::mergeFilterIntoTraversal (Optimizer* opt, std::vector&& tNodes = plan->findNodesOfType(EN::TRAVERSAL, true); - if (tNodes.size() == 0) { + if (tNodes.empty()) { opt->addPlan(plan, rule, false); return TRI_ERROR_NO_ERROR; diff --git a/js/server/tests/aql-optimizer-rule-sort-in-values.js b/js/server/tests/aql-optimizer-rule-sort-in-values.js index 4369eb110b..ddcafb95b1 100644 --- a/js/server/tests/aql-optimizer-rule-sort-in-values.js +++ b/js/server/tests/aql-optimizer-rule-sort-in-values.js @@ -70,7 +70,8 @@ function optimizerRuleTestSuite () { "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER i.a IN values RETURN i", "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER i.a NOT IN values RETURN i", "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER LENGTH(i.a) >= 3 FILTER i.a IN values RETURN i", - "LET values = NOOPT(RANGE(1, 100)) FOR i IN 1..100 FILTER i IN values RETURN i" + "LET values = NOOPT(RANGE(1, 100)) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) FOR i IN 1..100 FILTER i IN values RETURN i" ]; queries.forEach(function(query) { @@ -93,7 +94,12 @@ function optimizerRuleTestSuite () { "FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER i.a NOT IN SPLIT('foo,bar,foobar,qux', ',') RETURN i", "LET values = RANGE(1, 100) FOR i IN 1..100 FILTER i IN values RETURN i", "FOR i IN 1..100 FILTER i IN RANGE(1, 100) RETURN i", - "FOR i IN 1..100 FILTER i IN NOOPT(RANGE(1, 100)) RETURN i" + "FOR i IN 1..100 FILTER i IN NOOPT(RANGE(1, 100)) RETURN i", + "LET values = NOOPT([ 1 ]) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT([ 1, 2 ]) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT([ 1, 2, 3 ]) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT({ }) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT('foobar') FOR i IN 1..100 FILTER i IN values RETURN i" ]; queryList.forEach(function(query) { @@ -121,7 +127,8 @@ function optimizerRuleTestSuite () { "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER i.a IN values RETURN i", "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER i.a NOT IN values RETURN i", "LET values = NOOPT(SPLIT('foo,bar,foobar,qux', ',')) FOR i IN [ { a: 'foo' }, { a: 'bar' }, { a: 'baz' } ] FILTER LENGTH(i.a) >= 3 FILTER i.a IN values RETURN i", - "LET values = NOOPT(RANGE(1, 100)) FOR i IN 1..100 FILTER i IN values RETURN i" + "LET values = NOOPT(RANGE(1, 100)) FOR i IN 1..100 FILTER i IN values RETURN i", + "LET values = NOOPT([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) FOR i IN 1..100 FILTER i IN values RETURN i" ]; queries.forEach(function(query) {