mirror of https://gitee.com/bigwinds/arangodb
This commit is contained in:
parent
9f0036beb0
commit
021794e4d2
|
@ -1,3 +1,9 @@
|
||||||
|
v3.3.5 (XXXX-XX-XX)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* fixed issue #4827: COLLECT on edge _to field doesn't group distinct values as expected (MMFiles)
|
||||||
|
|
||||||
|
|
||||||
v3.3.4 (2018-03-01)
|
v3.3.4 (2018-03-01)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
@ -1858,9 +1858,55 @@ struct SortToIndexNode final : public WalkerWorker<ExecutionNode> {
|
||||||
auto condNode = root->getMember(0);
|
auto condNode = root->getMember(0);
|
||||||
|
|
||||||
if (condNode->isOnlyEqualityMatch()) {
|
if (condNode->isOnlyEqualityMatch()) {
|
||||||
// now check if the index fields are the same as the sort condition
|
// now check if the index fields are the same as the sort condition fields
|
||||||
// fields
|
|
||||||
// e.g. FILTER c.value1 == 1 && c.value2 == 42 SORT c.value1, c.value2
|
// e.g. FILTER c.value1 == 1 && c.value2 == 42 SORT c.value1, c.value2
|
||||||
|
auto i = index.getIndex();
|
||||||
|
// some special handling for the MMFiles edge index here, which to the outside
|
||||||
|
// world is an index on attributes _from and _to at the same time, but only one
|
||||||
|
// can be queried at a time
|
||||||
|
// this special handling is required in order to prevent lookups by one of the index
|
||||||
|
// attributes (e.g. _from) and a sort clause on the other index attribte (e.g. _to)
|
||||||
|
// to be treated as the same index attribute, e.g.
|
||||||
|
// FOR doc IN edgeCol FILTER doc._from == ... SORT doc._to ...
|
||||||
|
// can use the index either for lookup or for sorting, but not for both at the same
|
||||||
|
// time. this is because if we do the lookup by _from, the results will be sorted
|
||||||
|
// by _from, and not by _to.
|
||||||
|
if (i->type() == arangodb::Index::IndexType::TRI_IDX_TYPE_EDGE_INDEX && fields.size() == 2) {
|
||||||
|
// looks like MMFiles edge index
|
||||||
|
if (condNode->type == NODE_TYPE_OPERATOR_NARY_AND) {
|
||||||
|
// check all conditions of the index node, and check if we can find _from or _to
|
||||||
|
for (size_t j = 0; j < condNode->numMembers(); ++j) {
|
||||||
|
auto sub = condNode->getMemberUnchecked(j);
|
||||||
|
if (sub->type != NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto lhs = sub->getMember(0);
|
||||||
|
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS &&
|
||||||
|
lhs->getMember(0)->type == NODE_TYPE_REFERENCE &&
|
||||||
|
lhs->getMember(0)->getData() == outVariable) {
|
||||||
|
// check if this is either _from or _to
|
||||||
|
std::string attr = lhs->getString();
|
||||||
|
if (attr == StaticStrings::FromString || attr == StaticStrings::ToString) {
|
||||||
|
// reduce index fields to just the attribute we found in the index lookup condition
|
||||||
|
fields = {{arangodb::basics::AttributeName(attr, false)} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rhs = sub->getMember(1);
|
||||||
|
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS &&
|
||||||
|
rhs->getMember(0)->type == NODE_TYPE_REFERENCE &&
|
||||||
|
rhs->getMember(0)->getData() == outVariable) {
|
||||||
|
// check if this is either _from or _to
|
||||||
|
std::string attr = rhs->getString();
|
||||||
|
if (attr == StaticStrings::FromString || attr == StaticStrings::ToString) {
|
||||||
|
// reduce index fields to just the attribute we found in the index lookup condition
|
||||||
|
fields = {{arangodb::basics::AttributeName(attr, false)} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t const numCovered =
|
size_t const numCovered =
|
||||||
sortCondition.coveredAttributes(outVariable, fields);
|
sortCondition.coveredAttributes(outVariable, fields);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* jshint globalstrict:false, strict:false, maxlen: 500 */
|
/* jshint globalstrict:false, strict:false, maxlen: 500 */
|
||||||
/* global assertEqual, AQL_EXECUTE */
|
/* global assertEqual, assertTrue, AQL_EXECUTE, AQL_EXPLAIN */
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief tests for index usage
|
// / @brief tests for index usage
|
||||||
|
@ -166,6 +166,110 @@ function optimizerEdgeIndexTestSuite () {
|
||||||
assertEqual(0, results.stats.scannedFull);
|
assertEqual(0, results.stats.scannedFull);
|
||||||
assertEqual(query[1], results.stats.scannedIndex);
|
assertEqual(query[1], results.stats.scannedIndex);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnFromSortOnToAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._from == 'UnitTestsCollection/nono' COLLECT to = doc._to RETURN to";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(1, node.elements.length);
|
||||||
|
assertEqual("to", node.elements[0].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnFromSortOnFromAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._from == 'UnitTestsCollection/nono' COLLECT from = doc._from RETURN from";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(1, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(0, node.length);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnFromSortOnFromToAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._from == 'UnitTestsCollection/nono' COLLECT from = doc._from, to = doc._to RETURN { from, to }";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(2, node.elements.length);
|
||||||
|
assertEqual("from", node.elements[0].inVariable.name);
|
||||||
|
assertEqual("to", node.elements[1].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
assertTrue(node.elements[1].ascending);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnFromSortOnToFromAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._from == 'UnitTestsCollection/nono' COLLECT to = doc._to, from = doc._from RETURN { from, to }";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(2, node.elements.length);
|
||||||
|
assertEqual("to", node.elements[0].inVariable.name);
|
||||||
|
assertEqual("from", node.elements[1].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
assertTrue(node.elements[1].ascending);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnToSortOnFromAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._to == 'UnitTestsCollection/nono' COLLECT from = doc._from RETURN from";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(1, node.elements.length);
|
||||||
|
assertEqual("from", node.elements[0].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnToSortOnToAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._to == 'UnitTestsCollection/nono' COLLECT to = doc._to RETURN to";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(1, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(0, node.length);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnToSortOnToFromAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._to == 'UnitTestsCollection/nono' COLLECT to = doc._to, from = doc._from RETURN { from, to }";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(2, node.elements.length);
|
||||||
|
assertEqual("to", node.elements[0].inVariable.name);
|
||||||
|
assertEqual("from", node.elements[1].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
assertTrue(node.elements[1].ascending);
|
||||||
|
},
|
||||||
|
|
||||||
|
testLookupOnToSortOnFromToAttribute: function () {
|
||||||
|
let query = "FOR doc IN " + e.name() + " FILTER doc._to == 'UnitTestsCollection/nono' COLLECT from = doc._from, to = doc._to RETURN { from, to }";
|
||||||
|
let results = AQL_EXECUTE(query);
|
||||||
|
assertEqual(19, results.json.length);
|
||||||
|
|
||||||
|
let node = AQL_EXPLAIN(query).plan.nodes.filter(function(n) { return n.type === 'SortNode'; });
|
||||||
|
assertEqual(1, node.length);
|
||||||
|
node = node[0];
|
||||||
|
assertEqual(2, node.elements.length);
|
||||||
|
assertEqual("from", node.elements[0].inVariable.name);
|
||||||
|
assertEqual("to", node.elements[1].inVariable.name);
|
||||||
|
assertTrue(node.elements[0].ascending);
|
||||||
|
assertTrue(node.elements[1].ascending);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -174,4 +278,3 @@ function optimizerEdgeIndexTestSuite () {
|
||||||
jsunity.run(optimizerEdgeIndexTestSuite);
|
jsunity.run(optimizerEdgeIndexTestSuite);
|
||||||
|
|
||||||
return jsunity.done();
|
return jsunity.done();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue