mirror of https://gitee.com/bigwinds/arangodb
don't unfairly favor the edge index when comparing it to more specialized indexes on `_from`/`_to` plus other attributes (#9362)
This commit is contained in:
parent
671380b8fb
commit
fe19b8aaae
|
@ -226,34 +226,19 @@ Index::FilterCosts ClusterIndex::supportsFilterCondition(
|
||||||
switch (_indexType) {
|
switch (_indexType) {
|
||||||
case TRI_IDX_TYPE_PRIMARY_INDEX: {
|
case TRI_IDX_TYPE_PRIMARY_INDEX: {
|
||||||
if (_engineType == ClusterEngineType::RocksDBEngine) {
|
if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
return SortedIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex);
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
|
||||||
std::size_t values = 0;
|
|
||||||
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
|
||||||
values, nonNullAttributes,
|
|
||||||
/*skip evaluation (during execution)*/ false);
|
|
||||||
|
|
||||||
Index::FilterCosts costs = Index::FilterCosts::defaultCosts(itemsInIndex);
|
|
||||||
if (!found.empty()) {
|
|
||||||
costs.supportsCondition = true;
|
|
||||||
costs.coveredAttributes = found.size();
|
|
||||||
costs.estimatedItems = values;
|
|
||||||
costs.estimatedCosts = static_cast<double>(values);
|
|
||||||
}
|
|
||||||
return costs;
|
|
||||||
}
|
}
|
||||||
// MMFiles et al
|
// MMFiles et al
|
||||||
SimpleAttributeEqualityMatcher matcher(PrimaryIndexAttributes);
|
SimpleAttributeEqualityMatcher matcher(PrimaryIndexAttributes);
|
||||||
return matcher.matchOne(this, node, reference, itemsInIndex);
|
return matcher.matchOne(this, node, reference, itemsInIndex);
|
||||||
}
|
}
|
||||||
case TRI_IDX_TYPE_GEO_INDEX:
|
case TRI_IDX_TYPE_EDGE_INDEX: {
|
||||||
case TRI_IDX_TYPE_GEO1_INDEX:
|
if (_engineType == ClusterEngineType::RocksDBEngine) {
|
||||||
case TRI_IDX_TYPE_GEO2_INDEX:
|
return SortedIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex);
|
||||||
case TRI_IDX_TYPE_FULLTEXT_INDEX:
|
}
|
||||||
case TRI_IDX_TYPE_IRESEARCH_LINK:
|
// MMFiles et al
|
||||||
case TRI_IDX_TYPE_NO_ACCESS_INDEX: {
|
SimpleAttributeEqualityMatcher matcher(this->_fields);
|
||||||
// should not be called for these indexes
|
return matcher.matchOne(this, node, reference, itemsInIndex);
|
||||||
return Index::supportsFilterCondition(allIndexes, node, reference, itemsInIndex);
|
|
||||||
}
|
}
|
||||||
case TRI_IDX_TYPE_HASH_INDEX: {
|
case TRI_IDX_TYPE_HASH_INDEX: {
|
||||||
if (_engineType == ClusterEngineType::MMFilesEngine) {
|
if (_engineType == ClusterEngineType::MMFilesEngine) {
|
||||||
|
@ -265,11 +250,6 @@ Index::FilterCosts ClusterIndex::supportsFilterCondition(
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TRI_IDX_TYPE_EDGE_INDEX: {
|
|
||||||
// same for both engines
|
|
||||||
SimpleAttributeEqualityMatcher matcher(this->_fields);
|
|
||||||
return matcher.matchOne(this, node, reference, itemsInIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
case TRI_IDX_TYPE_SKIPLIST_INDEX:
|
case TRI_IDX_TYPE_SKIPLIST_INDEX:
|
||||||
case TRI_IDX_TYPE_TTL_INDEX:
|
case TRI_IDX_TYPE_TTL_INDEX:
|
||||||
|
@ -279,6 +259,16 @@ Index::FilterCosts ClusterIndex::supportsFilterCondition(
|
||||||
node, reference, itemsInIndex);
|
node, reference, itemsInIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TRI_IDX_TYPE_GEO_INDEX:
|
||||||
|
case TRI_IDX_TYPE_GEO1_INDEX:
|
||||||
|
case TRI_IDX_TYPE_GEO2_INDEX:
|
||||||
|
case TRI_IDX_TYPE_FULLTEXT_INDEX:
|
||||||
|
case TRI_IDX_TYPE_IRESEARCH_LINK:
|
||||||
|
case TRI_IDX_TYPE_NO_ACCESS_INDEX: {
|
||||||
|
// should not be called for these indexes
|
||||||
|
return Index::supportsFilterCondition(allIndexes, node, reference, itemsInIndex);
|
||||||
|
}
|
||||||
|
|
||||||
case TRI_IDX_TYPE_UNKNOWN:
|
case TRI_IDX_TYPE_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,12 @@ Index::FilterCosts SimpleAttributeEqualityMatcher::matchOne(arangodb::Index cons
|
||||||
arangodb::aql::AstNode const* node,
|
arangodb::aql::AstNode const* node,
|
||||||
arangodb::aql::Variable const* reference,
|
arangodb::aql::Variable const* reference,
|
||||||
size_t itemsInIndex) {
|
size_t itemsInIndex) {
|
||||||
|
size_t postFilterConditions = 0;
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
std::unordered_set<std::string> nonNullAttributes;
|
||||||
_found.clear();
|
_found.clear();
|
||||||
|
|
||||||
|
Index::FilterCosts costs = Index::FilterCosts::defaultCosts(itemsInIndex);
|
||||||
|
|
||||||
size_t const n = node->numMembers();
|
size_t const n = node->numMembers();
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
@ -75,13 +78,19 @@ Index::FilterCosts SimpleAttributeEqualityMatcher::matchOne(arangodb::Index cons
|
||||||
}
|
}
|
||||||
|
|
||||||
if (which != nullptr) {
|
if (which != nullptr) {
|
||||||
// we can use the index
|
// we can use the index for the condition
|
||||||
return calculateIndexCosts(index, which, itemsInIndex * values, 1);
|
costs = calculateIndexCosts(index, which, itemsInIndex * values, 1);
|
||||||
|
} else {
|
||||||
|
// we cannot use the index for the condition
|
||||||
|
++postFilterConditions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// honor the costs of post-index filter conditions
|
||||||
|
costs.estimatedCosts += costs.estimatedItems * postFilterConditions;
|
||||||
|
|
||||||
// set to defaults
|
// set to defaults
|
||||||
return Index::FilterCosts::defaultCosts(itemsInIndex);
|
return costs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief match all of the attributes, in any order
|
/// @brief match all of the attributes, in any order
|
||||||
|
@ -94,10 +103,12 @@ Index::FilterCosts SimpleAttributeEqualityMatcher::matchAll(arangodb::Index cons
|
||||||
_found.clear();
|
_found.clear();
|
||||||
arangodb::aql::AstNode const* which = nullptr;
|
arangodb::aql::AstNode const* which = nullptr;
|
||||||
|
|
||||||
|
size_t postFilterConditions = 0;
|
||||||
size_t values = 1;
|
size_t values = 1;
|
||||||
size_t const n = node->numMembers();
|
size_t const n = node->numMembers();
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
bool matches = false;
|
||||||
auto op = node->getMemberUnchecked(i);
|
auto op = node->getMemberUnchecked(i);
|
||||||
|
|
||||||
if (index->sparse() && (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE ||
|
if (index->sparse() && (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE ||
|
||||||
|
@ -114,26 +125,29 @@ Index::FilterCosts SimpleAttributeEqualityMatcher::matchAll(arangodb::Index cons
|
||||||
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||||
TRI_ASSERT(op->numMembers() == 2);
|
TRI_ASSERT(op->numMembers() == 2);
|
||||||
|
|
||||||
if (accessFitsIndex(index, op->getMember(0), op->getMember(1), op,
|
if (accessFitsIndex(index, op->getMemberUnchecked(0), op->getMemberUnchecked(1), op,
|
||||||
reference, nonNullAttributes, false)) {
|
reference, nonNullAttributes, false)) {
|
||||||
which = op->getMember(1);
|
which = op->getMemberUnchecked(1);
|
||||||
} else if (accessFitsIndex(index, op->getMember(1), op->getMember(0), op,
|
matches = true;
|
||||||
|
} else if (accessFitsIndex(index, op->getMemberUnchecked(1), op->getMemberUnchecked(0), op,
|
||||||
reference, nonNullAttributes, false)) {
|
reference, nonNullAttributes, false)) {
|
||||||
which = op->getMember(0);
|
which = op->getMemberUnchecked(0);
|
||||||
|
matches = true;
|
||||||
}
|
}
|
||||||
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||||
TRI_ASSERT(op->numMembers() == 2);
|
TRI_ASSERT(op->numMembers() == 2);
|
||||||
|
|
||||||
if (accessFitsIndex(index, op->getMember(0), op->getMember(1), op,
|
if (accessFitsIndex(index, op->getMemberUnchecked(0), op->getMemberUnchecked(1), op,
|
||||||
reference, nonNullAttributes, false)) {
|
reference, nonNullAttributes, false)) {
|
||||||
which = op->getMember(0);
|
which = op->getMemberUnchecked(0);
|
||||||
values *= estimateNumberOfArrayMembers(op->getMember(1));
|
values *= estimateNumberOfArrayMembers(op->getMember(1));
|
||||||
|
matches = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_found.size() == _attributes.size()) {
|
if (!matches) {
|
||||||
// got enough attributes
|
// we cannot use the index for this part of the condition
|
||||||
break;
|
++postFilterConditions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +171,9 @@ Index::FilterCosts SimpleAttributeEqualityMatcher::matchAll(arangodb::Index cons
|
||||||
costs = calculateIndexCosts(index, which, itemsInIndex * values, _found.size());
|
costs = calculateIndexCosts(index, which, itemsInIndex * values, _found.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// return defaults
|
// honor the costs of post-index filter conditions
|
||||||
|
costs.estimatedCosts += costs.estimatedItems * postFilterConditions;
|
||||||
|
|
||||||
return costs;
|
return costs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ bool SortedIndexAttributeMatcher::accessFitsIndex(
|
||||||
(!other->isConstant() || !(other->isIntValue() || other->isDoubleValue()))) {
|
(!other->isConstant() || !(other->isIntValue() || other->isDoubleValue()))) {
|
||||||
// TTL index can only be used for numeric lookup values, no date strings or
|
// TTL index can only be used for numeric lookup values, no date strings or
|
||||||
// anything else
|
// anything else
|
||||||
|
// TODO: move this into the specific index class
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,13 +142,15 @@ void SortedIndexAttributeMatcher::matchAttributes(
|
||||||
arangodb::Index const* idx, arangodb::aql::AstNode const* node,
|
arangodb::Index const* idx, arangodb::aql::AstNode const* node,
|
||||||
arangodb::aql::Variable const* reference,
|
arangodb::aql::Variable const* reference,
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
||||||
size_t& values, std::unordered_set<std::string>& nonNullAttributes, bool isExecution) {
|
size_t& postFilterConditions, size_t& values,
|
||||||
|
std::unordered_set<std::string>& nonNullAttributes, bool isExecution) {
|
||||||
// assert we have a proper formed conditiona - naray conjunction
|
// assert we have a proper formed conditiona - naray conjunction
|
||||||
TRI_ASSERT(node->type == arangodb::aql::NODE_TYPE_OPERATOR_NARY_AND);
|
TRI_ASSERT(node->type == arangodb::aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||||
|
|
||||||
// inspect the the conjuncts - allowed are binary comparisons and a contains check
|
// inspect the the conjuncts - allowed are binary comparisons and a contains check
|
||||||
for (size_t i = 0; i < node->numMembers(); ++i) {
|
for (size_t i = 0; i < node->numMembers(); ++i) {
|
||||||
auto op = node->getMember(i);
|
bool matches = false;
|
||||||
|
auto op = node->getMemberUnchecked(i);
|
||||||
|
|
||||||
switch (op->type) {
|
switch (op->type) {
|
||||||
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE:
|
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE:
|
||||||
|
@ -157,21 +160,22 @@ void SortedIndexAttributeMatcher::matchAttributes(
|
||||||
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT:
|
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT:
|
||||||
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE:
|
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE:
|
||||||
TRI_ASSERT(op->numMembers() == 2);
|
TRI_ASSERT(op->numMembers() == 2);
|
||||||
accessFitsIndex(idx, op->getMember(0), op->getMember(1), op, reference,
|
matches = accessFitsIndex(idx, op->getMemberUnchecked(0), op->getMemberUnchecked(1), op, reference,
|
||||||
found, nonNullAttributes, isExecution);
|
found, nonNullAttributes, isExecution);
|
||||||
accessFitsIndex(idx, op->getMember(1), op->getMember(0), op, reference,
|
matches |= accessFitsIndex(idx, op->getMemberUnchecked(1), op->getMemberUnchecked(0), op, reference,
|
||||||
found, nonNullAttributes, isExecution);
|
found, nonNullAttributes, isExecution);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN:
|
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN:
|
||||||
if (accessFitsIndex(idx, op->getMember(0), op->getMember(1), op,
|
if (accessFitsIndex(idx, op->getMemberUnchecked(0), op->getMemberUnchecked(1), op,
|
||||||
reference, found, nonNullAttributes, isExecution)) {
|
reference, found, nonNullAttributes, isExecution)) {
|
||||||
if (op->getMember(1)->isAttributeAccessForVariable(reference, /*indexed access*/ false)) {
|
matches = true;
|
||||||
|
if (op->getMemberUnchecked(1)->isAttributeAccessForVariable(reference, /*indexed access*/ false)) {
|
||||||
// 'abc' IN doc.attr[*]
|
// 'abc' IN doc.attr[*]
|
||||||
++values;
|
++values;
|
||||||
} else {
|
} else {
|
||||||
size_t av = SimpleAttributeEqualityMatcher::estimateNumberOfArrayMembers(
|
size_t av = SimpleAttributeEqualityMatcher::estimateNumberOfArrayMembers(
|
||||||
op->getMember(1));
|
op->getMemberUnchecked(1));
|
||||||
if (av > 1) {
|
if (av > 1) {
|
||||||
// attr IN [ a, b, c ] => this will produce multiple items, so
|
// attr IN [ a, b, c ] => this will produce multiple items, so
|
||||||
// count them!
|
// count them!
|
||||||
|
@ -182,8 +186,14 @@ void SortedIndexAttributeMatcher::matchAttributes(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
matches = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!matches) {
|
||||||
|
// count the number of conditions we will not be able to satisfy
|
||||||
|
++postFilterConditions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +211,8 @@ Index::FilterCosts SortedIndexAttributeMatcher::supportsFilterCondition(
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
std::unordered_set<std::string> nonNullAttributes;
|
||||||
size_t values = 0;
|
size_t values = 0;
|
||||||
matchAttributes(idx, node, reference, found, values, nonNullAttributes, false);
|
size_t postFilterConditions = 0;
|
||||||
|
matchAttributes(idx, node, reference, found, postFilterConditions, values, nonNullAttributes, false);
|
||||||
|
|
||||||
bool lastContainsEquality = true;
|
bool lastContainsEquality = true;
|
||||||
size_t attributesCovered = 0;
|
size_t attributesCovered = 0;
|
||||||
|
@ -212,11 +223,14 @@ Index::FilterCosts SortedIndexAttributeMatcher::supportsFilterCondition(
|
||||||
for (size_t i = 0; i < idx->fields().size(); ++i) {
|
for (size_t i = 0; i < idx->fields().size(); ++i) {
|
||||||
auto it = found.find(i);
|
auto it = found.find(i);
|
||||||
|
|
||||||
if (it == found.end()) {
|
if (it == found.end() || !lastContainsEquality) {
|
||||||
// index attribute not covered by condition
|
// index attribute not covered by condition, or unsupported condition.
|
||||||
|
// must abort
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++attributesCovered;
|
||||||
|
|
||||||
// check if the current condition contains an equality condition
|
// check if the current condition contains an equality condition
|
||||||
auto const& nodes = (*it).second;
|
auto const& nodes = (*it).second;
|
||||||
bool containsEquality = false;
|
bool containsEquality = false;
|
||||||
|
@ -228,12 +242,6 @@ Index::FilterCosts SortedIndexAttributeMatcher::supportsFilterCondition(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lastContainsEquality) {
|
|
||||||
// unsupported condition. must abort
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++attributesCovered;
|
|
||||||
if (containsEquality) {
|
if (containsEquality) {
|
||||||
++attributesCoveredByEquality;
|
++attributesCoveredByEquality;
|
||||||
estimatedCosts /= equalityReductionFactor;
|
estimatedCosts /= equalityReductionFactor;
|
||||||
|
@ -276,17 +284,12 @@ Index::FilterCosts SortedIndexAttributeMatcher::supportsFilterCondition(
|
||||||
costs.estimatedCosts = 0.0;
|
costs.estimatedCosts = 0.0;
|
||||||
} else {
|
} else {
|
||||||
costs.estimatedItems = values;
|
costs.estimatedItems = values;
|
||||||
// ALTERNATIVE: estimatedCost = static_cast<double>(estimatedItems * values);
|
|
||||||
costs.estimatedCosts = (std::max)(static_cast<double>(1),
|
costs.estimatedCosts = (std::max)(static_cast<double>(1),
|
||||||
std::log2(static_cast<double>(itemsInIndex)) * values);
|
std::log2(static_cast<double>(itemsInIndex)) * values);
|
||||||
|
}
|
||||||
// cost is already low... now slightly prioritize unique indexes
|
// cost is already low... now slightly prioritize unique indexes
|
||||||
costs.estimatedCosts *= 0.995 - 0.05 * (idx->fields().size() - 1);
|
costs.estimatedCosts *= 0.995 - 0.05 * (idx->fields().size() - 1);
|
||||||
}
|
} else if (attributesCovered > 0 &&
|
||||||
return costs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributesCovered > 0 &&
|
|
||||||
(!idx->sparse() || attributesCovered == idx->fields().size())) {
|
(!idx->sparse() || attributesCovered == idx->fields().size())) {
|
||||||
// if the condition contains at least one index attribute and is not sparse,
|
// 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,
|
// or the index is sparse and all attributes are covered by the condition,
|
||||||
|
@ -357,11 +360,14 @@ Index::FilterCosts SortedIndexAttributeMatcher::supportsFilterCondition(
|
||||||
// slightly prefer indexes that cover more attributes
|
// slightly prefer indexes that cover more attributes
|
||||||
costs.estimatedCosts -= (attributesCovered - 1) * 0.02;
|
costs.estimatedCosts -= (attributesCovered - 1) * 0.02;
|
||||||
}
|
}
|
||||||
return costs;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// index does not help for this condition
|
// index does not help for this condition
|
||||||
TRI_ASSERT(!costs.supportsCondition);
|
TRI_ASSERT(!costs.supportsCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// honor the costs of post-index filter conditions
|
||||||
|
costs.estimatedCosts += costs.estimatedItems * postFilterConditions;
|
||||||
|
|
||||||
return costs;
|
return costs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,8 +426,9 @@ arangodb::aql::AstNode* SortedIndexAttributeMatcher::specializeCondition(
|
||||||
|
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
std::unordered_set<std::string> nonNullAttributes;
|
||||||
size_t values = 0;
|
size_t values = 0; // ignored here
|
||||||
matchAttributes(idx, node, reference, found, values, nonNullAttributes, false);
|
size_t postFilterConditions = 0; // ignored here
|
||||||
|
matchAttributes(idx, node, reference, found, postFilterConditions, values, nonNullAttributes, false);
|
||||||
|
|
||||||
std::vector<arangodb::aql::AstNode const*> children;
|
std::vector<arangodb::aql::AstNode const*> children;
|
||||||
bool lastContainsEquality = true;
|
bool lastContainsEquality = true;
|
||||||
|
@ -429,13 +436,9 @@ arangodb::aql::AstNode* SortedIndexAttributeMatcher::specializeCondition(
|
||||||
for (size_t i = 0; i < idx->fields().size(); ++i) {
|
for (size_t i = 0; i < idx->fields().size(); ++i) {
|
||||||
auto it = found.find(i);
|
auto it = found.find(i);
|
||||||
|
|
||||||
if (it == found.end()) {
|
if (it == found.end() || !lastContainsEquality) {
|
||||||
// index attribute not covered by condition
|
// index attribute not covered by condition, or unsupported condition.
|
||||||
break;
|
// must abort
|
||||||
}
|
|
||||||
|
|
||||||
if (!lastContainsEquality) {
|
|
||||||
// unsupported condition. must abort
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,8 @@ arangodb::aql::AstNode* specializeCondition(arangodb::Index const* index,
|
||||||
void matchAttributes(arangodb::Index const* index, arangodb::aql::AstNode const* node,
|
void matchAttributes(arangodb::Index const* index, arangodb::aql::AstNode const* node,
|
||||||
arangodb::aql::Variable const* reference,
|
arangodb::aql::Variable const* reference,
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>>& found,
|
||||||
size_t& values, std::unordered_set<std::string>& nonNullAttributes,
|
size_t& postFilterConditions, size_t& values,
|
||||||
bool isExecution);
|
std::unordered_set<std::string>& nonNullAttributes, bool isExecution);
|
||||||
|
|
||||||
/// @brief whether or not the access fits
|
/// @brief whether or not the access fits
|
||||||
bool accessFitsIndex(
|
bool accessFitsIndex(
|
||||||
|
|
|
@ -709,7 +709,7 @@ std::unique_ptr<IndexIterator> MMFilesPersistentIndex::iteratorForCondition(
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
std::unordered_set<std::string> nonNullAttributes;
|
||||||
size_t unused = 0;
|
size_t unused = 0;
|
||||||
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found, unused,
|
||||||
unused, nonNullAttributes, true);
|
unused, nonNullAttributes, true);
|
||||||
|
|
||||||
// found contains all attributes that are relevant for this node.
|
// found contains all attributes that are relevant for this node.
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#include "Basics/VelocyPackHelper.h"
|
#include "Basics/VelocyPackHelper.h"
|
||||||
#include "Cache/CachedValue.h"
|
#include "Cache/CachedValue.h"
|
||||||
#include "Cache/TransactionalCache.h"
|
#include "Cache/TransactionalCache.h"
|
||||||
#include "Indexes/SimpleAttributeEqualityMatcher.h"
|
#include "Indexes/SortedIndexAttributeMatcher.h"
|
||||||
#include "RocksDBEdgeIndex.h"
|
#include "RocksDBEdgeIndex.h"
|
||||||
#include "RocksDBEngine/RocksDBCollection.h"
|
#include "RocksDBEngine/RocksDBCollection.h"
|
||||||
#include "RocksDBEngine/RocksDBCommon.h"
|
#include "RocksDBEngine/RocksDBCommon.h"
|
||||||
|
@ -523,8 +523,7 @@ Index::FilterCosts RocksDBEdgeIndex::supportsFilterCondition(
|
||||||
std::vector<std::shared_ptr<arangodb::Index>> const& allIndexes,
|
std::vector<std::shared_ptr<arangodb::Index>> const& allIndexes,
|
||||||
arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference,
|
arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference,
|
||||||
size_t itemsInIndex) const {
|
size_t itemsInIndex) const {
|
||||||
SimpleAttributeEqualityMatcher matcher(this->_fields);
|
return SortedIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex);
|
||||||
return matcher.matchOne(this, node, reference, itemsInIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief creates an IndexIterator for the given Condition
|
/// @brief creates an IndexIterator for the given Condition
|
||||||
|
@ -560,9 +559,7 @@ std::unique_ptr<IndexIterator> RocksDBEdgeIndex::iteratorForCondition(
|
||||||
/// @brief specializes the condition for use with the index
|
/// @brief specializes the condition for use with the index
|
||||||
arangodb::aql::AstNode* RocksDBEdgeIndex::specializeCondition(
|
arangodb::aql::AstNode* RocksDBEdgeIndex::specializeCondition(
|
||||||
arangodb::aql::AstNode* node, arangodb::aql::Variable const* reference) const {
|
arangodb::aql::AstNode* node, arangodb::aql::Variable const* reference) const {
|
||||||
// SimpleAttributeEqualityMatcher matcher(IndexAttributes);
|
return SortedIndexAttributeMatcher::specializeCondition(this, node, reference);
|
||||||
SimpleAttributeEqualityMatcher matcher(this->_fields);
|
|
||||||
return matcher.specializeOne(this, node, reference);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string FindMedian(rocksdb::Iterator* it, std::string const& start,
|
static std::string FindMedian(rocksdb::Iterator* it, std::string const& start,
|
||||||
|
|
|
@ -673,22 +673,7 @@ Index::FilterCosts RocksDBPrimaryIndex::supportsFilterCondition(
|
||||||
std::vector<std::shared_ptr<arangodb::Index>> const& allIndexes,
|
std::vector<std::shared_ptr<arangodb::Index>> const& allIndexes,
|
||||||
arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference,
|
arangodb::aql::AstNode const* node, arangodb::aql::Variable const* reference,
|
||||||
size_t itemsInIndex) const {
|
size_t itemsInIndex) const {
|
||||||
std::unordered_map<size_t, std::vector<arangodb::aql::AstNode const*>> found;
|
return SortedIndexAttributeMatcher::supportsFilterCondition(allIndexes, this, node, reference, itemsInIndex);
|
||||||
std::unordered_set<std::string> nonNullAttributes;
|
|
||||||
|
|
||||||
std::size_t values = 0;
|
|
||||||
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
|
||||||
values, nonNullAttributes,
|
|
||||||
/*skip evaluation (during execution)*/ false);
|
|
||||||
|
|
||||||
Index::FilterCosts costs = Index::FilterCosts::defaultCosts(itemsInIndex);
|
|
||||||
if (!found.empty()) {
|
|
||||||
costs.supportsCondition = true;
|
|
||||||
costs.coveredAttributes = 1; // always a single attribute
|
|
||||||
costs.estimatedItems = values;
|
|
||||||
costs.estimatedCosts = static_cast<double>(values);
|
|
||||||
}
|
|
||||||
return costs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Index::SortCosts RocksDBPrimaryIndex::supportsSortCondition(arangodb::aql::SortCondition const* sortCondition,
|
Index::SortCosts RocksDBPrimaryIndex::supportsSortCondition(arangodb::aql::SortCondition const* sortCondition,
|
||||||
|
|
|
@ -1079,7 +1079,7 @@ std::unique_ptr<IndexIterator> RocksDBVPackIndex::iteratorForCondition(
|
||||||
size_t unused = 0;
|
size_t unused = 0;
|
||||||
|
|
||||||
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
SortedIndexAttributeMatcher::matchAttributes(this, node, reference, found,
|
||||||
unused, nonNullAttributes, true);
|
unused, unused, nonNullAttributes, true);
|
||||||
|
|
||||||
// found contains all attributes that are relevant for this node.
|
// found contains all attributes that are relevant for this node.
|
||||||
// It might be less than fields().
|
// It might be less than fields().
|
||||||
|
|
|
@ -2541,14 +2541,14 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testAbsoluteBetweness: function () {
|
testAbsoluteBetweeness: function () {
|
||||||
var actual = g._absoluteBetweenness(vertexIds.Anton);
|
var actual = g._absoluteBetweenness(vertexIds.Anton);
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testAbsoluteBetwenessAll: function () {
|
testAbsoluteBetweenessAll: function () {
|
||||||
var actual = g._absoluteBetweenness({});
|
var actual = g._absoluteBetweenness({});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2561,7 +2561,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testAbsoluteBetwenessExample: function () {
|
testAbsoluteBetweenessExample: function () {
|
||||||
var actual = g._absoluteBetweenness({gender: "female"});
|
var actual = g._absoluteBetweenness({gender: "female"});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Berta] = 8;
|
expected[vertexIds.Berta] = 8;
|
||||||
|
@ -2569,7 +2569,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testAbsoluteBetwenessAllOutbound: function () {
|
testAbsoluteBetweenessAllOutbound: function () {
|
||||||
var actual = g._absoluteBetweenness({}, {direction: "outbound"});
|
var actual = g._absoluteBetweenness({}, {direction: "outbound"});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2582,7 +2582,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testAbsoluteBetwenessAllInbound: function () {
|
testAbsoluteBetweenessAllInbound: function () {
|
||||||
var actual = g._absoluteBetweenness({}, {direction: "inbound"});
|
var actual = g._absoluteBetweenness({}, {direction: "inbound"});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2595,7 +2595,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testBetwenessAny: function () {
|
testBetweenessAny: function () {
|
||||||
var actual = g._betweenness();
|
var actual = g._betweenness();
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2608,7 +2608,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testBetwenessOutbound: function () {
|
testBetweenessOutbound: function () {
|
||||||
var actual = g._betweenness({direction: "outbound"});
|
var actual = g._betweenness({direction: "outbound"});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2621,7 +2621,7 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
},
|
},
|
||||||
|
|
||||||
testBetwenessInbound: function () {
|
testBetweenessInbound: function () {
|
||||||
var actual = g._betweenness({direction: "inbound"});
|
var actual = g._betweenness({direction: "inbound"});
|
||||||
var expected = { };
|
var expected = { };
|
||||||
expected[vertexIds.Anton] = 0;
|
expected[vertexIds.Anton] = 0;
|
||||||
|
@ -2634,8 +2634,6 @@ function MeasurementsMovedFromAQLSuite() {
|
||||||
validateNumericValues(actual, expected);
|
validateNumericValues(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue