1
0
Fork 0
arangodb/tests/js/server/aql/aql-subqueries-cluster.js

381 lines
12 KiB
JavaScript

/*jshint globalstrict:false, strict:false */
/*global assertEqual, assertTrue, assertFalse */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for query language, subqueries in cluster
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2018-2018 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2018, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
const jsunity = require("jsunity");
const helper = require("@arangodb/aql-helper");
const getQueryResults = helper.getQueryResults;
const db = require("internal").db;
const c1 = "UnitTestSubQuery1";
const c2 = "UnitTestSubQuery2";
const c3 = "UnitTestSubQuery3";
const c4 = "UnitTestSubQuery4";
const c5 = "UnitTestSubQuery5";
const c6 = "UnitTestSubQuery6";
const c7 = "UnitTestSubQuery7";
const c8 = "UnitTestSubQuery8";
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
/**
* @brief This suite is supposed to test subquery execution
* in the cluster case. Planning for subqueries tend to be
* complicated and thereby error prone.
*
* NOTE: Please do not take the following AQLs as well designed
* production queries or best practices. They are made artificially
* complex for the internals.
*
* @return The TestStuie
*/
function clusterSubqueriesTestSuite () {
const clean = () => {
db._drop(c1);
db._drop(c2);
db._drop(c3);
db._drop(c4);
db._drop(c5);
db._drop(c6);
db._drop(c7);
db._drop(c8);
};
return {
setUp: function () {
clean();
db._create(c1, {numberOfShards: 4});
db._create(c2, {numberOfShards: 4});
db._create(c3, {numberOfShards: 4});
db._create(c4, {numberOfShards: 4});
db._create(c5, {numberOfShards: 4});
db._create(c6, {numberOfShards: 4});
db._create(c7, {numberOfShards: 4});
db._create(c8, {numberOfShards: 4});
},
tearDown: clean,
// This test validates that all input values `x` are
// transportet to the input register of the subquery
// This is done via successful initializeCursor calls
testSimpleInitialzeCursor: function () {
let docs = [];
// We add 5 times each of the values 0 -> 19
// In the query we will just use 1->10
for (let i = 0; i < 20; ++i) {
for (let j = 0; j < 5; ++j) {
docs.push({foo: `bar${i}`});
}
}
db[c1].save(docs);
let q = `
FOR x IN 1..10
LET sub = (
FOR y IN ${c1}
FILTER y.foo == CONCAT("bar", TO_STRING(x))
RETURN y
)
RETURN sub[*].foo
`;
let c = db._query(q).toArray();
assertEqual(c.length, 10); // we have 10 values for X
let seen = new Set();
for (let d of c) {
assertEqual(d.length, 5); // we have 5 values for each x
let first = d[0];
seen.add(first);
for (let q of d) {
assertEqual(q, first);
}
}
assertEqual(seen.size, 10); // We have 10 different values
},
// Tests if all snippets are created correctly
// in subquery case if both root nodes
// end on DBServers
testSnippetsRootDB: function () {
let docsA = [];
let docsB = [];
let docsC = [];
let fooVals = new Map();
// We add 5 times each of the values 1 -> 10
for (let i = 1; i < 11; ++i) {
fooVals.set(`${i}`, 0);
docsA.push({foo: `${i}`});
for (let j = 0; j < 5; ++j) {
docsB.push({foo: `${i}`});
docsC.push({foo: `${i}`});
}
}
db[c1].save(docsA);
db[c2].save(docsB);
db[c3].save(docsC);
let q = `
FOR a IN ${c1}
LET sub = (
FOR b IN ${c2}
FILTER b.foo == a.foo
SORT b._key // For easier check in result
RETURN b
)
FOR c IN ${c3}
FILTER c.foo == a.foo
FILTER c.foo == sub[0].foo
RETURN {
a: a._key,
b: sub[*]._key,
c: c._key,
foos: {
a: a.foo,
c: c.foo,
b: sub[*].foo
}
}
`;
let c = db._query(q).toArray();
assertEqual(c.length, 10 * 5);
// For 10 A values we find 5 C values. Each sharing identical 5 B values.
let seen = new Set();
for (let d of c) {
// We found all 5 elements in subquery
assertEqual(d.b.length, 5);
assertEqual(d.foos.b.length, 5);
// Check that all a,b,c combinations are unique
let key = d.a + d.b + d.c;
assertFalse(seen.has(key)); // Every combination is unique
seen.add(key);
// Check that all foo values are correct
assertEqual(d.foos.a, d.foos.c);
for (let f of d.foos.b) {
assertEqual(d.foos.a, f);
}
// Increase that we found this specific value.
// We need to find exactly 5 of each
fooVals.set(d.foos.a, fooVals.get(d.foos.a) + 1);
}
assertEqual(fooVals.size, 10); // we have 10 different foos
for (let [key, value] of fooVals) {
assertEqual(value, 5, `Found to few of foo: ${key}`);
}
},
// Tests if all snippets are created correctly
// in subquery case if both root nodes
// end on Coordinator
testSnippetsRootCoordinator: function () {
let docsA = [];
let docsB = [];
let docsC = [];
let fooVals = new Map();
// We add 5 times each of the values 1 -> 10
for (let i = 1; i < 11; ++i) {
fooVals.set(`${i}`, 0);
docsA.push({foo: `${i}`, val: "baz"});
for (let j = 0; j < 5; ++j) {
docsB.push({foo: `${i}`, val: "bar"});
docsC.push({foo: `${i}`});
}
}
db[c1].save(docsA);
db[c2].save(docsB);
db[c3].save(docsC);
db[c4].save([
{_key: "a", val: "baz"},
{_key: "b", val: "bar"}
]);
let q = `
LET src = DOCUMENT("${c4}/a")
FOR a IN ${c1}
FILTER a.val == src.val
LET sub = (
LET src2 = DOCUMENT("${c4}/b")
FOR b IN ${c2}
FILTER b.foo == a.foo
FILTER b.val == src2.val
RETURN b
)
FOR c IN ${c3}
FILTER c.foo == a.foo
FILTER c.foo == sub[0].foo
RETURN {
a: a._key,
b: sub[*]._key,
c: c._key,
foos: {
a: a.foo,
c: c.foo,
b: sub[*].foo
}
}
`;
let c = db._query(q).toArray();
assertEqual(c.length, 10 * 5);
// For 10 A values we find 5 C values. Each sharing identical 5 B values.
let seen = new Set();
for (let d of c) {
// We found all 5 elements in subquery
assertEqual(d.b.length, 5);
assertEqual(d.foos.b.length, 5);
// Check that all a,b,c combinations are unique
let key = d.a + d.b + d.c;
assertFalse(seen.has(key)); // Every combination is unique
seen.add(key);
// Check that all foo values are correct
assertEqual(d.foos.a, d.foos.c);
for (let f of d.foos.b) {
assertEqual(d.foos.a, f);
}
// Increase that we found this specific value.
// We need to find exactly 5 of each
fooVals.set(d.foos.a, fooVals.get(d.foos.a) + 1);
}
assertEqual(fooVals.size, 10); // we have 10 different foos
for (let [key, value] of fooVals) {
assertEqual(value, 5, `Found to few of foo: ${key}`);
}
},
// Tests if we have a wierd
// combination of a subquery in a subquery
// and switching from Coordinator to DBServer
// all the time.
//
// General IDEA:
// We have a subquery node,
// where the "main query" contains a subquery
// and its "subquery" contains a subquery as well.
// Filters use the allowed amount of objects within
// the different scopes (e.g. in every subquery we
// validate that we still have access to the main
// query variables)
testSubqueryInception: function () {
let docsA = [];
let docsB = [];
let docsC = [];
let docsD = [];
let docsE = [];
let docsF = [];
let docsG = [];
let numDocs = 200;
for (let i = 0; i < numDocs; ++i) {
docsA.push({val: `A${i}`});
docsB.push({val: `B${i}`, valA: `A${i}`});
docsC.push({val: `C${i}`, valA: `A${i}`, valB: `B${i}`});
docsD.push({val: `D${i}`, valA: `A${i}`, valB: `B${i}`, valC: `C${i}`});
docsE.push({val: `E${i}`, valA: `A${i}`, valB: `B${i}`, valC: `C${i}`, valD: `D${i}`});
docsF.push({val: `F${i}`, valA: `A${i}`, valB: `B${i}`, valC: `C${i}`, valD: `D${i}`, valE: `E${i}`});
docsG.push({val: `G${i}`, valA: `A${i}`, valB: `B${i}`, valC: `C${i}`, valD: `D${i}`, valE: `E${i}`, valF: `F${i}`});
}
db[c1].save(docsA);
db[c2].ensureHashIndex("valA");
db[c2].save(docsB);
db[c3].ensureHashIndex("valA", "valB");
db[c3].save(docsC);
db[c4].ensureHashIndex("valA", "valB", "valC");
db[c4].save(docsD);
db[c5].ensureHashIndex("valA", "valB", "valC", "valD");
db[c5].save(docsE);
db[c6].ensureHashIndex("valA", "valB", "valC", "valD", "valE");
db[c6].save(docsF);
db[c7].ensureHashIndex("valA", "valB", "valC", "valF");
db[c7].save(docsG);
let q = `
FOR a IN ${c1}
LET s1 = (
FOR b IN ${c2}
FILTER b.valA == a.val
RETURN b
)
FOR c IN ${c3}
FILTER c.valB == s1[0].val
FILTER c.valA == a.val
LET s2 = (
FOR d IN ${c4}
FILTER d.valA == a.val
FILTER d.valB == s1[0].val
FILTER d.valC == c.val
LET s3 = (
FOR e IN ${c5}
FILTER e.valA == a.val
FILTER e.valB == s1[0].val
FILTER e.valC == c.val
FILTER e.valD == d.val
RETURN e
)
FOR f IN ${c6}
FILTER f.valA == a.val
FILTER f.valB == s1[0].val
FILTER f.valC == c.val
FILTER f.valD == d.val
FILTER f.valE == s3[0].val
RETURN f
)
FOR g IN ${c7}
FILTER g.valA == a.val
FILTER g.valB == s1[0].val
FILTER g.valC == c.val
FILTER g.valF == s2[0].val
RETURN g
`;
let c = db._query(q).toArray();
// We expect numDocs result documents
assertEqual(c.length, numDocs);
let seen = new Set();
for (let d of c) {
seen.add(d.val);
}
// Check that we have numDocs distinct ones
assertEqual(seen.size, numDocs);
for (let i = 0; i < numDocs; ++i) {
assertTrue(seen.has(`G${i}`));
}
}
};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(clusterSubqueriesTestSuite);
return jsunity.done();