mongodb之图聚合查询 之图查询$graphLookup
官网的流程解释:
$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 thefrom
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 thestartWith
value against the field designated byconnectToField
in other documents in thefrom
collection. startWith来匹配from文档里的connectToField。只是第一轮。后边就不用startWith了 -
For each matching document,
$graphLookup
takes the value of theconnectFromField
and checks every document in thefrom
collection for a matchingconnectToField
value. For each match,$graphLookup
adds the matching document in thefrom
collection to an array field named by theas
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: <collection>,
startWith: <expression>,
connectFromField: <string>,
connectToField: <string>,
as: <string>,
maxDepth: <number>,
depthField: <string>,
restrictSearchWithMatch: <document>
}
}
//查入数据
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构成如下图。