Spring Boot 整合——MongoDB整合4(MongoDB分組去重以及MongoDB聯表查詢)
文章前面
關於版本
依賴 | 版本 |
---|---|
springboot | 2.0.8.RELEASE |
mongodb | 4.0.14 |
本內容只是為了介紹mongodb最基礎的使用以及配置,作為一個知名的資料庫,其存在相當多的高階用法,展開來介紹內容會相當多,當然本人並非相關領域的大神,下面內容只不過整理了自己日常使用的一些積累。是對自己經驗的積累,也希望能幫助後來的同學
關於專案
本內容也是我嘗試整理工作中接觸過各種工具在springboot中使用的方法。下面介紹的所有方法都已經提供了測試用例。因為每個例子涉及程式碼較多,所以文章中只貼出了部分程式碼。全部的程式碼在這裡:https://gitee.com/daifyutils/springboot-samples。
聯表查詢
簡單聯表查詢
mongodb多表查詢使用Aggregation.lookup
API進行操作。其方法lookup(String from,String localField,String foreignField,String as)
四個引數分別為需要連線的表、主表中的連線欄位、被連線表的欄位、以及連線後的資料被對映到結果集中的欄位。
下面是一個簡單的聯表查詢,將Order
表中的userId
和UserInfo
表中的id欄位進行關聯,查詢出的UserInfo
資訊儲存到結果集中的userInfo
欄位中。
@Override public List<OrderVo> queryOrderAndUserInfo() { Criteria criteria = new Criteria(); MatchOperation match = Aggregation.match(criteria); // 和另外一個集合進行關聯,將其作為其屬性 LookupOperation lookup = Aggregation.lookup("UserInfo",// 連線表 "userId",// 查詢表中欄位 "_id",// 連線表中欄位 "userInfo");// 返回資料所在的屬性名稱 TypedAggregation<Order> noRepeatAggregation2 = Aggregation.newAggregation(Order.class,match,lookup); AggregationResults<OrderVo> noRepeatDataInfoVos2 = mongoTemplate.aggregate(noRepeatAggregation2,OrderVo.class); List<OrderVo> noRepeatDataList2 = noRepeatDataInfoVos2.getMappedResults(); return noRepeatDataList2; }
聯表查詢-對被聯結的資料進行操作
當兩個表資料進行連線之後,資料被對映到某個屬性下面。其結構類似於下面格式。
{
"field":"value","field2":"value2","field3":"value3","被對映的屬性" : {
"field":"value","field2":"value2"
}
}
對於上面這種格式我們可以使用object.object
方式直接訪問被巢狀進來的資料,同時對其內容進行一些聚合或者排序或者篩選操作。
下面的例子就是在完成Order
和UserInfo
表關聯後,UserInfo
集合的資料被巢狀在了userInfo
屬性下,然後使用userInfo.type
UserInfo
的type
屬性,並根據其內容進行排序。
@Override
public List<OrderVo> queryOrderAndUserInfoSort(Sort.Direction direction) {
Criteria criteria = new Criteria();
MatchOperation match = Aggregation.match(criteria);
// 和另外一個集合進行關聯,將其作為其屬性
LookupOperation lookup = Aggregation.lookup("UserInfo","userId",// 連線表中欄位
"userInfo");// 返回資料所在的屬性名稱
SortOperation sort =
Aggregation.sort(new Sort(direction,"userInfo.type"));
TypedAggregation<Order> noRepeatAggregation2 =
Aggregation.newAggregation(Order.class,lookup,sort);
AggregationResults<OrderVo> noRepeatDataInfoVos2 = mongoTemplate.aggregate(noRepeatAggregation2,OrderVo.class);
List<OrderVo> noRepeatDataList2 = noRepeatDataInfoVos2.getMappedResults();
return noRepeatDataList2;
}
巢狀結構的展開
對於存在一對多關係的連線時,被連線的資料會被放在一個集合中,這個時候可以使用Aggregation.unwind
將資料展開。上面的例子新增下面的語句就是講UserInfos
欄位內容展開
Aggregation.unwind("UserInfos")
- 展開前的結構
{
"field":"value","UserInfos" : [{"field":"value1"},{"field":"value2"}]
}
- 展開後的結構
{
"field":"value","UserInfos" : {"field":"value1"}
}
{
"field":"value","UserInfos" : {"field":"value2"}
}
ps. 需要注意程式碼中我並沒有實現其測試程式碼。假如需要測試其展開效果,需要重新定義其返回結構物件。
聯表查詢-調整被聯結資料的名稱
有些時候我們使用多表關聯的時候使用的是單獨一層資料的物件來接收資料,在程式碼層面上巢狀的資料結構可能並不是太方便我們使用。這個時候我們可以配合上篇介紹的Aggregation.project
來改變其資料結構。上一篇中我介紹了其修改資料名稱能使用,下面就是其修改資料結構的方式。下面例子中將userInfo.userName
指向到userName
中,這樣操作後userName
作為一個巢狀資料的屬性在修改後會作為資料根節點展示出來。
@Override
public List<OrderAndUserVo> queryOrderUserInfo() {
Criteria criteria = new Criteria();
MatchOperation match = Aggregation.match(criteria);
// 和另外一個集合進行關聯,將其作為其屬性
LookupOperation lookup = Aggregation.lookup("UserInfo",// 連線表中欄位
"userInfo");// 返回資料所在的屬性名稱
Field userName = Fields.field("userName","userInfo.userName");
ProjectionOperation project = Aggregation.project("id","totalMoney","totalProduct","type")
.andInclude(Fields.from(userName));
TypedAggregation<Order> noRepeatAggregation2 =
Aggregation.newAggregation(Order.class,project);
AggregationResults<OrderAndUserVo> noRepeatDataInfoVos2 = mongoTemplate.aggregate(noRepeatAggregation2,OrderAndUserVo.class);
List<OrderAndUserVo> noRepeatDataList2 = noRepeatDataInfoVos2.getMappedResults();
return noRepeatDataList2;
}
分組去重
我們使用Aggregation.group
可以實現資料的分組,但是有些時候我們想知道在存在多種重複資料的時候不同查詢條件下重複資料去重後的內容。
比如我們將不同顧客(user)的訂單(order)分為不同的型別(type),現在我們想知道不同顧客到底存在幾種不同的型別時,通過將"userId"和"type"進行分組我們只能拿到不同使用者不同訂單型別的聚合結果。當然我們大可以在程式碼中迴圈其結果得到不同使用者(user)到底包含幾種(type)的數量。但這就導致邏輯被拆分到了資料查詢和程式程式碼中。
之前介紹過聚合管道其實是一個管道,後一個管道處理的資料是經過之前管道處理後的結果。要解決分組去重還是要使用其管道的特性。比如上面描述中我們通過"userId"和"type"進行分得到的結果是下面
userId | type | count |
---|---|---|
1 | 1 | 此userId此type包含資料量 |
1 | 2 | 此userId此type包含資料量 |
2 | 1 | 此userId此type包含資料量 |
2 | 2 | 此userId此type包含資料量 |
2 | 3 | 此userId此type包含資料量 |
… | … | … |
此時我們是無法知道每個userId到底存在多少個不同的type
。但是我們後續的管道可以對上面的資料進行再次分組。
我們再次分組,此時使用userId
進行資料的分組,最後拿到的count就是根據type值的總和,此時拿到的結果如下圖就是最終的結果。
userId | count | |
---|---|---|
1 | 此userId包含的type數量 | |
2 | 此userId包含的type數量 | |
… | … | … |
程式碼
上面的一套邏輯使用程式碼就是下面的內容。
@Override
public List<GroupVo> getAggGroupDeDuplication() {
// 根據使用者id和type進行分組
GroupOperation operation1 = Aggregation.group( "userId","type");
// 對上面的結果再次分組可以獲得userId的type資料
GroupOperation operation2 = Aggregation.group("userId").count().as("count");
TypedAggregation<Order> noRepeatAggregation =
Aggregation.newAggregation(Order.class,operation1,operation2);
AggregationResults<GroupVo> noRepeatDataInfoVos = mongoTemplate.aggregate(noRepeatAggregation,GroupVo.class);
List<GroupVo> noRepeatDataList = noRepeatDataInfoVos.getMappedResults();
System.out.println(JSON.toJSONString(noRepeatDataList));
return noRepeatDataList;
}
個人水平有限,上面的內容可能存在沒有描述清楚或者錯誤的地方,假如開發同學發現了,請及時告知,我會第一時間修改相關內容,也希望大家看在這個新春佳節只能宅到家中埋頭苦逼的碼程式碼的情況下,能給我點一個贊。你的點贊就是我前進的動力。在這裡也祝大家新春快樂。