1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
// In MongoDB 3.4, $graphLookup was introduced. In this file, we test basic behavior and correctness
// of the stage.
(function() {
"use strict";
var local = db.local;
var foreign = db.foreign;
local.drop();
foreign.drop();
var bulk = foreign.initializeUnorderedBulkOp();
for (var i = 0; i < 100; i++) {
bulk.insert({_id: i, neighbors: [i - 1, i + 1]});
}
assert.writeOK(bulk.execute());
assert.writeOK(local.insert({starting: 50}));
// Perform a simple $graphLookup and ensure it retrieves every result.
var res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
connectFromField: "neighbors",
connectToField: "_id",
as: "integers"
}
})
.toArray()[0];
assert.eq(res.integers.length, 100);
// Perform a $graphLookup and ensure it respects "maxDepth".
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
connectFromField: "neighbors",
connectToField: "_id",
maxDepth: 5,
as: "integers"
}
})
.toArray()[0];
// At depth zero, we retrieve one integer, and two for every depth thereafter.
assert.eq(res.integers.length, 11);
// Perform a $graphLookup and ensure it properly evaluates "startWith".
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: {$add: ["$starting", 3]},
connectFromField: "neighbors",
connectToField: "_id",
maxDepth: 0,
as: "integers"
}
})
.toArray()[0];
assert.eq(res.integers.length, 1);
assert.eq(res.integers[0]._id, 53);
// Perform a $graphLookup and ensure it properly expands "startWith".
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: {$literal: [1, 2, 3]},
connectFromField: "neighbors",
connectToField: "_id",
maxDepth: 0,
as: "integers"
}
})
.toArray()[0];
assert.eq(res.integers.length, 3);
// $graphLookup should not recurse when the 'connectFromField' is missing. However, if it
// mistakenly does, then it would look for a 'connectToField' value of null. In order to prevent
// regressions, we insert a document with a 'connectToField' value of null, then perform a
// $graphLookup, and ensure that we do not find the erroneous document.
assert.writeOK(foreign.remove({_id: 51}));
assert.writeOK(foreign.insert({_id: 51}));
assert.writeOK(foreign.insert({_id: null, neighbors: [50, 52]}));
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
connectFromField: "neighbors",
connectToField: "_id",
as: "integers"
}
})
.toArray()[0];
// Our result should be missing the values with _id from 52 to 99.
assert.eq(res.integers.length, 52);
// Perform a $graphLookup and ensure we don't go into an infinite loop when our graph is cyclic.
assert.writeOK(foreign.remove({_id: {$in: [null, 51]}}));
assert.writeOK(foreign.insert({_id: 51, neighbors: [50, 52]}));
assert.writeOK(foreign.update({_id: 99}, {$set: {neighbors: [98, 0]}}));
assert.writeOK(foreign.update({_id: 0}, {$set: {neighbors: [99, 1]}}));
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
connectFromField: "neighbors",
connectToField: "_id",
as: "integers"
}
})
.toArray()[0];
assert.eq(res.integers.length, 100);
// Perform a $graphLookup and ensure that "depthField" is properly populated.
res = local
.aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
connectFromField: "neighbors",
connectToField: "_id",
depthField: "distance",
as: "integers"
}
})
.toArray()[0];
assert.eq(res.integers.length, 100);
res.integers.forEach(function(n) {
assert.eq(n.distance, Math.abs(50 - n._id));
});
}());
|