1
0
Fork 0
arangodb/tests/js/server/aql/aql-optimizer-index-only-ro...

441 lines
20 KiB
JavaScript

/*jshint globalstrict:false, strict:false, maxlen: 500 */
/*global assertTrue, assertFalse, assertEqual, AQL_EXPLAIN, AQL_EXECUTE */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for produces result
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
const jsunity = require("jsunity");
const db = require("@arangodb").db;
const disableSingleDocOp = { optimizer : { rules : [ "-optimize-cluster-single-document-operations"] } };
function optimizerIndexOnlyPrimaryTestSuite () {
let c;
return {
setUp : function () {
db._drop("UnitTestsCollection");
c = db._create("UnitTestsCollection");
for (var i = 0; i < 2000; ++i) {
c.save({ _key: "test" + i, a: (i % 10), b: i });
}
},
tearDown : function () {
db._drop("UnitTestsCollection");
},
testNoProjectionsButIndex : function () {
let queries = [
`FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._key == "test123" SORT doc._key RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._key == "test123" SORT doc RETURN doc.a`,
`FOR doc IN ${c.name()} FILTER doc._key == "test123" SORT doc RETURN doc._key`,
`FOR doc IN ${c.name()} FILTER doc._id == "${c.name()}/test123" RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._id == "${c.name()}/test123" SORT doc RETURN doc._id`
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query, {}, disableSingleDocOp).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual([], nodes[0].projections, query);
assertTrue(nodes[0].producesResult);
assertFalse(nodes[0].indexCoversProjections);
});
},
testNotCoveringProjection : function () {
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN [ doc._key, doc.b ]`, ["_key", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN [ doc.c, doc.d ]`, ["c", "d"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" && doc.b == 1 RETURN doc.x`, ["b", "x"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" && doc.b == 1 RETURN CONCAT(doc._key, doc.u)`, ["_key", "b", "u"] ],
[ `FOR doc IN ${c.name()} FILTER doc._key == "test123" SORT doc.x RETURN doc.x`, ["x"] ]
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0], {}, disableSingleDocOp).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort(), query);
assertFalse(nodes[0].indexCoversProjections);
});
},
testIndexCoveringProjection : function () {
let query = `FOR doc IN ${c.name()} FILTER doc._key == "test123" RETURN doc._key`;
let results = AQL_EXECUTE(query, {}, disableSingleDocOp).json;
assertEqual(1, results.length);
assertEqual("test123", results[0]);
let plan = AQL_EXPLAIN(query, {}, disableSingleDocOp).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["_key"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testIndexCoveringInProjection : function () {
let query = `FOR doc IN ${c.name()} FILTER doc._key IN ["test123", "test124", "test125"] RETURN doc._key`;
let results = AQL_EXECUTE(query, {}, disableSingleDocOp).json;
assertEqual(3, results.length);
let plan = AQL_EXPLAIN(query, {}, disableSingleDocOp).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["_key"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
}
};
}
function optimizerIndexOnlyEdgeTestSuite () {
let c;
return {
setUp : function () {
db._drop("UnitTestsCollection");
c = db._createEdgeCollection("UnitTestsCollection");
for (var i = 0; i < 2000; ++i) {
c.save({ _key: "test" + i, _from: "test/" + (i % 10), _to: "test/" + i });
}
},
tearDown : function () {
db._drop("UnitTestsCollection");
},
testEdgeNoProjectionsButIndex : function () {
let queries = [
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc._from RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc RETURN doc._to`,
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc RETURN doc._a`,
`FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc._from RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc._to RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc RETURN doc._to`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc RETURN doc._from`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc RETURN doc._a`,
`FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN doc`
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual([], nodes[0].projections, query);
assertTrue(nodes[0].producesResult);
assertFalse(nodes[0].indexCoversProjections);
});
},
testEdgeNotCoveringProjection : function () {
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN [ doc._from, doc.b ]`, ["_from", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN [ doc.c, doc.d ]`, ["c", "d"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" && doc.b == 1 RETURN doc.x`, ["b", "x"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" && doc.b == 1 RETURN CONCAT(doc._from, doc.u)`, ["_from", "b", "u"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc.x RETURN doc.x`, ["x"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN [ doc._to, doc.b ]`, ["_to", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN [ doc.c, doc.d ]`, ["c", "d"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" && doc.b == 1 RETURN doc.x`, ["b", "x"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" && doc.b == 1 RETURN CONCAT(doc._to, doc.u)`, ["_to", "b", "u"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc.x RETURN doc.x`, ["x"] ]
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort(), query);
assertFalse(nodes[0].indexCoversProjections);
});
},
testEdgeIndexFromCoveringProjection : function () {
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN doc._from`, ["_from"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" SORT doc._from RETURN doc._to`, ["_to"] ],
[ `FOR doc IN ${c.name()} FILTER doc._from == "test/123" RETURN doc._to`, ["_to"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" SORT doc._to RETURN doc._from`, ["_from"] ],
[ `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN doc._from`, ["_from"] ]
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
});
},
testEdgeIndexFromCoveringInProjection : function () {
let query = `FOR doc IN ${c.name()} FILTER doc._from IN ["test/123", "test/124", "test/125"] RETURN doc._from`;
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["_from"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testEdgeIndexToCoveringProjection : function () {
let query = `FOR doc IN ${c.name()} FILTER doc._to == "test/123" RETURN doc._to`;
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["_to"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testEdgeIndexToCoveringInProjection : function () {
let query = `FOR doc IN ${c.name()} FILTER doc._to IN ["test/123", "test/124", "test/125"] RETURN doc._to`;
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["_to"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
}
};
}
function optimizerIndexOnlyVPackTestSuite () {
let c;
return {
setUp : function () {
db._drop("UnitTestsCollection");
c = db._create("UnitTestsCollection");
for (var i = 0; i < 2000; ++i) {
c.save({ _key: "test" + i, a: (i % 10), b: i });
}
},
tearDown : function () {
db._drop("UnitTestsCollection");
},
testNoProjectionsNoIndex : function () {
let queries = [
`FOR doc IN ${c.name()} RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc.a == 1 && doc.b == 1 && doc.c == 1 && doc.d == 1 && doc.e == 1 RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc.a == 1 && doc.b == 1 && doc.c == 1 && doc.d == 1 && doc.e == 1 RETURN doc.f`,
`FOR doc IN ${c.name()} RETURN [doc.a, doc.b, doc.c, doc.d, doc.e, doc.f]`
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'EnumerateCollectionNode'; });
assertEqual(1, nodes.length);
assertEqual([], nodes[0].projections);
assertTrue(nodes[0].producesResult);
});
},
testJustProjectionsNoIndex : function () {
let queries = [
[ `FOR doc IN ${c.name()} RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} RETURN [ doc.a, doc.b, doc.c ]`, ["a", "b", "c"] ],
[ `FOR doc IN ${c.name()} RETURN [ doc.a, doc.b, doc.c, doc.d ]`, ["a", "b", "c", "d"] ],
[ `FOR doc IN ${c.name()} RETURN [ doc.a, doc.b, doc.c, doc.d, doc.e ]`, ["a", "b", "c", "d", "e"] ],
[ `FOR doc IN ${c.name()} RETURN [ doc.a, doc.b, doc.c, doc.d, doc.e, 123 ]`, ["a", "b", "c", "d", "e"] ],
[ `FOR doc IN ${c.name()} FILTER doc.y == 1 RETURN [ doc.a, doc.b ]`, ["a", "b", "y"] ],
[ `FOR doc IN ${c.name()} FILTER doc.x == 1 && doc.y == 1 RETURN [ doc.a, doc.b ]`, ["a", "b", "x", "y"] ],
[ `FOR doc IN ${c.name()} SORT doc.x RETURN doc.a`, ["a", "x"] ],
[ `FOR doc IN ${c.name()} SORT doc.x, doc.y RETURN doc.a`, ["a", "x", "y"] ]
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'EnumerateCollectionNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort());
assertTrue(nodes[0].producesResult);
});
},
testVPackNoProjectionsButIndex : function () {
c.ensureIndex({ type: "hash", fields: ["a"] });
let queries = [
`FOR doc IN ${c.name()} FILTER doc.a == 1 RETURN doc`,
`FOR doc IN ${c.name()} FILTER doc.a == 1 SORT doc RETURN doc.a`,
`FOR doc IN ${c.name()} FILTER doc.a > 1 RETURN doc`
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual([], nodes[0].projections);
assertTrue(nodes[0].producesResult);
assertFalse(nodes[0].indexCoversProjections);
});
},
testVPackSingleFieldIndexNotCoveringProjection : function () {
c.ensureIndex({ type: "hash", fields: ["a"] });
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN [ doc.c, doc.d ]`, ["c", "d"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b == 1 RETURN doc.x`, ["b", "x"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b == 1 RETURN doc.a + doc.u`, ["a", "b", "u"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a == 1 SORT doc.x RETURN doc.x`, ["x"] ]
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort());
assertFalse(nodes[0].indexCoversProjections);
});
},
testVPackSingleFieldIndexCoveringProjection : function () {
c.ensureIndex({ type: "hash", fields: ["a"] });
let query = `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN doc.a`;
let results = AQL_EXECUTE(query).json;
assertEqual(2000, results.length);
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["a"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testVPackSingleFieldIndexCoveringInProjection : function () {
c.ensureIndex({ type: "hash", fields: ["a"] });
let query = `FOR doc IN ${c.name()} FILTER doc.a IN [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] RETURN doc.a`;
let results = AQL_EXECUTE(query).json;
assertEqual(2000, results.length);
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["a"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testVPackSingleFieldUniqueIndexCoveringProjection : function () {
c.ensureIndex({ type: "hash", fields: ["b"], unique: true });
let query = `FOR doc IN ${c.name()} FILTER doc.b >= 0 RETURN doc.b`;
let results = AQL_EXECUTE(query).json;
assertEqual(2000, results.length);
let plan = AQL_EXPLAIN(query).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(["b"], nodes[0].projections);
assertTrue(nodes[0].indexCoversProjections);
},
testVPackMultipleFieldsIndexCoveringProjection : function () {
c.ensureIndex({ type: "hash", fields: ["a", "b"] });
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a, doc.b RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a, doc.b RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 RETURN doc.a`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 SORT doc.a RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 SORT doc.a, doc.b RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort(), query);
assertTrue(nodes[0].indexCoversProjections);
});
},
testVPackMultipleFieldsUniqueIndexCoveringProjection : function () {
c.ensureIndex({ type: "hash", fields: ["a", "b"], unique: true });
let queries = [
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a RETURN doc.b`, ["b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a, doc.b RETURN doc.a`, ["a"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 SORT doc.a, doc.b RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 RETURN doc.a`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 SORT doc.a RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
[ `FOR doc IN ${c.name()} FILTER doc.a >= 0 && doc.b >= 0 SORT doc.a, doc.b RETURN [ doc.a, doc.b ]`, ["a", "b"] ],
];
queries.forEach(function(query) {
let plan = AQL_EXPLAIN(query[0]).plan;
let nodes = plan.nodes.filter(function(n) { return n.type === 'IndexNode'; });
assertEqual(1, nodes.length);
assertEqual(query[1], nodes[0].projections.sort(), query);
assertTrue(nodes[0].indexCoversProjections);
});
}
};
}
jsunity.run(optimizerIndexOnlyPrimaryTestSuite);
jsunity.run(optimizerIndexOnlyEdgeTestSuite);
jsunity.run(optimizerIndexOnlyVPackTestSuite);
return jsunity.done();