[java] MongoTemplate 을 이용한 Mongodb join 예 By starseat 2021-11-30 09:55:25 java/spring Post Tags mongodb를 사용하는 spring boot 기반 프로젝트 진행 중 join 에 대해서 rdb 와 달라 정리하고자 한다. mongodb 의 data는 다음과 같다. # Data ## [users] collection ``` { _id: ObjectId("612da21a9a29cfeea9a7a74d"), tntId: ObjectId("61276d4c7790a01da4bac0fd"), name: "아무개1", groupId: ObjectId("612d79f11e9f447e0e046ec8"), ... } ``` ## [group] collection ``` { _id: ObjectId("612d79f11e9f447e0e046ec4"), tntId: ObjectId("61276d4c7790a01da4bac0fd"), groupName: "임시그룹1" } ``` ## [route] collection ``` _id: ObjectId("612dc12ea45efb224a986ce5"), tntId: ObjectId("61276d4c7790a01da4bac0fd"), target: { route "test", list: \["612da21a9a29cfeea9a7a74d"\] // users._id 값들이 String 타입으로 추가됨. } ... } ``` 위에서 보는 것처럼 `users`, `group`, `target` 이라는 3개의 collection 이 있고 `target` 의 `target.list` 에는 `users._id` 값이 `String` 타입으로 추가되는 구조이다. `target.list` 를 기준으로 `users._id`, `users.name`, `group._id`, `group.groupName` 을 가져오는 `mongotemplate`을 사용한 `java` 소스는 다음과 같다. # MongoTemplate 을 사용한 java 소스 ```java public List getTargetViewList(String _id) { // mongodb routes 와 group 을 join // sql => select * from (select u.*, g.* from users u left outer join group g on u.groupId = g._id) agg_groups LookupOperation lookupOperation = LookupOperation.newLookup() .from("group") .localField("groupId") .foreignField("_id") .as("agg_groups"); // agg_groups 가 list 로 나와서 object 로 변환 // (user 의 group 은 1:1 매핑) // { // "_id": xxx, // "name": xxx, // "agg_groups: [ { ... } ] // => "agg_group": { ... } // } ProjectionOperation projectionOperation = Aggregation.project("agg_groups") .andInclude("_id") .andInclude("name") .and(ArrayOperators.ArrayElemAt.arrayOf("agg_groups").elementAt(0)).as("agg_group") ; // user 와 group을 합친 결과에서 // target list 에 있는 user._id 와 매치. 즉, in 절 추가 // sql => select * from ( agg_group ) where id in ( target_list ) List targetUserIdList = mongoTemplate.findById(_id, Target.class, "target").getList(); MatchOperation matchOperation = Aggregation.match(Criteria.where("_id").in(targetUserIdList)); // 위 형태에서 필요한 정보를 한 object 로 변경 // { // "user_id": xxx,Routeroute // "user_name": xxx, // "group_id": xxx, // "group_name: xxx // } ProjectionOperation convertAggGroupsOperation = Aggregation.project() .and("_id").as("user_id") .andExclude("_id") // _id 와 user_id 가 중복되므로 _id 는 제거 .and("name").as("user_name") .and("agg_group._id").as("group_id") .and("agg_group.groupName").as("group_name") ; // 조건을 순서대로 대입하여 mongodb 쿼리 생성 Aggregation aggregation = Aggregation.newAggregation( lookupOperation, projectionOperation, matchOperation, convertAggGroupsOperation ); // 기준이 되는 users 를 여기다가 넣기 return mongoTemplate.aggregate(aggregation, "users", HashMap.class).getMappedResults(); } ``` 위 소스 중 `Route.class`는 `route` collection 을 담기 위한 entity class 이다. ```java @Document("route") @AllArgsConstructor @NoArgsConstructor @Getter @Setter @Schema // swagger class Route{ @Id @JsonProperty(access = JsonProperty.Access.READ_ONLY) @Schema(description = "Object ID", required = true) private ObjectId _id; @Schema(description = "소속 테넌트 ID", required = true) private ObjectId tntId; private Target target; } @Getter @Setter @Schema // swagger class Target { private String kind; private List list; } ``` 이렇게 원하는 결과를 만들어 냈다. (이 글을 보는 분들 중 제 글에 잘못되거나 태클 꺼리가 보이실 경우 생각하시는 바가 맞을겁니다...) # 출처 * [SpringBoot에서 사용하기](https://jaehun2841.github.io/2019/02/24/2019-02-24-mongodb-3/#springboot%EC%97%90%EC%84%9C-mongodb-%EA%B0%84%EB%8B%A8%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0) Previous Post [java] java8 이상에서의 날짜 처리 Next Post [Effective Java 3/E] 01. 들어가기