1
0
Fork 0

slightly changed cost estimation for IndexRangeNode

This commit is contained in:
Jan Steemann 2015-01-07 15:11:42 +01:00
parent 2148e695f4
commit c591357dd3
4 changed files with 487 additions and 70 deletions

View File

@ -1251,7 +1251,7 @@ bool IndexRangeBlock::initRanges () {
for (size_t u = 0; u < _condition->at(s).size(); u++) {
auto ri = _condition->at(s)[u];
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;
}
}
@ -1909,7 +1909,7 @@ void IndexRangeBlock::getSkiplistIterator (IndexAndCondition const& ranges) {
Json parameters(Json::Array);
size_t i = 0;
for (; i < ranges.size(); i++) {
auto range = ranges[i];
auto const& range = ranges[i];
TRI_ASSERT(range.isConstant());
if (range.is1ValueRangeInfo()) { // it's an equality . . .
parameters(range._lowConst.bound().copy());

View File

@ -462,8 +462,8 @@ void ExecutionNode::appendAsString (std::string& st, int indent) {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief inspect one index; only skiplist indices which match attrs in sequence.
/// @returns a a qualification how good they match;
/// @brief inspect one index; only skiplist indexes which match attrs in sequence.
/// @returns a qualification how good they match;
/// match->index==nullptr means no match at all.
////////////////////////////////////////////////////////////////////////////////
@ -741,7 +741,6 @@ struct RegisterPlanningDebugger : public WalkerWorker<ExecutionNode> {
////////////////////////////////////////////////////////////////////////////////
void ExecutionNode::planRegisters (ExecutionNode* super) {
// std::cout << triagens::arango::ServerState::instance()->getId() << ": PLAN REGISTERS\n";
// The super is only for the case of subqueries.
shared_ptr<RegisterPlan> v;
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
@ -1386,6 +1385,8 @@ ExecutionNode::IndexMatch IndexRangeNode::MatchesIndex (IndexMatchVec const& pat
////////////////////////////////////////////////////////////////////////////////
double IndexRangeNode::estimateCost (size_t& nrItems) const {
static double const EqualityReductionFactor = 100.0;
size_t incoming = 0;
double const dependencyCost = _dependencies.at(0)->getCost(incoming);
size_t docCount = _collection->count();
@ -1393,22 +1394,33 @@ double IndexRangeNode::estimateCost (size_t& nrItems) const {
TRI_ASSERT(! _ranges.empty());
if (_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
nrItems = incoming;
// always an equality lookup
nrItems = incoming * _ranges.size();
return dependencyCost + nrItems;
}
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;
}
if (_index->type == TRI_IDX_TYPE_HASH_INDEX) {
// always an equality lookup
if (_index->unique) {
nrItems = incoming;
nrItems = incoming * _ranges.size();
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) {
@ -1420,56 +1432,78 @@ double IndexRangeNode::estimateCost (size_t& nrItems) const {
return dependencyCost + nrItems;
}
if (_index->unique &&
count == _index->fields.size()) {
if (_ranges.at(0).back().is1ValueRangeInfo()) {
if (_index->unique) {
bool allEquality = true;
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
nrItems = incoming;
nrItems = incoming * _ranges.size();
return dependencyCost + nrItems;
}
}
double cost = static_cast<double>(docCount) * incoming;
for (auto x: _ranges.at(0)) { //only doing the 1-d case so far
if (x.is1ValueRangeInfo()) {
// equality lookup
cost /= 100.0;
continue;
// build a total cost for the index usage by peeking into all ranges
double totalCost = 0.0;
for (auto const& x : _ranges) {
double cost = static_cast<double>(docCount) * incoming;
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;
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;
}
totalCost += cost;
}
nrItems = static_cast<size_t>(cost);
return dependencyCost + cost;
nrItems = static_cast<size_t>(totalCost);
return dependencyCost + totalCost;
}
// no index

View File

@ -1333,7 +1333,6 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
// results), for example
// x.a == 1 || y.c == 2 || x.a == 3
if (_rangeInfoMapVec->isMapped(var->name)) {
std::vector<size_t> const validPos = _rangeInfoMapVec->validPositions(var->name);
// 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 <=
// if a skip index in the order of the fields of the
// index.
auto idx = idxs.at(i);
auto const idx = idxs.at(i);
TRI_ASSERT(idx != nullptr);
if (idx->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
for (size_t k = 0; k < validPos.size(); k++) {
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));
if (range != map->end()) {
@ -1412,7 +1411,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
else if (idx->type == TRI_IDX_TYPE_HASH_INDEX) {
//each valid orCondition should match every field of the given index
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++) {
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) {
for (size_t k = 0; k < validPos.size(); k++) {
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));
if (range != map->end()) {
if (! range->second.is1ValueRangeInfo()) {
@ -1459,18 +1458,22 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
}
else if (idx->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
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]);
if (range == map->end()) {
indexOrCondition.clear();
break; // not usable
}
// insert the first index attribute
indexOrCondition.at(k).push_back(range->second);
// iterate over all index attributes from left to right
bool equality = range->second.is1ValueRangeInfo();
bool handled = false;
size_t j = 0;
while (++j < prefixes.at(i) && equality) {
range = map->find(idx->fields[j]);
if (range == map->end()) {
@ -1481,6 +1484,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
indexOrCondition.at(k).push_back(range->second);
equality = equality && range->second.is1ValueRangeInfo();
}
if (handled) {
// exit the for loop, too. otherwise it will crash because
// indexOrCondition is empty now

View File

@ -1,5 +1,5 @@
/*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
@ -544,7 +544,241 @@ function optimizerIndexesTestSuite () {
/// @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");
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;
});
assertEqual("SingletonNode", nodeTypes[0], 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);
assertEqual([ 1 ], results.json, query);
@ -570,7 +801,158 @@ function optimizerIndexesTestSuite () {
/// @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");
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;
});
assertEqual("SingletonNode", nodeTypes[0], 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);
assertEqual([ 1 ], results.json, query);
assertEqual(0, results.stats.scannedFull);
assertTrue(results.stats.scannedIndex > 0);
},
}
};
}