mirror of https://gitee.com/bigwinds/arangodb
slightly changed cost estimation for IndexRangeNode
This commit is contained in:
parent
2148e695f4
commit
c591357dd3
|
@ -1251,7 +1251,7 @@ bool IndexRangeBlock::initRanges () {
|
||||||
for (size_t u = 0; u < _condition->at(s).size(); u++) {
|
for (size_t u = 0; u < _condition->at(s).size(); u++) {
|
||||||
auto ri = _condition->at(s)[u];
|
auto ri = _condition->at(s)[u];
|
||||||
if (en->_index->fields[t].compare(ri._attr) == 0) {
|
if (en->_index->fields[t].compare(ri._attr) == 0) {
|
||||||
prefix.at(s).insert(prefix.at(s).begin()+t, u);
|
prefix.at(s).insert(prefix.at(s).begin() + t, u);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1909,7 +1909,7 @@ void IndexRangeBlock::getSkiplistIterator (IndexAndCondition const& ranges) {
|
||||||
Json parameters(Json::Array);
|
Json parameters(Json::Array);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (; i < ranges.size(); i++) {
|
for (; i < ranges.size(); i++) {
|
||||||
auto range = ranges[i];
|
auto const& range = ranges[i];
|
||||||
TRI_ASSERT(range.isConstant());
|
TRI_ASSERT(range.isConstant());
|
||||||
if (range.is1ValueRangeInfo()) { // it's an equality . . .
|
if (range.is1ValueRangeInfo()) { // it's an equality . . .
|
||||||
parameters(range._lowConst.bound().copy());
|
parameters(range._lowConst.bound().copy());
|
||||||
|
|
|
@ -462,8 +462,8 @@ void ExecutionNode::appendAsString (std::string& st, int indent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief inspect one index; only skiplist indices which match attrs in sequence.
|
/// @brief inspect one index; only skiplist indexes which match attrs in sequence.
|
||||||
/// @returns a a qualification how good they match;
|
/// @returns a qualification how good they match;
|
||||||
/// match->index==nullptr means no match at all.
|
/// match->index==nullptr means no match at all.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -741,7 +741,6 @@ struct RegisterPlanningDebugger : public WalkerWorker<ExecutionNode> {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void ExecutionNode::planRegisters (ExecutionNode* super) {
|
void ExecutionNode::planRegisters (ExecutionNode* super) {
|
||||||
// std::cout << triagens::arango::ServerState::instance()->getId() << ": PLAN REGISTERS\n";
|
|
||||||
// The super is only for the case of subqueries.
|
// The super is only for the case of subqueries.
|
||||||
shared_ptr<RegisterPlan> v;
|
shared_ptr<RegisterPlan> v;
|
||||||
if (super == nullptr) {
|
if (super == nullptr) {
|
||||||
|
@ -1098,7 +1097,7 @@ size_t EnumerateCollectionNode::getUsableFieldsOfIndex (Index const* idx,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief get vector of indices with fields <attrs>
|
/// @brief get vector of indexes with fields <attrs>
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// checks if a subset of <attrs> is a prefix of <idx->_fields> for every index
|
// checks if a subset of <attrs> is a prefix of <idx->_fields> for every index
|
||||||
|
@ -1386,6 +1385,8 @@ ExecutionNode::IndexMatch IndexRangeNode::MatchesIndex (IndexMatchVec const& pat
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
double IndexRangeNode::estimateCost (size_t& nrItems) const {
|
double IndexRangeNode::estimateCost (size_t& nrItems) const {
|
||||||
|
static double const EqualityReductionFactor = 100.0;
|
||||||
|
|
||||||
size_t incoming = 0;
|
size_t incoming = 0;
|
||||||
double const dependencyCost = _dependencies.at(0)->getCost(incoming);
|
double const dependencyCost = _dependencies.at(0)->getCost(incoming);
|
||||||
size_t docCount = _collection->count();
|
size_t docCount = _collection->count();
|
||||||
|
@ -1393,22 +1394,33 @@ double IndexRangeNode::estimateCost (size_t& nrItems) const {
|
||||||
TRI_ASSERT(! _ranges.empty());
|
TRI_ASSERT(! _ranges.empty());
|
||||||
|
|
||||||
if (_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
if (_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
nrItems = incoming;
|
// always an equality lookup
|
||||||
|
nrItems = incoming * _ranges.size();
|
||||||
return dependencyCost + nrItems;
|
return dependencyCost + nrItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
if (_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||||
nrItems = incoming * docCount / 1000;
|
// always an equality lookup
|
||||||
|
nrItems = incoming * _ranges.size() * docCount / EqualityReductionFactor;
|
||||||
return dependencyCost + nrItems;
|
return dependencyCost + nrItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
if (_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
|
// always an equality lookup
|
||||||
if (_index->unique) {
|
if (_index->unique) {
|
||||||
nrItems = incoming;
|
nrItems = incoming * _ranges.size();
|
||||||
return dependencyCost + nrItems;
|
return dependencyCost + nrItems;
|
||||||
}
|
}
|
||||||
nrItems = incoming * docCount / 1000;
|
|
||||||
return dependencyCost + nrItems;
|
double cost = static_cast<double>(docCount) * incoming * _ranges.size();
|
||||||
|
// the more attributes are contained in the index, the more specific the lookup will be
|
||||||
|
for (auto const& x : _ranges.at(0)) {
|
||||||
|
cost /= EqualityReductionFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrItems = static_cast<size_t>(cost);
|
||||||
|
cost *= 0.9999995; // this is to prefer the hash index over skiplists if everything else is equal
|
||||||
|
return dependencyCost + cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
if (_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
|
@ -1420,56 +1432,78 @@ double IndexRangeNode::estimateCost (size_t& nrItems) const {
|
||||||
return dependencyCost + nrItems;
|
return dependencyCost + nrItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_index->unique &&
|
if (_index->unique) {
|
||||||
count == _index->fields.size()) {
|
bool allEquality = true;
|
||||||
if (_ranges.at(0).back().is1ValueRangeInfo()) {
|
for (auto const& x : _ranges) {
|
||||||
|
// check if we are using all indexed attributes in the query
|
||||||
|
if (x.size() != _index->fields.size()) {
|
||||||
|
allEquality = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this is an equality comparison
|
||||||
|
if (x.empty() || ! x.back().is1ValueRangeInfo()) {
|
||||||
|
allEquality = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allEquality) {
|
||||||
// unique index, all attributes compared using eq (==) operator
|
// unique index, all attributes compared using eq (==) operator
|
||||||
nrItems = incoming;
|
nrItems = incoming * _ranges.size();
|
||||||
return dependencyCost + nrItems;
|
return dependencyCost + nrItems;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double cost = static_cast<double>(docCount) * incoming;
|
// build a total cost for the index usage by peeking into all ranges
|
||||||
for (auto x: _ranges.at(0)) { //only doing the 1-d case so far
|
double totalCost = 0.0;
|
||||||
if (x.is1ValueRangeInfo()) {
|
|
||||||
// equality lookup
|
for (auto const& x : _ranges) {
|
||||||
cost /= 100.0;
|
double cost = static_cast<double>(docCount) * incoming;
|
||||||
continue;
|
|
||||||
|
for (auto const& y : x) { //only doing the 1-d case so far
|
||||||
|
if (y.is1ValueRangeInfo()) {
|
||||||
|
// equality lookup
|
||||||
|
cost /= EqualityReductionFactor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasLowerBound = false;
|
||||||
|
bool hasUpperBound = false;
|
||||||
|
|
||||||
|
if (y._lowConst.isDefined() || y._lows.size() > 0) {
|
||||||
|
hasLowerBound = true;
|
||||||
|
}
|
||||||
|
if (y._highConst.isDefined() || y._highs.size() > 0) {
|
||||||
|
hasUpperBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLowerBound && hasUpperBound) {
|
||||||
|
// both lower and upper bounds defined
|
||||||
|
cost /= 10.0;
|
||||||
|
}
|
||||||
|
else if (hasLowerBound || hasUpperBound) {
|
||||||
|
// either only low or high bound defined
|
||||||
|
cost /= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// each bound (const and dynamic) counts!
|
||||||
|
size_t const numBounds = y._lows.size() +
|
||||||
|
y._highs.size() +
|
||||||
|
(y._lowConst.isDefined() ? 1 : 0) +
|
||||||
|
(y._highConst.isDefined() ? 1 : 0);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < numBounds; ++j) {
|
||||||
|
// each dynamic bound again reduces the cost
|
||||||
|
cost *= 0.95;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasLowerBound = false;
|
totalCost += cost;
|
||||||
bool hasUpperBound = false;
|
|
||||||
|
|
||||||
if (x._lowConst.isDefined() || x._lows.size() > 0) {
|
|
||||||
hasLowerBound = true;
|
|
||||||
}
|
|
||||||
if (x._highConst.isDefined() || x._highs.size() > 0) {
|
|
||||||
hasUpperBound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasLowerBound && hasUpperBound) {
|
|
||||||
// both lower and upper bounds defined
|
|
||||||
cost /= 10.0;
|
|
||||||
}
|
|
||||||
else if (hasLowerBound || hasUpperBound) {
|
|
||||||
// either only low or high bound defined
|
|
||||||
cost /= 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// each bound (const and dynamic) counts!
|
|
||||||
size_t const numBounds = x._lows.size() +
|
|
||||||
x._highs.size() +
|
|
||||||
(x._lowConst.isDefined() ? 1 : 0) +
|
|
||||||
(x._highConst.isDefined() ? 1 : 0);
|
|
||||||
|
|
||||||
for (size_t j = 0; j < numBounds; ++j) {
|
|
||||||
// each dynamic bound again reduces the cost
|
|
||||||
cost *= 0.95;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nrItems = static_cast<size_t>(cost);
|
nrItems = static_cast<size_t>(totalCost);
|
||||||
return dependencyCost + cost;
|
return dependencyCost + totalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no index
|
// no index
|
||||||
|
|
|
@ -1333,7 +1333,6 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
// results), for example
|
// results), for example
|
||||||
// x.a == 1 || y.c == 2 || x.a == 3
|
// x.a == 1 || y.c == 2 || x.a == 3
|
||||||
if (_rangeInfoMapVec->isMapped(var->name)) {
|
if (_rangeInfoMapVec->isMapped(var->name)) {
|
||||||
|
|
||||||
std::vector<size_t> const validPos = _rangeInfoMapVec->validPositions(var->name);
|
std::vector<size_t> const validPos = _rangeInfoMapVec->validPositions(var->name);
|
||||||
|
|
||||||
// are any of the RangeInfoMaps in the vector valid?
|
// are any of the RangeInfoMaps in the vector valid?
|
||||||
|
@ -1373,14 +1372,14 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
// index or == followed by a single <, >, >=, or <=
|
// index or == followed by a single <, >, >=, or <=
|
||||||
// if a skip index in the order of the fields of the
|
// if a skip index in the order of the fields of the
|
||||||
// index.
|
// index.
|
||||||
auto idx = idxs.at(i);
|
auto const idx = idxs.at(i);
|
||||||
TRI_ASSERT(idx != nullptr);
|
TRI_ASSERT(idx != nullptr);
|
||||||
|
|
||||||
if (idx->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
if (idx->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
for (size_t k = 0; k < validPos.size(); k++) {
|
for (size_t k = 0; k < validPos.size(); k++) {
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
auto map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
auto const map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
||||||
auto range = map->find(std::string(TRI_VOC_ATTRIBUTE_ID));
|
auto range = map->find(std::string(TRI_VOC_ATTRIBUTE_ID));
|
||||||
|
|
||||||
if (range != map->end()) {
|
if (range != map->end()) {
|
||||||
|
@ -1412,7 +1411,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
else if (idx->type == TRI_IDX_TYPE_HASH_INDEX) {
|
else if (idx->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
//each valid orCondition should match every field of the given index
|
//each valid orCondition should match every field of the given index
|
||||||
for (size_t k = 0; k < validPos.size() && !indexOrCondition.empty(); k++) {
|
for (size_t k = 0; k < validPos.size() && !indexOrCondition.empty(); k++) {
|
||||||
auto map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
auto const map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
||||||
for (size_t j = 0; j < idx->fields.size(); j++) {
|
for (size_t j = 0; j < idx->fields.size(); j++) {
|
||||||
auto range = map->find(idx->fields[j]);
|
auto range = map->find(idx->fields[j]);
|
||||||
|
|
||||||
|
@ -1429,7 +1428,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
else if (idx->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
else if (idx->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||||
for (size_t k = 0; k < validPos.size(); k++) {
|
for (size_t k = 0; k < validPos.size(); k++) {
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
auto map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
auto const map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
||||||
auto range = map->find(std::string(TRI_VOC_ATTRIBUTE_FROM));
|
auto range = map->find(std::string(TRI_VOC_ATTRIBUTE_FROM));
|
||||||
if (range != map->end()) {
|
if (range != map->end()) {
|
||||||
if (! range->second.is1ValueRangeInfo()) {
|
if (! range->second.is1ValueRangeInfo()) {
|
||||||
|
@ -1459,18 +1458,22 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
}
|
}
|
||||||
else if (idx->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
else if (idx->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
for (size_t k = 0; k < validPos.size(); k++) {
|
for (size_t k = 0; k < validPos.size(); k++) {
|
||||||
auto map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
auto const map = _rangeInfoMapVec->find(var->name, validPos[k]);
|
||||||
|
|
||||||
size_t j = 0;
|
// check if there is a range that contains the first index attribute
|
||||||
auto range = map->find(idx->fields[0]);
|
auto range = map->find(idx->fields[0]);
|
||||||
if (range == map->end()) {
|
if (range == map->end()) {
|
||||||
indexOrCondition.clear();
|
indexOrCondition.clear();
|
||||||
break; // not usable
|
break; // not usable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert the first index attribute
|
||||||
indexOrCondition.at(k).push_back(range->second);
|
indexOrCondition.at(k).push_back(range->second);
|
||||||
|
|
||||||
|
// iterate over all index attributes from left to right
|
||||||
bool equality = range->second.is1ValueRangeInfo();
|
bool equality = range->second.is1ValueRangeInfo();
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
size_t j = 0;
|
||||||
while (++j < prefixes.at(i) && equality) {
|
while (++j < prefixes.at(i) && equality) {
|
||||||
range = map->find(idx->fields[j]);
|
range = map->find(idx->fields[j]);
|
||||||
if (range == map->end()) {
|
if (range == map->end()) {
|
||||||
|
@ -1481,6 +1484,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
|
||||||
indexOrCondition.at(k).push_back(range->second);
|
indexOrCondition.at(k).push_back(range->second);
|
||||||
equality = equality && range->second.is1ValueRangeInfo();
|
equality = equality && range->second.is1ValueRangeInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handled) {
|
if (handled) {
|
||||||
// exit the for loop, too. otherwise it will crash because
|
// exit the for loop, too. otherwise it will crash because
|
||||||
// indexOrCondition is empty now
|
// indexOrCondition is empty now
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*jshint strict: false, maxlen: 500 */
|
/*jshint strict: false, maxlen: 500 */
|
||||||
/*global require, assertTrue, assertEqual, assertNotEqual, AQL_EXPLAIN, AQL_EXECUTE */
|
/*global require, assertTrue, assertFalse, assertEqual, assertNotEqual, AQL_EXPLAIN, AQL_EXECUTE */
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief tests for index usage
|
/// @brief tests for index usage
|
||||||
|
@ -544,7 +544,241 @@ function optimizerIndexesTestSuite () {
|
||||||
/// @brief test index usage
|
/// @brief test index usage
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testIndexOr : function () {
|
testSortWithMultipleIndexes : function () {
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
// create multiple indexes
|
||||||
|
c.ensureHashIndex("value");
|
||||||
|
c.ensureHashIndex("value", "value2");
|
||||||
|
c.ensureSkiplist("value", "value2");
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 SORT i.value RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1 ], results.json, query);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
assertEqual(1, results.stats.scannedIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testSortWithMultipleIndexesAndRanges : function () {
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
// create multiple indexes
|
||||||
|
c.ensureHashIndex("value");
|
||||||
|
c.ensureHashIndex("value", "value2");
|
||||||
|
c.ensureSkiplist("value", "value2");
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 9 || i.value == 1 SORT i.value RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json, query);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testMultiSortWithMultipleIndexes : function () {
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
// create multiple indexes
|
||||||
|
c.ensureHashIndex("value");
|
||||||
|
c.ensureHashIndex("value", "value2");
|
||||||
|
c.ensureSkiplist("value", "value2");
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 SORT i.value, i.value2 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1 ], results.json, query);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
assertEqual(1, results.stats.scannedIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testMultiSortWithMultipleIndexesAndRanges : function () {
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
// create multiple indexes
|
||||||
|
c.ensureHashIndex("value");
|
||||||
|
c.ensureHashIndex("value", "value2");
|
||||||
|
c.ensureSkiplist("value", "value2");
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 9 || i.value == 1 SORT i.value, i.value2 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json, query);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrPrimary : function () {
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i._key == 'test1' || i._key == 'test9' RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("primary", node.index.type);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrHash : function () {
|
||||||
|
c.ensureHashIndex("value");
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 || i.value == 9 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("hash", node.index.type);
|
||||||
|
assertFalse(node.index.unique);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrUniqueHash : function () {
|
||||||
|
c.ensureUniqueConstraint("value");
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 || i.value == 9 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("hash", node.index.type);
|
||||||
|
assertTrue(node.index.unique);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrSkiplist : function () {
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 || i.value == 9 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("skiplist", node.index.type);
|
||||||
|
assertFalse(node.index.unique);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrUniqueSkiplist : function () {
|
||||||
|
c.ensureUniqueSkiplist("value");
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER i.value == 1 || i.value == 9 RETURN i.value";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("skiplist", node.index.type);
|
||||||
|
assertTrue(node.index.unique);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 9 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrNoIndex : function () {
|
||||||
c.ensureSkiplist("value2");
|
c.ensureSkiplist("value2");
|
||||||
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
@ -555,10 +789,7 @@ function optimizerIndexesTestSuite () {
|
||||||
return node.type;
|
return node.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
assertEqual("SingletonNode", nodeTypes[0], query);
|
|
||||||
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
|
||||||
assertEqual("ReturnNode", nodeTypes[nodeTypes.length - 1], query);
|
|
||||||
|
|
||||||
var results = AQL_EXECUTE(query);
|
var results = AQL_EXECUTE(query);
|
||||||
assertEqual([ 1 ], results.json, query);
|
assertEqual([ 1 ], results.json, query);
|
||||||
|
@ -570,7 +801,158 @@ function optimizerIndexesTestSuite () {
|
||||||
/// @brief test index usage
|
/// @brief test index usage
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testIndexAnd : function () {
|
testIndexOrHashMultipleRanges : function () {
|
||||||
|
c.ensureHashIndex("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 2 && i.value3 == 2) || (i.value2 == 3 && i.value3 == 3) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("hash", node.index.type);
|
||||||
|
assertFalse(node.index.unique);
|
||||||
|
assertEqual([ "value2", "value3" ], node.index.fields);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 2, 3 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrSkiplistMultipleRanges : function () {
|
||||||
|
c.ensureSkiplist("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 2 && i.value3 == 2) || (i.value2 == 3 && i.value3 == 3) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("skiplist", node.index.type);
|
||||||
|
assertFalse(node.index.unique);
|
||||||
|
assertEqual([ "value2", "value3" ], node.index.fields);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 2, 3 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrHashMultipleRangesPartialNoIndex1 : function () {
|
||||||
|
c.ensureHashIndex("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value3 == 1) || (i.value2 == 2) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 2 ], results.json.sort(), query);
|
||||||
|
assertEqual(0, results.stats.scannedIndex);
|
||||||
|
assertTrue(results.stats.scannedFull > 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrHashMultipleRangesPartialNoIndex2 : function () {
|
||||||
|
c.ensureHashIndex("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value3 == 1) || (i.value3 == 2) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 2 ], results.json.sort(), query);
|
||||||
|
assertEqual(0, results.stats.scannedIndex);
|
||||||
|
assertTrue(results.stats.scannedFull > 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
testIndexOrSkiplistMultipleRangesPartialIndex : function () {
|
||||||
|
c.ensureSkiplist("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value3 == 1) || (i.value2 == 2) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
if (node.type === "IndexRangeNode") {
|
||||||
|
assertEqual("skiplist", node.index.type);
|
||||||
|
assertFalse(node.index.unique);
|
||||||
|
assertEqual([ "value2", "value3" ], node.index.fields);
|
||||||
|
}
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 2 ], results.json.sort(), query);
|
||||||
|
assertEqual(2, results.stats.scannedIndex);
|
||||||
|
assertEqual(0, results.stats.scannedFull);
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexOrSkiplistMultipleRangesPartialNoIndex : function () {
|
||||||
|
c.ensureSkiplist("value2", "value3");
|
||||||
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
var query = "FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value3 == 1) || (i.value3 == 2) RETURN i.value2";
|
||||||
|
|
||||||
|
var plan = AQL_EXPLAIN(query).plan;
|
||||||
|
var nodeTypes = plan.nodes.map(function(node) {
|
||||||
|
return node.type;
|
||||||
|
});
|
||||||
|
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
|
|
||||||
|
var results = AQL_EXECUTE(query);
|
||||||
|
assertEqual([ 1, 2 ], results.json.sort(), query);
|
||||||
|
assertEqual(0, results.stats.scannedIndex);
|
||||||
|
assertTrue(results.stats.scannedFull > 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test index usage
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIndexAndNoIndex : function () {
|
||||||
c.ensureSkiplist("value2");
|
c.ensureSkiplist("value2");
|
||||||
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
|
||||||
|
|
||||||
|
@ -581,16 +963,13 @@ function optimizerIndexesTestSuite () {
|
||||||
return node.type;
|
return node.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
assertEqual("SingletonNode", nodeTypes[0], query);
|
|
||||||
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
|
||||||
assertEqual(-1, nodeTypes.indexOf("SortNode"), query);
|
|
||||||
assertEqual("ReturnNode", nodeTypes[nodeTypes.length - 1], query);
|
|
||||||
|
|
||||||
var results = AQL_EXECUTE(query);
|
var results = AQL_EXECUTE(query);
|
||||||
assertEqual([ 1 ], results.json, query);
|
assertEqual([ 1 ], results.json, query);
|
||||||
assertEqual(0, results.stats.scannedFull);
|
assertEqual(0, results.stats.scannedFull);
|
||||||
assertTrue(results.stats.scannedIndex > 0);
|
assertTrue(results.stats.scannedIndex > 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue