diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index 7913f82966..1fc870f4b4 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -1425,6 +1425,38 @@ namespace triagens { std::vector> criteria; bool isValid = true; bool isComplex = false; + + bool isCoveredBy (SortInformation const& other) { + if (! isValid || ! other.isValid) { + return false; + } + + if (isComplex) { + return false; + } + + size_t const n = criteria.size(); + for (size_t i = 0; i < n; ++i) { + if (other.criteria.size() < i) { + return false; + } + + auto ours = criteria[i]; + auto theirs = other.criteria[i]; + + if (std::get<2>(ours) != std::get<2>(theirs)) { + // sort order is different + return false; + } + + if (std::get<1>(ours) != std::get<1>(theirs)) { + // sort criterion is different + return false; + } + } + + return true; + } }; // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index dd45558aa5..a16fb5e2db 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -59,22 +59,54 @@ int triagens::aql::removeRedundantSorts (Optimizer* opt, ExecutionPlan* plan, int level, Optimizer::PlanList& out) { - // should we enter subqueries?? std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::SORT, true); + std::unordered_set toUnlink; for (auto n : nodes) { auto sortInfo = static_cast(n)->getSortInformation(plan); - if (sortInfo.isValid) { - /* - TODO: finalize - std::cout << "FOUND SORT:\n"; - for (auto it = sortInfo.criteria.begin(); it != sortInfo.criteria.end(); ++it) { - std::cout << "- " << std::get<1>(*it) << ", " << std::get<2>(*it) << "\n"; + if (sortInfo.isValid && ! sortInfo.criteria.empty()) { + // we found a sort that we can understand + + std::vector stack; + for (auto dep : n->getDependencies()) { + stack.push_back(dep); + } + + while (! stack.empty()) { + auto current = stack.back(); + stack.pop_back(); + + if (current->getType() == triagens::aql::ExecutionNode::SORT) { + // we found another sort. now check if they are compatible! + auto other = static_cast(current)->getSortInformation(plan); + + if (sortInfo.isCoveredBy(other)) { + // the sort at the start of the pipeline makes the sort at the end + // superfluous, so we'll remove it + toUnlink.insert(n); + break; + } + } + + auto deps = current->getDependencies(); + if (deps.size() != 1) { + // node either has no or more than one dependency. we don't know what to do and must abort + // note: this will also handle Singleton nodes + break; + } + + for (auto dep : deps) { + stack.push_back(dep); + } } - */ } } + + if (! toUnlink.empty()) { + plan->unlinkNodes(toUnlink); + plan->findVarUsage(); + } out.push_back(plan, level);