mirror of https://gitee.com/bigwinds/arangodb
Bugfix of DFS path uniqueness implementation (#9041)
* Began work on generic traversal testing framework * Translate shard ids to smart attributes * Minor fixes * Added a TODO note * fixed tests * cleanup * first tests inside new traversal framework working * added single server diamond tests * Enable sharding in aql-graph-traversal-generic-graphs.js * Tried to fix smart sharding * Fixed smart graph name clashes, some cleanup * Added meta tests * Added TODO note * Fixed single server * Added a BFS order check * Renamed test * added more dfs and bfs tests for the openDiamond graph * Added new graph "largeBinTree" * Fix Map usage * added smallCircle graph, added three dfs tests * added smallCircle graph, added three dfs tests * more smallCircle dfs tests * Fixed largeBinTree shardings, and long graph names * Fix long suffixes * Provided names for shardings * Added a log message * more bfs smallCircle tests * more bfs smallCircle tests - global * smallCircle picture * added completeGraph to graph test suite * added more dfs tests for the complete graph up to depth 3 * Removed duplicate function definitions * added depth 2 dfs complete graph test, added missing test to the exports * Added test for largeBinTree * added three path graphs, easy, advanced and more advanced * Added a working check for bfs with global uniqueness * Fixed usage of bfs checks * Fixed expected input for two tests * Added some static assertions to largeBinTree * Oops * added more advancedPath dfs tests * better formatting of the visual graph * more dfs advanced graph tests * Implemented all easy path test combinations * Fixed testAqlGraphQuery * Fix in bfs check error reports * Removed duplicate test * Added advancedPath BFS tests * Updated comments * Fixed new tests * Fixed graph description * added more complete graph bfs tests * added more complete graph bfs tests * added testCompleteGraphBfsUniqueEdgesNoneD1 and testCompleteGraphBfsUniqueEdgesNoneD2 * added testCompleteGraphBfsUniqueVerticesUniqueEdgesPathD3 test * added a few more complete graph bfs checks * Extended a comment
This commit is contained in:
parent
3d697d8584
commit
5da9fcb0ce
|
@ -1,6 +1,11 @@
|
|||
devel
|
||||
-----
|
||||
|
||||
* Bugfix for smart graph traversals with uniqueVertices: path, which could
|
||||
sometimes lead to erroneous traversal results
|
||||
|
||||
* updated ArangoDB Starter 0.14.4
|
||||
|
||||
* fix a race in TTL thread deactivation/shutdown
|
||||
|
||||
* updated ArangoDB starter to 0.14.4
|
||||
|
|
|
@ -0,0 +1,760 @@
|
|||
/*jshint globalstrict:true, strict:true, esnext: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 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 Tobias Gödderz
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const internal = require("internal");
|
||||
const db = internal.db;
|
||||
const sgm = require("@arangodb/smart-graph");
|
||||
const cgm = require("@arangodb/general-graph");
|
||||
const _ = require("lodash");
|
||||
const assert = require("jsunity").jsUnity.assertions;
|
||||
|
||||
|
||||
const TestVariants = Object.freeze({
|
||||
SingleServer: 1,
|
||||
GeneralGraph: 2,
|
||||
SmartGraph: 3,
|
||||
});
|
||||
|
||||
class TestGraph {
|
||||
constructor (graphName, edges, eRel, vn, en, protoSmartSharding, testVariant, numberOfShards) {
|
||||
this.graphName = graphName;
|
||||
this.edges = edges;
|
||||
this.eRel = eRel;
|
||||
this.vn = vn;
|
||||
this.en = en;
|
||||
this.protoSmartSharding = protoSmartSharding;
|
||||
this.testVariant = testVariant;
|
||||
this.numberOfShards = numberOfShards;
|
||||
}
|
||||
|
||||
create() {
|
||||
switch (this.testVariant) {
|
||||
case TestVariants.SingleServer: {
|
||||
cgm._create(this.name(), [this.eRel], [], {});
|
||||
break;
|
||||
}
|
||||
case TestVariants.GeneralGraph: {
|
||||
const options = {numberOfShards: this.numberOfShards};
|
||||
cgm._create(this.name(), [this.eRel], [], options);
|
||||
break;
|
||||
}
|
||||
case TestVariants.SmartGraph: {
|
||||
const options = {
|
||||
numberOfShards: this.numberOfShards,
|
||||
smartGraphAttribute: ProtoGraph.smartAttr(),
|
||||
isSmart: true
|
||||
};
|
||||
sgm._create(this.name(), [this.eRel], [], options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.testVariant === TestVariants.SingleServer) {
|
||||
this.verticesByName = TestGraph._fillGraph(this.graphName, this.edges, db[this.vn], db[this.en]);
|
||||
} else {
|
||||
const shardAttrsByShardIndex = this._shardAttrPerShard(db[this.vn]);
|
||||
const vertexSharding = this.protoSmartSharding.map(([v, i]) => [v, shardAttrsByShardIndex[i]]);
|
||||
this.verticesByName = TestGraph._fillGraph(this.graphName, this.edges, db[this.vn], db[this.en], vertexSharding);
|
||||
}
|
||||
}
|
||||
|
||||
name() {
|
||||
return this.graphName;
|
||||
}
|
||||
|
||||
vertex(name) {
|
||||
return this.verticesByName[name];
|
||||
}
|
||||
|
||||
drop() {
|
||||
cgm._drop(this.name(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gn Graph Name
|
||||
* @param edges Array of pairs of strings, e.g. [["A", "B"], ["B", "C"]]
|
||||
* @param vc Vertex collection
|
||||
* @param ec Edge collection
|
||||
* @param vertexSharding Array of pairs, where the first element is the vertex
|
||||
* key and the second the smart attribute.
|
||||
* @private
|
||||
*/
|
||||
static _fillGraph(gn, edges, vc, ec, vertexSharding = []) {
|
||||
const vertices = new Map(vertexSharding);
|
||||
for (const edge of edges) {
|
||||
if (!vertices.has(edge[0])) {
|
||||
vertices.set(edge[0], null);
|
||||
}
|
||||
if (!vertices.has(edge[1])) {
|
||||
vertices.set(edge[1], null);
|
||||
}
|
||||
}
|
||||
|
||||
const verticesByName = {};
|
||||
for (const [vertexKey, smart] of vertices) {
|
||||
const doc = {key: vertexKey};
|
||||
if (smart !== null) {
|
||||
doc[ProtoGraph.smartAttr()] = smart;
|
||||
}
|
||||
verticesByName[vertexKey] = vc.save(doc)._id;
|
||||
}
|
||||
for (const edge of edges) {
|
||||
let v = verticesByName[edge[0]];
|
||||
let w = verticesByName[edge[1]];
|
||||
ec.save(v, w, {});
|
||||
}
|
||||
return verticesByName;
|
||||
}
|
||||
|
||||
_shardAttrPerShard(col) {
|
||||
const shards = col.shards();
|
||||
|
||||
// Create an array of size numberOfShards, each entry null.
|
||||
const exampleAttributeByShard = _.fromPairs(shards.map(id => [id, null]));
|
||||
|
||||
const done = () => !
|
||||
_.values(exampleAttributeByShard)
|
||||
.includes(null);
|
||||
let i = 0;
|
||||
// const key = this.enterprise ? ProtoGraph.smartAttr() : "_key";
|
||||
const key = "_key";
|
||||
while (!done()) {
|
||||
const value = this.enterprise ? i.toString() + ":" : i.toString() ;
|
||||
const doc = {[key]: value};
|
||||
|
||||
let shard;
|
||||
try {
|
||||
shard = col.getResponsibleShard(doc);
|
||||
} catch (e) {
|
||||
throw new Error('Tried to get shard for ' + JSON.stringify(doc) + ', original error: ' + e);
|
||||
}
|
||||
if (exampleAttributeByShard[shard] === null) {
|
||||
exampleAttributeByShard[shard] = value;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return _.values(exampleAttributeByShard);
|
||||
}
|
||||
}
|
||||
|
||||
class ProtoGraph {
|
||||
static smartAttr() { return "smart"; }
|
||||
|
||||
constructor (name, edges, generalShardings, smartShardings) {
|
||||
this.protoGraphName = name;
|
||||
this.edges = edges;
|
||||
this.generalShardings = generalShardings;
|
||||
this.smartShardings = smartShardings;
|
||||
}
|
||||
|
||||
name() {
|
||||
return this.protoGraphName;
|
||||
}
|
||||
|
||||
prepareSingleServerGraph() {
|
||||
const vn = this.protoGraphName + '_Vertex';
|
||||
const en = this.protoGraphName + '_Edge';
|
||||
const gn = this.protoGraphName + '_Graph';
|
||||
const eRel = cgm._relation(en, vn, vn);
|
||||
|
||||
return [new TestGraph(gn, this.edges, eRel, vn, en, [], TestVariants.SingleServer)];
|
||||
}
|
||||
|
||||
prepareGeneralGraphs() {
|
||||
return this.generalShardings.map(numberOfShards => {
|
||||
const suffix = `_${numberOfShards}shards`;
|
||||
const vn = this.protoGraphName + '_Vertex' + suffix;
|
||||
const en = this.protoGraphName + '_Edge' + suffix;
|
||||
const gn = this.protoGraphName + '_Graph' + suffix;
|
||||
|
||||
const eRel = cgm._relation(en, vn, vn);
|
||||
|
||||
return new TestGraph(gn, this.edges, eRel, vn, en, [], TestVariants.GeneralGraph, numberOfShards);
|
||||
});
|
||||
}
|
||||
|
||||
prepareSmartGraphs() {
|
||||
return this.smartShardings.map((sharding, idx) => {
|
||||
const {numberOfShards, vertexSharding} = sharding;
|
||||
const suffix = ProtoGraph._buildSmartSuffix(sharding, idx);
|
||||
|
||||
const vn = this.protoGraphName + '_Vertex' + suffix;
|
||||
const en = this.protoGraphName + '_Edge' + suffix;
|
||||
const gn = this.protoGraphName + '_Graph' + suffix;
|
||||
|
||||
const eRel = sgm._relation(en, vn, vn);
|
||||
|
||||
return new TestGraph(gn, this.edges, eRel, vn, en, vertexSharding, TestVariants.SmartGraph, numberOfShards);
|
||||
});
|
||||
}
|
||||
|
||||
static _buildSmartSuffix({numberOfShards, vertexSharding, name}, shardingIndex) {
|
||||
if (name) {
|
||||
return `_${name}`;
|
||||
}
|
||||
|
||||
// vertexSharding is an array of pairs, each pair holding a vertex (string)
|
||||
// and a shard (index), e.g. [["A", 0], ["B", 0], ["C", 1]].
|
||||
// For this (with 2 shards) this will return the string
|
||||
// "_2shards_s0-AB_s1-C"
|
||||
let suffix = `_${numberOfShards}shards_` +
|
||||
_.toPairs(
|
||||
_.groupBy(vertexSharding, ([,s]) => s)
|
||||
).map(
|
||||
([s, vs]) => 's' + s + '-' + vs.map(([v,]) => v).join('')
|
||||
).join('_');
|
||||
|
||||
// Override long suffixes
|
||||
if (suffix.length > 40) {
|
||||
suffix = `_shardingNr${shardingIndex}`;
|
||||
}
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const protoGraphs = {};
|
||||
|
||||
/*
|
||||
* B E
|
||||
* ↗ ↘ ↗
|
||||
* A D
|
||||
* ↘ ↗ ↘
|
||||
* C F
|
||||
*/
|
||||
protoGraphs.openDiamond = new ProtoGraph("openDiamond", [
|
||||
["A", "B"],
|
||||
["A", "C"],
|
||||
["B", "D"],
|
||||
["C", "D"],
|
||||
["D", "E"],
|
||||
["D", "F"],
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 1],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 1],
|
||||
["F", 1],
|
||||
],
|
||||
},
|
||||
{
|
||||
numberOfShards: 6,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3],
|
||||
["E", 4],
|
||||
["F", 5],
|
||||
],
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* B
|
||||
* ↗ ↘
|
||||
* A C
|
||||
* ↖ ↙
|
||||
* D
|
||||
*/
|
||||
protoGraphs.smallCircle = new ProtoGraph("smallCircle", [
|
||||
["A", "B"],
|
||||
["B", "C"],
|
||||
["C", "D"],
|
||||
["D", "A"]
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 0],
|
||||
["D", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 4,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3]
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* B
|
||||
* ↙↗ ↑ ↖↘
|
||||
* A ← → C // Same rules as in the picture for Node: "E"
|
||||
* ↖↘ ↓ ↙↗
|
||||
* D
|
||||
*/
|
||||
protoGraphs.completeGraph = new ProtoGraph("completeGraph", [
|
||||
["A", "B"],
|
||||
["A", "C"],
|
||||
["A", "D"],
|
||||
["A", "E"],
|
||||
["B", "A"],
|
||||
["B", "C"],
|
||||
["B", "D"],
|
||||
["B", "E"],
|
||||
["C", "A"],
|
||||
["C", "B"],
|
||||
["C", "D"],
|
||||
["C", "E"],
|
||||
["D", "A"],
|
||||
["D", "B"],
|
||||
["D", "C"],
|
||||
["D", "E"],
|
||||
["E", "A"],
|
||||
["E", "B"],
|
||||
["E", "C"],
|
||||
["E", "D"]
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 0],
|
||||
["D", 1],
|
||||
["E", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 5,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3],
|
||||
["E", 4]
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* A → B → C → D → E → F → G → H → I → J
|
||||
*
|
||||
*
|
||||
*/
|
||||
protoGraphs.easyPath = new ProtoGraph("easyPath", [
|
||||
["A", "B"],
|
||||
["B", "C"],
|
||||
["C", "D"],
|
||||
["D", "E"],
|
||||
["E", "F"],
|
||||
["F", "G"],
|
||||
["G", "H"],
|
||||
["H", "I"],
|
||||
["I", "J"]
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
["G", 0],
|
||||
["H", 0],
|
||||
["I", 0],
|
||||
["J", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 0],
|
||||
["D", 1],
|
||||
["E", 0],
|
||||
["F", 1],
|
||||
["G", 0],
|
||||
["H", 1],
|
||||
["I", 0],
|
||||
["J", 1]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 4,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3],
|
||||
["E", 0],
|
||||
["F", 1],
|
||||
["G", 2],
|
||||
["H", 3],
|
||||
["I", 0],
|
||||
["J", 1]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 1],
|
||||
["F", 1],
|
||||
["G", 1],
|
||||
["H", 1],
|
||||
["I", 0],
|
||||
["J", 0]
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
*
|
||||
* ┌───────────↴ ┌───────────↴
|
||||
* A → B → C → D → E → F → G → H → I
|
||||
*
|
||||
*
|
||||
*/
|
||||
protoGraphs.advancedPath = new ProtoGraph("advancedPath", [
|
||||
["A", "B"],
|
||||
["A", "D"],
|
||||
["B", "C"],
|
||||
["C", "D"],
|
||||
["D", "E"],
|
||||
["E", "F"],
|
||||
["E", "H"],
|
||||
["F", "G"],
|
||||
["G", "H"],
|
||||
["H", "I"],
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
["G", 0],
|
||||
["H", 0],
|
||||
["I", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 1],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
["G", 0],
|
||||
["H", 1],
|
||||
["I", 0],
|
||||
["J", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 4,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3],
|
||||
["E", 0],
|
||||
["F", 1],
|
||||
["G", 2],
|
||||
["H", 3],
|
||||
["I", 0],
|
||||
["J", 1]
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ┌───────────────────────┐(to G)
|
||||
* ├───────────↴ ┌──────┄↓┄──↴
|
||||
* A → B → C → D → E → F → G → H → I
|
||||
*
|
||||
*
|
||||
*/
|
||||
protoGraphs.moreAdvancedPath = new ProtoGraph("moreAdvancedPath", [
|
||||
["A", "B"],
|
||||
["A", "D"],
|
||||
["A", "G"],
|
||||
["B", "C"],
|
||||
["C", "D"],
|
||||
["D", "E"],
|
||||
["E", "F"],
|
||||
["E", "H"],
|
||||
["F", "G"],
|
||||
["G", "H"],
|
||||
["H", "I"],
|
||||
],
|
||||
[1, 2, 5],
|
||||
[
|
||||
{
|
||||
numberOfShards: 1,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 0],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
["G", 0],
|
||||
["H", 0],
|
||||
["I", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 2,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 0],
|
||||
["C", 0],
|
||||
["D", 1],
|
||||
["E", 0],
|
||||
["F", 0],
|
||||
["G", 0],
|
||||
["H", 1],
|
||||
["I", 0],
|
||||
["J", 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
numberOfShards: 4,
|
||||
vertexSharding:
|
||||
[
|
||||
["A", 0],
|
||||
["B", 1],
|
||||
["C", 2],
|
||||
["D", 3],
|
||||
["E", 0],
|
||||
["F", 1],
|
||||
["G", 2],
|
||||
["H", 3],
|
||||
["I", 0],
|
||||
["J", 1]
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Perfect binary tree of depth 8 (i.e. 9 levels).
|
||||
* Vertices get the names "v0", "v1", ..., "v510".
|
||||
* v0 is the root vertex. Edges are (v0, v1), (v0, v2), (v1, v3), ...
|
||||
* Contains 511 vertices and 510 edges.
|
||||
*/
|
||||
{ // scope for largeBinTree-local variables
|
||||
const numVertices = Math.pow(2, 9) - 1;
|
||||
const parentIdx = (i) => _.floor((i - 1) / 2);
|
||||
const vertices = _.range(0, numVertices)
|
||||
.map(i => `v${i}`);
|
||||
const edges = _.range(1, numVertices)
|
||||
.map(i => [`v${parentIdx(i)}`, `v${i}`]);
|
||||
|
||||
assert.assertEqual(511, vertices.length);
|
||||
assert.assertEqual(510, edges.length);
|
||||
assert.assertEqual('v0', vertices[0]);
|
||||
assert.assertEqual('v510', vertices[vertices.length - 1]);
|
||||
|
||||
const vi = (v) => Number(v.match(/^v(\d+)$/)[1]);
|
||||
const vertexLevel = (v) => Math.floor(Math.log2(vi(v)+1));
|
||||
const parent = (v) => 'v' + parentIdx(vi(v));
|
||||
const ancestor = (v, i) => i === 0 ? v : ancestor(parent(v), i-1);
|
||||
|
||||
// when splitting the tree into perfect subtrees of depth 3 (this is
|
||||
// non-ambiguous), these helper functions return, for a given vertex,
|
||||
// the level inside its subtree, and its subtrees root, respectively.
|
||||
const subTreeD3Level = (v) => vertexLevel(v) % 3;
|
||||
const subTreeD3Root = (v) => ancestor(v, subTreeD3Level(v));
|
||||
|
||||
// The subtree roots are all at (2^3)^n-1..2*(2^3)^n-1 (not including the last)
|
||||
// all together 73 subtrees (1 + 8 + 64)
|
||||
const subTreeD3Roots = [0, ..._.range(7, 15), ..._.range(63, 127)].map(i => `v${i}`);
|
||||
const subTreeD3RootToShardIdx = new Map(subTreeD3Roots.map((v, i) => [v, i]));
|
||||
|
||||
assert.assertEqual(73, subTreeD3Roots.length);
|
||||
|
||||
protoGraphs.largeBinTree = new ProtoGraph("largeBinTree",
|
||||
edges,
|
||||
[1, 2, 5],
|
||||
[
|
||||
{ // one shard
|
||||
name: "oneShard",
|
||||
numberOfShards: 1,
|
||||
vertexSharding: vertices.map(v => [v, 0]),
|
||||
},
|
||||
{ // one shard per three levels
|
||||
name: "oneShardPerThreeLevels",
|
||||
numberOfShards: 3,
|
||||
vertexSharding: vertices.map(v => [v, Math.floor(vertexLevel(v)/3)]),
|
||||
},
|
||||
{ // one shard per level
|
||||
name: "oneShardPerLevel",
|
||||
numberOfShards: 9,
|
||||
vertexSharding: vertices.map(v => [v, vertexLevel(v)]),
|
||||
},
|
||||
{ // alternating distribution of vertices
|
||||
name: "alternatingSharding",
|
||||
numberOfShards: 5,
|
||||
vertexSharding: vertices.map(v => [v, vi(v) % 5]),
|
||||
},
|
||||
{ // alternating sequence distribution of vertices
|
||||
name: "alternatingSequenceSharding",
|
||||
numberOfShards: 5,
|
||||
vertexSharding: vertices.map(v => [v, Math.floor(vi(v) / 11) % 5]),
|
||||
},
|
||||
{ // most vertices in 0, but for a diagonal cut through the tree:
|
||||
// v0
|
||||
// v1 (v2)
|
||||
// v3 (v4) v5 v6
|
||||
// v7 (v8) v9 v10 v11 v12 v13 v14
|
||||
// ...
|
||||
name: "diagonalCutSharding",
|
||||
numberOfShards: 2,
|
||||
vertexSharding: vertices.map(v => [v, [2,4,8,16,32,64,128,256].includes(vi(v)) ? 1 : 0]),},
|
||||
{ // perfect subtrees of depth 3, each in different shards
|
||||
name: "perfectSubtreeSharding",
|
||||
numberOfShards: 73,
|
||||
vertexSharding: vertices.map(v => [v, subTreeD3RootToShardIdx.get(subTreeD3Root(v))]),
|
||||
},
|
||||
{ // perfect subtrees of depth 3 as above, but divided in fewer shards
|
||||
name: "perfectSubtreeShardingWithFewShards",
|
||||
numberOfShards: 5,
|
||||
vertexSharding: vertices.map(v => [v, subTreeD3RootToShardIdx.get(subTreeD3Root(v)) % 5]),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
exports.ProtoGraph = ProtoGraph;
|
||||
exports.protoGraphs = protoGraphs;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
|||
/*jshint globalstrict:true, strict:true, esnext: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 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 Tobias Gödderz
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const {protoGraphs} = require('@arangodb/aql-graph-traversal-generic-graphs.js');
|
||||
const {testsByGraph, metaTests} = require('@arangodb/aql-graph-traversal-generic-tests.js');
|
||||
|
||||
const jsunity = require('jsunity');
|
||||
const console = require('console');
|
||||
const _ = require("lodash");
|
||||
|
||||
function graphTraversalGenericGeneralGraphClusterSuite() {
|
||||
let testGraphs = _.fromPairs(_.keys(protoGraphs).map(x => [x, {}]));
|
||||
_.each(protoGraphs, function (protoGraph) {
|
||||
_.each(protoGraph.prepareGeneralGraphs(), function (testGraph) {
|
||||
testGraphs[protoGraph.name()][testGraph.name()] = testGraph;
|
||||
});
|
||||
});
|
||||
|
||||
const suite = {
|
||||
setUpAll: function () {
|
||||
try {
|
||||
const numGraphs = _.sumBy(_.values(testGraphs), g => _.keys(g).length);
|
||||
console.info(`Creating ${numGraphs} graphs, this might take a few seconds.`);
|
||||
_.each(testGraphs, function (graphs) {
|
||||
_.each(graphs, function (graph) {
|
||||
graph.create();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e.stack);
|
||||
throw(e);
|
||||
}
|
||||
},
|
||||
|
||||
tearDownAll: function () {
|
||||
try {
|
||||
_.each(testGraphs, function (graphs) {
|
||||
_.each(graphs, function (graph) {
|
||||
graph.drop();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e.stack);
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_.each(metaTests, (test, testName) => {
|
||||
suite[testName] = test;
|
||||
});
|
||||
|
||||
_.each(testsByGraph, function (localTests, graphName) {
|
||||
let graphs = testGraphs[graphName];
|
||||
_.each(localTests, function (test, testName) {
|
||||
_.each(graphs, function (graph){
|
||||
suite[testName + '_' + graph.name()] = function () {
|
||||
test(graph);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
jsunity.run(graphTraversalGenericGeneralGraphClusterSuite);
|
||||
|
||||
return jsunity.done();
|
|
@ -0,0 +1,94 @@
|
|||
/*jshint globalstrict:true, strict:true, esnext: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2019 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 Tobias Gödderz
|
||||
/// @author Heiko Kernbach
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const {protoGraphs} = require('@arangodb/aql-graph-traversal-generic-graphs.js');
|
||||
const {testsByGraph, metaTests} = require('@arangodb/aql-graph-traversal-generic-tests.js');
|
||||
|
||||
const jsunity = require("jsunity");
|
||||
const _ = require("lodash");
|
||||
|
||||
function graphTraversalGenericGeneralGraphStandaloneSuite() {
|
||||
let testGraphs = _.fromPairs(_.keys(protoGraphs).map(x => [x, {}]));
|
||||
_.each(protoGraphs, function (protoGraph) {
|
||||
_.each(protoGraph.prepareSingleServerGraph(), function (testGraph) {
|
||||
testGraphs[protoGraph.name()][testGraph.name()] = testGraph;
|
||||
});
|
||||
});
|
||||
|
||||
const suite = {
|
||||
setUpAll: function () {
|
||||
try {
|
||||
const numGraphs = _.sumBy(_.values(testGraphs), g => _.keys(g).length);
|
||||
console.info(`Creating ${numGraphs} graphs, this might take a few seconds.`);
|
||||
_.each(testGraphs, function (graphs) {
|
||||
_.each(graphs, function (graph) {
|
||||
graph.create();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e.stack);
|
||||
throw(e);
|
||||
}
|
||||
},
|
||||
|
||||
tearDownAll: function () {
|
||||
try {
|
||||
_.each(testGraphs, function (graphs) {
|
||||
_.each(graphs, function (graph) {
|
||||
graph.drop();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(e.stack);
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_.each(metaTests, (test, testName) => {
|
||||
suite[testName] = test;
|
||||
});
|
||||
|
||||
_.each(testsByGraph, function (localTests, graphName) {
|
||||
let graphs = testGraphs[graphName];
|
||||
_.each(localTests, function (test, testName) {
|
||||
_.each(graphs, function (graph){
|
||||
suite[testName + '_' + graph.name()] = function () {
|
||||
test(graph);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
jsunity.run(graphTraversalGenericGeneralGraphStandaloneSuite);
|
||||
|
||||
return jsunity.done();
|
|
@ -626,28 +626,29 @@ function dumpTestEnterpriseSuite () {
|
|||
assertEqual(300, c.count());
|
||||
},
|
||||
|
||||
testAqlGraphQuery: function() {
|
||||
testAqlGraphQueryOutbound: function() {
|
||||
// Precondition
|
||||
let c = db[edges];
|
||||
const c = db[edges];
|
||||
assertEqual(300, c.count());
|
||||
// We first need the vertices
|
||||
let vC = db[vertices];
|
||||
const vC = db[vertices];
|
||||
assertEqual(100, vC.count());
|
||||
|
||||
let vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
let vertex = db._query(vertexQuery).toArray();
|
||||
const vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
const vertex = db._query(vertexQuery).toArray();
|
||||
assertEqual(1, vertex.length);
|
||||
|
||||
let q = `FOR v IN 1..2 ANY "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'} SORT TO_NUMBER(v.value) RETURN v`;
|
||||
/* We expect the following result:
|
||||
* 10 <- 9 <- 8
|
||||
const q = `FOR v IN 1..2 OUTBOUND "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'}
|
||||
SORT TO_NUMBER(v.value) RETURN v`;
|
||||
/* We expect the following paths:
|
||||
* 10 -> 9 -> 8
|
||||
* 10 <- 9
|
||||
* 10 -> 11
|
||||
* 10 -> 11 -> 12
|
||||
*/
|
||||
|
||||
//Validate that everything is wired to a smart graph correctly
|
||||
let res = db._query(q).toArray();
|
||||
const res = db._query(q).toArray();
|
||||
assertEqual(4, res.length);
|
||||
assertEqual("8", res[0].value);
|
||||
assertEqual("9", res[1].value);
|
||||
|
@ -655,6 +656,40 @@ function dumpTestEnterpriseSuite () {
|
|||
assertEqual("12", res[3].value);
|
||||
},
|
||||
|
||||
testAqlGraphQueryAny: function() {
|
||||
// Precondition
|
||||
const c = db[edges];
|
||||
assertEqual(300, c.count());
|
||||
// We first need the vertices
|
||||
const vC = db[vertices];
|
||||
assertEqual(100, vC.count());
|
||||
|
||||
const vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
const vertex = db._query(vertexQuery).toArray();
|
||||
assertEqual(1, vertex.length);
|
||||
|
||||
const q = `FOR v IN 1..2 ANY "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'}
|
||||
SORT TO_NUMBER(v.value) RETURN v.value`;
|
||||
/* We expect the following paths:
|
||||
* 10 -> 9 -> 8
|
||||
* 10 -> 9 <- 8
|
||||
* 10 <- 9 -> 8
|
||||
* 10 <- 9 <- 8
|
||||
* 10 <- 9
|
||||
* 10 -> 9
|
||||
* 10 -> 11
|
||||
* 10 <- 11
|
||||
* 10 -> 11 -> 12
|
||||
* 10 -> 11 <- 12
|
||||
* 10 <- 11 -> 12
|
||||
* 10 <- 11 <- 12
|
||||
*/
|
||||
|
||||
//Validate that everything is wired to a smart graph correctly
|
||||
const res = db._query(q).toArray();
|
||||
assertEqual('8 8 8 8 9 9 11 11 12 12 12 12'.split(' '), res);
|
||||
},
|
||||
|
||||
testSmartGraphSharding: function () {
|
||||
const eCol = db._collection(edges);
|
||||
const eProp = eCol.properties();
|
||||
|
|
|
@ -607,28 +607,29 @@ function dumpTestEnterpriseSuite () {
|
|||
assertEqual(300, c.count());
|
||||
},
|
||||
|
||||
testAqlGraphQuery: function() {
|
||||
testAqlGraphQueryOutbound: function() {
|
||||
// Precondition
|
||||
let c = db[edges];
|
||||
const c = db[edges];
|
||||
assertEqual(300, c.count());
|
||||
// We first need the vertices
|
||||
let vC = db[vertices];
|
||||
const vC = db[vertices];
|
||||
assertEqual(100, vC.count());
|
||||
|
||||
let vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
let vertex = db._query(vertexQuery).toArray();
|
||||
const vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
const vertex = db._query(vertexQuery).toArray();
|
||||
assertEqual(1, vertex.length);
|
||||
|
||||
let q = `FOR v IN 1..2 ANY "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'} SORT TO_NUMBER(v.value) RETURN v`;
|
||||
/* We expect the following result:
|
||||
* 10 <- 9 <- 8
|
||||
const q = `FOR v IN 1..2 OUTBOUND "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'}
|
||||
SORT TO_NUMBER(v.value) RETURN v`;
|
||||
/* We expect the following paths:
|
||||
* 10 -> 9 -> 8
|
||||
* 10 <- 9
|
||||
* 10 -> 11
|
||||
* 10 -> 11 -> 12
|
||||
*/
|
||||
|
||||
//Validate that everything is wired to a smart graph correctly
|
||||
let res = db._query(q).toArray();
|
||||
const res = db._query(q).toArray();
|
||||
assertEqual(4, res.length);
|
||||
assertEqual("8", res[0].value);
|
||||
assertEqual("9", res[1].value);
|
||||
|
@ -636,6 +637,40 @@ function dumpTestEnterpriseSuite () {
|
|||
assertEqual("12", res[3].value);
|
||||
},
|
||||
|
||||
testAqlGraphQueryAny: function() {
|
||||
// Precondition
|
||||
const c = db[edges];
|
||||
assertEqual(300, c.count());
|
||||
// We first need the vertices
|
||||
const vC = db[vertices];
|
||||
assertEqual(100, vC.count());
|
||||
|
||||
const vertexQuery = `FOR x IN ${vertices} FILTER x.value == "10" RETURN x._id`;
|
||||
const vertex = db._query(vertexQuery).toArray();
|
||||
assertEqual(1, vertex.length);
|
||||
|
||||
const q = `FOR v IN 1..2 ANY "${vertex[0]}" GRAPH "${smartGraphName}" OPTIONS {uniqueVertices: 'path'}
|
||||
SORT TO_NUMBER(v.value) RETURN v.value`;
|
||||
/* We expect the following paths:
|
||||
* 10 -> 9 -> 8
|
||||
* 10 -> 9 <- 8
|
||||
* 10 <- 9 -> 8
|
||||
* 10 <- 9 <- 8
|
||||
* 10 <- 9
|
||||
* 10 -> 9
|
||||
* 10 -> 11
|
||||
* 10 <- 11
|
||||
* 10 -> 11 -> 12
|
||||
* 10 -> 11 <- 12
|
||||
* 10 <- 11 -> 12
|
||||
* 10 <- 11 <- 12
|
||||
*/
|
||||
|
||||
//Validate that everything is wired to a smart graph correctly
|
||||
const res = db._query(q).toArray();
|
||||
assertEqual('8 8 8 8 9 9 11 11 12 12 12 12'.split(' '), res);
|
||||
},
|
||||
|
||||
testSmartGraphSharding: function () {
|
||||
const eCol = db._collection(edges);
|
||||
const eProp = eCol.properties();
|
||||
|
|
|
@ -38,7 +38,7 @@ const isEnterprise = require("internal").isEnterprise();
|
|||
* That has 100 orphans (value 0 -> 99)
|
||||
* That has 300 edges, for each value i:
|
||||
* Connect i -> i
|
||||
* Connect i - 1 -> i
|
||||
* Connect i - 1 <- i
|
||||
* Connect i -> i + 1
|
||||
*/
|
||||
const setupSmartGraph = function () {
|
||||
|
|
Loading…
Reference in New Issue