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++) {
|
||||
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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue