diff --git a/arangod/ClusterEngine/ClusterIndex.cpp b/arangod/ClusterEngine/ClusterIndex.cpp index 0cd0830ae5..a54067e6cd 100644 --- a/arangod/ClusterEngine/ClusterIndex.cpp +++ b/arangod/ClusterEngine/ClusterIndex.cpp @@ -117,7 +117,7 @@ bool ClusterIndex::hasSelectivityEstimate() const { } /// @brief default implementation for selectivityEstimate -double ClusterIndex::selectivityEstimate(StringRef const* extra) const { +double ClusterIndex::selectivityEstimate(StringRef const&) const { TRI_ASSERT(hasSelectivityEstimate()); if (_unique) { return 1.0; @@ -198,6 +198,7 @@ bool ClusterIndex::matchesDefinition(VPackSlice const& info) const { } bool ClusterIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { @@ -216,7 +217,7 @@ bool ClusterIndex::supportsFilterCondition( #endif case TRI_IDX_TYPE_NO_ACCESS_INDEX: { // should not be called for these indexes - return Index::supportsFilterCondition(node, reference, itemsInIndex, + return Index::supportsFilterCondition(allIndexes, node, reference, itemsInIndex, estimatedItems, estimatedCost); } case TRI_IDX_TYPE_HASH_INDEX:{ @@ -225,7 +226,7 @@ bool ClusterIndex::supportsFilterCondition( return matcher.matchAll(this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } else if (_engineType == ClusterEngineType::RocksDBEngine) { - return PersistentIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, + return PersistentIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } break; @@ -239,17 +240,17 @@ bool ClusterIndex::supportsFilterCondition( case TRI_IDX_TYPE_SKIPLIST_INDEX: { if (_engineType == ClusterEngineType::MMFilesEngine) { - return SkiplistIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, + return SkiplistIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } else if (_engineType == ClusterEngineType::RocksDBEngine) { - return PersistentIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, + return PersistentIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } break; } case TRI_IDX_TYPE_PERSISTENT_INDEX: { // same for both engines - return PersistentIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, + return PersistentIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } diff --git a/arangod/ClusterEngine/ClusterIndex.h b/arangod/ClusterEngine/ClusterIndex.h index bd9a63d20b..7c81861295 100644 --- a/arangod/ClusterEngine/ClusterIndex.h +++ b/arangod/ClusterEngine/ClusterIndex.h @@ -24,6 +24,7 @@ #define ARANGOD_CLUSTER_ENGINE_CLUSTER_INDEX_H 1 #include "Basics/Common.h" +#include "Basics/StringRef.h" #include "ClusterEngine/ClusterTransactionState.h" #include "ClusterEngine/Common.h" #include "Indexes/Index.h" @@ -66,7 +67,7 @@ class ClusterIndex : public Index { bool hasSelectivityEstimate() const override; - double selectivityEstimate(arangodb::StringRef const* = nullptr) const override; + double selectivityEstimate(arangodb::StringRef const& = arangodb::StringRef()) const override; /// @brief update the cluster selectivity estimate void updateClusterSelectivityEstimate(double estimate) override; @@ -83,7 +84,8 @@ class ClusterIndex : public Index { /// @brief Checks if this index is identical to the given definition bool matchesDefinition(arangodb::velocypack::Slice const&) const override; - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/Indexes/Index.cpp b/arangod/Indexes/Index.cpp index e7ff4f6b1d..bef949cc53 100644 --- a/arangod/Indexes/Index.cpp +++ b/arangod/Indexes/Index.cpp @@ -614,7 +614,7 @@ bool Index::matchesDefinition(VPackSlice const& info) const { } /// @brief default implementation for selectivityEstimate -double Index::selectivityEstimate(StringRef const* extra) const { +double Index::selectivityEstimate(StringRef const&) const { if (_unique) { return 1.0; } @@ -657,7 +657,8 @@ int Index::sizeHint(transaction::Methods*, size_t) { bool Index::hasBatchInsert() const { return false; } /// @brief default implementation for supportsFilterCondition -bool Index::supportsFilterCondition(arangodb::aql::AstNode const*, +bool Index::supportsFilterCondition(std::vector> const&, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/Indexes/Index.h b/arangod/Indexes/Index.h index b15fc15823..bcc1bdb8f4 100644 --- a/arangod/Indexes/Index.h +++ b/arangod/Indexes/Index.h @@ -28,6 +28,7 @@ #include "Basics/AttributeNameParser.h" #include "Basics/Exceptions.h" #include "Basics/Result.h" +#include "Basics/StringRef.h" #include "VocBase/LocalDocumentId.h" #include "VocBase/voc-types.h" #include "VocBase/vocbase.h" @@ -42,7 +43,6 @@ class LocalTaskQueue; class IndexIterator; class LogicalCollection; class ManagedDocumentResult; -class StringRef; struct IndexIteratorOptions; namespace velocypack { @@ -252,7 +252,7 @@ class Index { /// The extra StringRef is only used in the edge index as direction /// attribute attribute, a Slice would be more flexible. virtual double selectivityEstimate( - arangodb::StringRef const* extra = nullptr) const; + arangodb::StringRef const& extra = arangodb::StringRef()) const; /// @brief update the cluster selectivity estimate virtual void updateClusterSelectivityEstimate(double /*estimate*/) { @@ -330,7 +330,8 @@ class Index { virtual bool hasBatchInsert() const; - virtual bool supportsFilterCondition(arangodb::aql::AstNode const*, + virtual bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const; diff --git a/arangod/Indexes/PersistentIndexAttributeMatcher.cpp b/arangod/Indexes/PersistentIndexAttributeMatcher.cpp index 35b53dcb59..d11b7d857f 100644 --- a/arangod/Indexes/PersistentIndexAttributeMatcher.cpp +++ b/arangod/Indexes/PersistentIndexAttributeMatcher.cpp @@ -59,6 +59,7 @@ void PersistentIndexAttributeMatcher::matchAttributes( } bool PersistentIndexAttributeMatcher::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::Index const* idx, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) { @@ -68,128 +69,9 @@ bool PersistentIndexAttributeMatcher::supportsFilterCondition( THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } } - - std::unordered_map> found; - std::unordered_set nonNullAttributes; - size_t values = 0; - matchAttributes(idx, node, reference, found, values, nonNullAttributes, - false); - - bool lastContainsEquality = true; - size_t attributesCovered = 0; - size_t attributesCoveredByEquality = 0; - double equalityReductionFactor = 20.0; - estimatedCost = static_cast(itemsInIndex); - - for (size_t i = 0; i < idx->fields().size(); ++i) { - auto it = found.find(i); - - if (it == found.end()) { - // index attribute not covered by condition - break; - } - - // check if the current condition contains an equality condition - auto const& nodes = (*it).second; - bool containsEquality = false; - for (size_t j = 0; j < nodes.size(); ++j) { - if (nodes[j]->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ || - nodes[j]->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN) { - containsEquality = true; - break; - } - } - - if (!lastContainsEquality) { - // unsupported condition. must abort - break; - } - - ++attributesCovered; - if (containsEquality) { - ++attributesCoveredByEquality; - estimatedCost /= equalityReductionFactor; - - // decrease the effect of the equality reduction factor - equalityReductionFactor *= 0.25; - if (equalityReductionFactor < 2.0) { - // equalityReductionFactor shouldn't get too low - equalityReductionFactor = 2.0; - } - } else { - // quick estimate for the potential reductions caused by the conditions - if (nodes.size() >= 2) { - // at least two (non-equality) conditions. probably a range with lower - // and upper bound defined - estimatedCost /= 7.5; - } else { - // one (non-equality). this is either a lower or a higher bound - estimatedCost /= 2.0; - } - } - - lastContainsEquality = containsEquality; - } - - if (values == 0) { - values = 1; - } - - if (attributesCoveredByEquality == idx->fields().size() && idx->unique()) { - // index is unique and condition covers all attributes by equality - if (itemsInIndex == 0) { - estimatedItems = 0; - estimatedCost = 0.0; - return true; - } - - estimatedItems = values; - estimatedCost = static_cast(estimatedItems * values); - // cost is already low... now slightly prioritize unique indexes - estimatedCost *= 0.995 - 0.05 * (idx->fields().size() - 1); - return true; - } - - if (attributesCovered > 0 && - (!idx->sparse() || attributesCovered == idx->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))); - - // check if the index has a selectivity estimate ready - if (idx->hasSelectivityEstimate() && - attributesCoveredByEquality == idx->fields().size()) { - StringRef ignore; - double estimate = idx->selectivityEstimate(&ignore); - if (estimate > 0.0) { - estimatedItems = static_cast(1.0 / estimate); - } - } - if (itemsInIndex == 0) { - estimatedCost = 0.0; - } else { - // simon: neither RocksDBVPackIndex nor MMFilesPersistentIndex have caches - /*if (useCache()) { - estimatedCost = static_cast(estimatedItems * values) - - (_fields.size() - 1) * 0.01; - } else {*/ - estimatedCost = - (std::max)(static_cast(1), - std::log2(static_cast(itemsInIndex)) * values) - - (idx->fields().size() - 1) * 0.01; - //} - } - return true; - } - - // index does not help for this condition - estimatedItems = itemsInIndex; - estimatedCost = static_cast(estimatedItems); - return false; + + // just forward to reference implementation + return SkiplistIndexAttributeMatcher::supportsFilterCondition(allIndexes, idx, node, reference, itemsInIndex, estimatedItems, estimatedCost); } bool PersistentIndexAttributeMatcher::supportsSortCondition( diff --git a/arangod/Indexes/PersistentIndexAttributeMatcher.h b/arangod/Indexes/PersistentIndexAttributeMatcher.h index 0d7ec96ec5..cfeab29443 100644 --- a/arangod/Indexes/PersistentIndexAttributeMatcher.h +++ b/arangod/Indexes/PersistentIndexAttributeMatcher.h @@ -40,7 +40,8 @@ class Index; /// rocksdb based storage namespace PersistentIndexAttributeMatcher { -bool supportsFilterCondition(arangodb::Index const*, +bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::Index const*, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, diff --git a/arangod/Indexes/SimpleAttributeEqualityMatcher.cpp b/arangod/Indexes/SimpleAttributeEqualityMatcher.cpp index 34a8462aa8..0bffb242ca 100644 --- a/arangod/Indexes/SimpleAttributeEqualityMatcher.cpp +++ b/arangod/Indexes/SimpleAttributeEqualityMatcher.cpp @@ -323,7 +323,7 @@ void SimpleAttributeEqualityMatcher::calculateIndexCosts( if (attribute != nullptr && attribute->type == aql::NODE_TYPE_ATTRIBUTE_ACCESS) { att = StringRef(attribute->getStringValue(), attribute->getStringLength()); } - double estimate = index->selectivityEstimate(&att); + double estimate = index->selectivityEstimate(att); if (estimate <= 0.0) { // prevent division by zero estimatedItems = itemsInIndex; diff --git a/arangod/Indexes/SkiplistIndexAttributeMatcher.cpp b/arangod/Indexes/SkiplistIndexAttributeMatcher.cpp index fdca32eba7..2783d9505f 100644 --- a/arangod/Indexes/SkiplistIndexAttributeMatcher.cpp +++ b/arangod/Indexes/SkiplistIndexAttributeMatcher.cpp @@ -160,11 +160,16 @@ void SkiplistIndexAttributeMatcher::matchAttributes( case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN: if (accessFitsIndex(idx, op->getMember(0), op->getMember(1), op, reference, found, nonNullAttributes, isExecution)) { - size_t av = SimpleAttributeEqualityMatcher::estimateNumberOfArrayMembers(op->getMember(1)); - if (av > 1) { - // attr IN [ a, b, c ] => this will produce multiple items, so - // count them! - values += av - 1; + if (op->getMember(1)->isAttributeAccessForVariable(reference, false)) { + // 'abc' IN doc.attr[*] + ++values; + } else { + size_t av = SimpleAttributeEqualityMatcher::estimateNumberOfArrayMembers(op->getMember(1)); + if (av > 1) { + // attr IN [ a, b, c ] => this will produce multiple items, so + // count them! + values += av - 1; + } } } break; @@ -176,6 +181,7 @@ void SkiplistIndexAttributeMatcher::matchAttributes( } bool SkiplistIndexAttributeMatcher::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::Index const* idx, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) { @@ -254,12 +260,10 @@ bool SkiplistIndexAttributeMatcher::supportsFilterCondition( return true; } - if (estimatedItems >= values) { - TRI_ASSERT(itemsInIndex > 0); - - estimatedItems = values; - estimatedCost = (std::max)(static_cast(1), std::log2(static_cast(itemsInIndex)) * values); - } + estimatedItems = values; + // ALTERNATIVE: estimatedCost = static_cast(estimatedItems * values); + estimatedCost = (std::max)(static_cast(1), std::log2(static_cast(itemsInIndex)) * values); + // cost is already low... now slightly prioritize unique indexes estimatedCost *= 0.995 - 0.05 * (idx->fields().size() - 1); return true; @@ -272,11 +276,66 @@ bool SkiplistIndexAttributeMatcher::supportsFilterCondition( // 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))); + static_cast(estimatedCost * values), static_cast(1))); + + // check if the index has a selectivity estimate ready + if (idx->hasSelectivityEstimate() && + attributesCoveredByEquality == idx->fields().size()) { + double estimate = idx->selectivityEstimate(); + if (estimate > 0.0) { + estimatedItems = static_cast(1.0 / estimate); + } + } else if (attributesCoveredByEquality > 0) { + TRI_ASSERT(attributesCovered > 0); + // the index either does not have a selectivity estimate, or not all + // of its attributes are covered by the condition using an equality lookup + // however, if the search condition uses equality lookups on the prefix + // of the index, then we can check if there is another index which is just + // indexing the prefix, and "steal" the selectivity estimate from that index + // for example, if the condition is "doc.a == 1 && doc.b > 2", and the current + // index is created on ["a", "b"], then we will not use the selectivity + // estimate of the current index (due to the range condition used for the second + // index attribute). however, if there is another index on just "a", we know + // that the current index is at least as selective as the index on the single + // attribute. and that the extra condition we have will make it even more + // selectivity. so in this case we will re-use the selectivity estimate from + // the other index, and are happy. + for (auto const& otherIdx : allIndexes) { + auto const* other = otherIdx.get(); + if (other == idx || !other->hasSelectivityEstimate()) { + continue; + } + auto const& otherFields = other->fields(); + if (otherFields.size() >= attributesCovered) { + // other index has more fields than we have, or the same amount. + // then it will not be helpful + continue; + } + size_t matches = 0; + for (size_t i = 0; i < otherFields.size(); ++i) { + if (otherFields[i] != idx->fields()[i]) { + break; + } + ++matches; + } + if (matches == otherFields.size()) { + double estimate = other->selectivityEstimate(); + if (estimate > 0.0) { + // reuse the estimate from the other index + estimatedItems = static_cast(1.0 / estimate); + break; + } + } + } + } + if (itemsInIndex == 0) { estimatedCost = 0.0; } else { + // lookup cost is O(log(n)) estimatedCost = (std::max)(static_cast(1), std::log2(static_cast(itemsInIndex)) * values); + // slightly prefer indexes that cover more attributes + estimatedCost -= (idx->fields().size() - 1) * 0.01; } return true; } diff --git a/arangod/Indexes/SkiplistIndexAttributeMatcher.h b/arangod/Indexes/SkiplistIndexAttributeMatcher.h index b5f1395ec6..642dd89d92 100644 --- a/arangod/Indexes/SkiplistIndexAttributeMatcher.h +++ b/arangod/Indexes/SkiplistIndexAttributeMatcher.h @@ -38,7 +38,8 @@ class Index; /// Contains code for in-memory skiplist indexes (MMFilesSkiplistIndex) namespace SkiplistIndexAttributeMatcher { -bool supportsFilterCondition(arangodb::Index const*, +bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::Index const* index, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, diff --git a/arangod/MMFiles/MMFilesEdgeIndex.cpp b/arangod/MMFiles/MMFilesEdgeIndex.cpp index 338810f645..107e349c8c 100644 --- a/arangod/MMFiles/MMFilesEdgeIndex.cpp +++ b/arangod/MMFiles/MMFilesEdgeIndex.cpp @@ -206,7 +206,7 @@ MMFilesEdgeIndex::MMFilesEdgeIndex( /// @brief return a selectivity estimate for the index double MMFilesEdgeIndex::selectivityEstimate( - arangodb::StringRef const* attribute) const { + arangodb::StringRef const& attribute) const { TRI_ASSERT(!ServerState::instance()->isCoordinator()); if (_unique) { return 1.0; @@ -216,14 +216,14 @@ double MMFilesEdgeIndex::selectivityEstimate( return 0.1; } - if (attribute != nullptr) { + if (!attribute.empty()) { // the index attribute is given here // now check if we can restrict the selectivity estimation to the correct // part of the index - if (attribute->compare(StaticStrings::FromString) == 0) { + if (attribute.compare(StaticStrings::FromString) == 0) { // _from return _edgesFrom->selectivity(); - } else if (attribute->compare(StaticStrings::ToString) == 0) { + } else if (attribute.compare(StaticStrings::ToString) == 0) { // _to return _edgesTo->selectivity(); } @@ -408,6 +408,7 @@ int MMFilesEdgeIndex::sizeHint(transaction::Methods* trx, size_t size) { /// @brief checks whether the index supports the condition bool MMFilesEdgeIndex::supportsFilterCondition( + std::vector> const&, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/MMFiles/MMFilesEdgeIndex.h b/arangod/MMFiles/MMFilesEdgeIndex.h index 775be718d9..65546e844f 100644 --- a/arangod/MMFiles/MMFilesEdgeIndex.h +++ b/arangod/MMFiles/MMFilesEdgeIndex.h @@ -158,7 +158,7 @@ class MMFilesEdgeIndex final : public MMFilesIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(arangodb::StringRef const* = nullptr) const override; + double selectivityEstimate(arangodb::StringRef const& = arangodb::StringRef()) const override; size_t memory() const override; @@ -184,11 +184,12 @@ class MMFilesEdgeIndex final : public MMFilesIndex { bool hasBatchInsert() const override { return true; } - TRI_MMFilesEdgeIndexHash_t* from() { return _edgesFrom.get(); } + TRI_MMFilesEdgeIndexHash_t* from() const { return _edgesFrom.get(); } - TRI_MMFilesEdgeIndexHash_t* to() { return _edgesTo.get(); } + TRI_MMFilesEdgeIndexHash_t* to() const { return _edgesTo.get(); } - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/MMFiles/MMFilesHashIndex.cpp b/arangod/MMFiles/MMFilesHashIndex.cpp index 507683a7c4..12e69c7480 100644 --- a/arangod/MMFiles/MMFilesHashIndex.cpp +++ b/arangod/MMFiles/MMFilesHashIndex.cpp @@ -352,7 +352,7 @@ MMFilesHashIndex::~MMFilesHashIndex() { } /// @brief returns a selectivity estimate for the index -double MMFilesHashIndex::selectivityEstimate(StringRef const*) const { +double MMFilesHashIndex::selectivityEstimate(StringRef const&) const { TRI_ASSERT(!ServerState::instance()->isCoordinator()); if (_unique) { return 1.0; @@ -871,6 +871,7 @@ int MMFilesHashIndex::removeMultiElement(transaction::Methods* trx, /// @brief checks whether the index supports the condition bool MMFilesHashIndex::supportsFilterCondition( + std::vector> const&, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/MMFiles/MMFilesHashIndex.h b/arangod/MMFiles/MMFilesHashIndex.h index b03312b4e4..c40aaaefb3 100644 --- a/arangod/MMFiles/MMFilesHashIndex.h +++ b/arangod/MMFiles/MMFilesHashIndex.h @@ -253,7 +253,7 @@ class MMFilesHashIndex final : public MMFilesPathBasedIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(arangodb::StringRef const* = nullptr) const override; + double selectivityEstimate(arangodb::StringRef const& = arangodb::StringRef()) const override; size_t memory() const override; @@ -280,7 +280,8 @@ class MMFilesHashIndex final : public MMFilesPathBasedIndex { bool hasBatchInsert() const override { return true; } - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/MMFiles/MMFilesPersistentIndex.cpp b/arangod/MMFiles/MMFilesPersistentIndex.cpp index 8cea738700..9fa2a9555c 100644 --- a/arangod/MMFiles/MMFilesPersistentIndex.cpp +++ b/arangod/MMFiles/MMFilesPersistentIndex.cpp @@ -683,10 +683,11 @@ MMFilesPersistentIndexIterator* MMFilesPersistentIndex::lookup( } bool MMFilesPersistentIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { - return PersistentIndexAttributeMatcher::supportsFilterCondition(this, node, reference, + return PersistentIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } diff --git a/arangod/MMFiles/MMFilesPersistentIndex.h b/arangod/MMFiles/MMFilesPersistentIndex.h index 431d4d062b..dd8b9346a1 100644 --- a/arangod/MMFiles/MMFilesPersistentIndex.h +++ b/arangod/MMFiles/MMFilesPersistentIndex.h @@ -181,7 +181,8 @@ class MMFilesPersistentIndex final : public MMFilesPathBasedIndex { arangodb::velocypack::Slice const, bool reverse) const; - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/MMFiles/MMFilesPrimaryIndex.cpp b/arangod/MMFiles/MMFilesPrimaryIndex.cpp index e739a559e8..595b50b291 100644 --- a/arangod/MMFiles/MMFilesPrimaryIndex.cpp +++ b/arangod/MMFiles/MMFilesPrimaryIndex.cpp @@ -450,6 +450,7 @@ void MMFilesPrimaryIndex::invokeOnAllElementsForRemoval( /// @brief checks whether the index supports the condition bool MMFilesPrimaryIndex::supportsFilterCondition( + std::vector> const&, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/MMFiles/MMFilesPrimaryIndex.h b/arangod/MMFiles/MMFilesPrimaryIndex.h index ed275ff954..19dd327e4d 100644 --- a/arangod/MMFiles/MMFilesPrimaryIndex.h +++ b/arangod/MMFiles/MMFilesPrimaryIndex.h @@ -184,7 +184,7 @@ class MMFilesPrimaryIndex final : public MMFilesIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(StringRef const* = nullptr) const override { + double selectivityEstimate(StringRef const& = StringRef()) const override { return 1.0; } @@ -261,7 +261,8 @@ class MMFilesPrimaryIndex final : public MMFilesIndex { void invokeOnAllElementsForRemoval( std::function); - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/MMFiles/MMFilesSkiplistIndex.cpp b/arangod/MMFiles/MMFilesSkiplistIndex.cpp index 2b4bbc3676..c112599805 100644 --- a/arangod/MMFiles/MMFilesSkiplistIndex.cpp +++ b/arangod/MMFiles/MMFilesSkiplistIndex.cpp @@ -1234,10 +1234,11 @@ IndexIterator* MMFilesSkiplistIndex::iteratorForCondition( } bool MMFilesSkiplistIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { - return SkiplistIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, + return SkiplistIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex, estimatedItems, estimatedCost); } diff --git a/arangod/MMFiles/MMFilesSkiplistIndex.h b/arangod/MMFiles/MMFilesSkiplistIndex.h index a0aee649b5..dd66d8627e 100644 --- a/arangod/MMFiles/MMFilesSkiplistIndex.h +++ b/arangod/MMFiles/MMFilesSkiplistIndex.h @@ -299,7 +299,8 @@ class MMFilesSkiplistIndex final : public MMFilesPathBasedIndex { void unload() override; - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/RocksDBEngine/RocksDBEdgeIndex.cpp b/arangod/RocksDBEngine/RocksDBEdgeIndex.cpp index e5cf2194c4..4281335209 100644 --- a/arangod/RocksDBEngine/RocksDBEdgeIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBEdgeIndex.cpp @@ -545,12 +545,12 @@ RocksDBEdgeIndex::~RocksDBEdgeIndex() {} /// @brief return a selectivity estimate for the index double RocksDBEdgeIndex::selectivityEstimate( - arangodb::StringRef const* attribute) const { + arangodb::StringRef const& attribute) const { TRI_ASSERT(!ServerState::instance()->isCoordinator()); if (_unique) { return 1.0; } - if (attribute != nullptr && attribute->compare(_directionAttr)) { + if (!attribute.empty() && attribute.compare(_directionAttr)) { return 0.0; } TRI_ASSERT(_estimator != nullptr); @@ -674,6 +674,7 @@ void RocksDBEdgeIndex::batchInsert( /// @brief checks whether the index supports the condition bool RocksDBEdgeIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/RocksDBEngine/RocksDBEdgeIndex.h b/arangod/RocksDBEngine/RocksDBEdgeIndex.h index b2aa923a08..8ecbd898a1 100644 --- a/arangod/RocksDBEngine/RocksDBEdgeIndex.h +++ b/arangod/RocksDBEngine/RocksDBEdgeIndex.h @@ -26,6 +26,7 @@ #include "Basics/Common.h" #include "Basics/LocalTaskQueue.h" +#include "Basics/StringRef.h" #include "Indexes/Index.h" #include "Indexes/IndexIterator.h" #include "RocksDBEngine/RocksDBCuckooIndexEstimator.h" @@ -137,7 +138,7 @@ class RocksDBEdgeIndex final : public RocksDBIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(arangodb::StringRef const* = nullptr) const override; + double selectivityEstimate(arangodb::StringRef const& = arangodb::StringRef()) const override; RocksDBCuckooIndexEstimator* estimator() override; bool needToPersistEstimate() const override; @@ -152,7 +153,8 @@ class RocksDBEdgeIndex final : public RocksDBIndex { bool hasBatchInsert() const override { return false; } - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp index 0c4099affa..b9bf3c605f 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp @@ -354,6 +354,7 @@ Result RocksDBPrimaryIndex::removeInternal(transaction::Methods* trx, /// @brief checks whether the index supports the condition bool RocksDBPrimaryIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h index 7a416d7295..04d262ecf7 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h @@ -24,6 +24,7 @@ #ifndef ARANGOD_ROCKSDB_ENGINE_ROCKSDB_PRIMARY_INDEX_H #define ARANGOD_ROCKSDB_ENGINE_ROCKSDB_PRIMARY_INDEX_H 1 +#include "Basics/StringRef.h" #include "Indexes/Index.h" #include "Indexes/IndexIterator.h" #include "RocksDBEngine/RocksDBIndex.h" @@ -102,7 +103,7 @@ class RocksDBPrimaryIndex final : public RocksDBIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(StringRef const* = nullptr) const override { + double selectivityEstimate(StringRef const& = StringRef()) const override { return 1.0; } @@ -127,7 +128,8 @@ class RocksDBPrimaryIndex final : public RocksDBIndex { LocalDocumentId& id, TRI_voc_rid_t& revisionId) const; - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp index 957a2ca7a5..211a62346f 100644 --- a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp @@ -299,7 +299,7 @@ RocksDBVPackIndex::RocksDBVPackIndex( RocksDBVPackIndex::~RocksDBVPackIndex() {} double RocksDBVPackIndex::selectivityEstimate( - arangodb::StringRef const*) const { + arangodb::StringRef const&) const { TRI_ASSERT(!ServerState::instance()->isCoordinator()); if (_unique) { return 1.0; @@ -927,11 +927,13 @@ IndexIterator* RocksDBVPackIndex::lookup(transaction::Methods* trx, } bool RocksDBVPackIndex::supportsFilterCondition( + std::vector> const& allIndexes, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex, size_t& estimatedItems, double& estimatedCost) const { - return PersistentIndexAttributeMatcher::supportsFilterCondition(this, node, reference, itemsInIndex, - estimatedItems, estimatedCost); + return PersistentIndexAttributeMatcher::supportsFilterCondition( + allIndexes, this, node, reference, itemsInIndex, + estimatedItems, estimatedCost); } bool RocksDBVPackIndex::supportsSortCondition( diff --git a/arangod/RocksDBEngine/RocksDBVPackIndex.h b/arangod/RocksDBEngine/RocksDBVPackIndex.h index e9fdb3b27c..e67ef107a2 100644 --- a/arangod/RocksDBEngine/RocksDBVPackIndex.h +++ b/arangod/RocksDBEngine/RocksDBVPackIndex.h @@ -28,6 +28,7 @@ #include "Aql/AstNode.h" #include "Basics/Common.h" +#include "Basics/StringRef.h" #include "Indexes/IndexIterator.h" #include "RocksDBEngine/RocksDBCuckooIndexEstimator.h" #include "RocksDBEngine/RocksDBIndex.h" @@ -152,7 +153,7 @@ class RocksDBVPackIndex : public RocksDBIndex { bool hasSelectivityEstimate() const override { return true; } - double selectivityEstimate(arangodb::StringRef const* = nullptr) const override; + double selectivityEstimate(arangodb::StringRef const& = arangodb::StringRef()) const override; RocksDBCuckooIndexEstimator* estimator() override; bool needToPersistEstimate() const override; @@ -182,7 +183,8 @@ class RocksDBVPackIndex : public RocksDBIndex { IndexIterator* lookup(transaction::Methods*, arangodb::velocypack::Slice const, bool reverse) const; - bool supportsFilterCondition(arangodb::aql::AstNode const*, + bool supportsFilterCondition(std::vector> const& allIndexes, + arangodb::aql::AstNode const*, arangodb::aql::Variable const*, size_t, size_t&, double&) const override; diff --git a/arangod/Transaction/Methods.cpp b/arangod/Transaction/Methods.cpp index eebd0bb4d6..356a72f069 100644 --- a/arangod/Transaction/Methods.cpp +++ b/arangod/Transaction/Methods.cpp @@ -573,7 +573,7 @@ std::pair transaction::Methods::findIndexHandleForAndNode( // check if the index supports the filter expression double estimatedCost; size_t estimatedItems; - if (idx->supportsFilterCondition(node, reference, itemsInIndex, + if (idx->supportsFilterCondition(indexes, node, reference, itemsInIndex, estimatedItems, estimatedCost)) { // index supports the filter condition filterCost = estimatedCost; @@ -661,7 +661,7 @@ bool transaction::Methods::findIndexHandleForAndNode( // check if the index supports the filter expression double estimatedCost; size_t estimatedItems; - bool supportsFilter = idx->supportsFilterCondition(node, reference, itemsInIndex, + bool supportsFilter = idx->supportsFilterCondition(indexes, node, reference, itemsInIndex, estimatedItems, estimatedCost); // enable the following line to see index candidates considered with their @@ -3001,7 +3001,7 @@ bool transaction::Methods::supportsFilterCondition( "The index id cannot be empty."); } - return idx->supportsFilterCondition(condition, reference, itemsInIndex, + return idx->supportsFilterCondition(std::vector>(), condition, reference, itemsInIndex, estimatedItems, estimatedCost); } diff --git a/tests/IResearch/StorageEngineMock.cpp b/tests/IResearch/StorageEngineMock.cpp index 0b0dfa310a..117063fcbd 100644 --- a/tests/IResearch/StorageEngineMock.cpp +++ b/tests/IResearch/StorageEngineMock.cpp @@ -263,6 +263,7 @@ class EdgeIndexMock final : public arangodb::Index { } bool supportsFilterCondition( + std::vector> const& /*allIndexes*/, arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference, size_t itemsInIndex,