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構成如下圖。