官网的流程解释:
$graphLookup
Performs a recursive search on a collection, with options for restricting the search by recursion depth and query filter.
The $graphLookup
search process is summarized below:
Input documents flow into the $graphLookup
stage of an aggregation operation.
$graphLookup
targets the search to the collection designated by the from
parameter (see below for full list of search parameters).
For each input document, the search begins with the value designated by startWith
. 对每个输入的文档,先用startWith来匹配。
$graphLookup
matches the startWith
value against the field designated by connectToField
in other documents in the from
collection. startWith来匹配from文档里的connectToField。只是第一轮。后边就不用startWith了
For each matching document, $graphLookup
takes the value of the connectFromField
and checks every document in the from
collection for a matching connectToField
value. For each match, $graphLookup
adds the matching document in the from
collection to an array field named by the as
parameter.当startWith与from中的文档的connectToField匹配成功,就取connectFromField进行下一轮from里的connectToField进行匹配,一直递归循环,直到最大深度。
This step continues recursively until no more matching documents are found, or until the operation reaches a recursion depth specified by the maxDepth
parameter. $graphLookup
then appends the array field to the input document. $graphLookup
returns results after completing its search on all input documents. 设置最大递归层次。
综上进行如下流程:A join B join B join B join ... 其中A,B都是集合。是左外连接。也就是A中的文档会全部会留下来。
//集合A与集合B进行连接
for(docA in A){
dfs_join(docA.startWith, B, 0); //初始时使用startWith作为第一个值
}
//深度优先搜索
void dfs_join(docConnectFromField, B, deep){
if(deep > maxDeep){ //超过最大深度就退出
return;
}
for(docB in B){ //对B中的每个文档的connectToField与输入的docConnectFromField比较
if(docConnectFromField == docB.connectToField){
as.insert(docB); //匹配成功,保存dockB到as指定的数组里
dfs_join(docB.connectFromField, B, deep+1); //拿出docB的connectFromField继续进行匹配
}
}
}
{
$graphLookup: {
from: ,
startWith: ,
connectFromField: ,
connectToField: ,
as: ,
maxDepth: ,
depthField: ,
restrictSearchWithMatch:
}
}
//查入数据
db.mp.insertMany([
{"val":1, "name":"A", "tar":["B","C"]},
{"val":2, "name":"B", "tar":["D","E"]},
{"val":3, "name":"C", "tar":["E","F"]},
{"val":4, "name":"D", "tar":["F","A"]}
])
db.src.insertMany([
{"uname":"A", "age":28, "addr":"shenzheng"},
{"uname":"B", "age":30, "addr":"hangzhou"}
])
//startWith是src.uname==mp.name(connectToField), 匹配成功后,取mp.tar(connectFromField)再与mp中的每个文档的name(connectToField)进行匹配,匹配成功后,取mptar....
//聚合
db.src.aggregate([
{
"$match":{
"age":{
"$gte":20
},
"uname":{
"$exists":1
}
}
},{
"$graphLookup":{
"from":"mp",
"startWith":"$uname",
"connectFromField":"tar", //这里tar是个数组,那么就用每个元素分别进行匹配
"connectToField":"name",
"as":"next",
"maxDepth":10
}
},
{
"$project":{
"_id":0,
"uname":1,
"next.name":1,
"next.tar":1
}
}
])
//查询结果
{ "uname" : "A", "next" : [ { "name" : "B", "tar" : [ "D", "E" ] }, { "name" : "C", "tar" : [ "E", "F" ] }, { "name" : "A", "tar" : [ "B", "C" ] }, { "name" : "D", "tar" : [ "F", "A" ] } ] }
{ "uname" : "B", "next" : [ { "name" : "B", "tar" : [ "D", "E" ] }, { "name" : "C", "tar" : [ "E", "F" ] }, { "name" : "A", "tar" : [ "B", "C" ] }, { "name" : "D", "tar" : [ "F", "A" ] } ] }
注意,as(next)里的数据是没顺序的。最后uname=A的next构成如下图。