Skip to content

[DOC/CHAT] MongoDB 의 Query 작성에 대해서 #33

@Cassiiopeia

Description

@Cassiiopeia

💡 Description


SQL문을 작성하는것은 우리가 익히 익숙해서 잘 알고있을것을것인데요
스프링에서 지원하는 JPA를 사용하게되면

스프링에서 알아서 해당 DB에 맞는 쿼리문으로 변경시켜서 우리는 이를 고려하지않아도 되는 인터페이스로만 접근해도되는 장점이 있습니다.

하지만 JPA는 관계형 데이터베이스 기준으로 만들어져있습니다. MongoDB 같은 비관계형 데이터베이스에서는 완벽하게 지원하지 않기 때문에 복잡한 로직이 들어가면 @query를 이용해서 쿼리문을 직접 작성해야합니다.

이를 위해서 Mongo DB의 사용법의 기본쿼리문을 알필요가있습니다.

https://www.mongodb.com/docs/manual/

해당 주소는 몽고디비 공식문서입니다.

현재 ChatRoom 엔티티와 ChatMessage 엔티티는 다음과 같습니다.

    @Document
    public class ChatRoom {
      @Id
      private String chatRoomId;
      private Set<String> memberIdList;
      private String recipientId;
      private String lastChatMessageContent;
      private LocalDateTime lastChatMessageAt;
    }

.

    @Document
    public class ChatMessage {
      @Id
      private String chatMessageId;
      private String chatRoomId;
      private String senderId;
      private String recipientId;
      private String content;
      private List<String> imageUrlList;
      private LocalDateTime createdAt;
    }

이때 private Set memberIdList; 이부분에서
기본 RDMS에서 MemberId값이 Long이지만 String값으로 변환해서 저장하는 이유가 있습니다. 바로 통일성�과 일관성을 위해서인데요.

MongoDB는 기본적으로 문서의 ID를 ObjectId를 사용합니다. 이는 문서를 식별하는데 사용하는 12byte 바이너리 값입니다.

RDMS에서는 숫자타입을 기본키값으로 사용하는 반면
MongoDB에서는 일반적으로 문자열형태의 ID를 다룹니다.

그러므로 두 시스템간의 데이터를 연동하고 처리할때 일관성 있게 처리하기 위해
MongoDB에서는 ID를 다룰때 String형식을 사용하도록했습니다.
( Set으로 저장할시 오류가 발생하지는 않습니다. )

기본쿼리


find() : 문서 조회 

insert(): 문서 삽입

update(): 문서 수정

delete(): 문서 삭제

예시 기본 쿼리문


  • 특정 채팅방 -> 특정 Member가 보낸 ChatMessage 검색

      db.chatMessages.find({chatRoomId: "1_2", senderId: "1"})
    
  • 결과

      [
        {
          "_id": "asfhkwqwkj",
          "chatRoomId": "1_2",
          "senderId": "1",
          "recipientId": "2",
          "content": "기본 내용!",
          "imageUrlList": [],
          "createdAt": ISODate("2024-03-07T12:12:12.000Z")
        },
        ...
      ]
    

여기서 생길수있는 의문점 2가지가 있는데요

  1. 만든 Entity는 ChatMessage인데
    왜 ChatMessages 콜렉션에서 조회를 하는지?

-> Spring Data MongoDB는 엔티티 클래스의 이름을 기반으로 자동으로 컬렉션 이름을 결정합니다. 기본적으로 복수형인 ChatMessages가 됩니다.

이를 원치 않는경우 @document 어노테이션을 사용하여 명시적으로 지정할수 있습니다.
@document(collection = "chatMessage")

  1. 분명 ChatMessage의 Id값은 chatMessageId 인데 왜 MongoDB에서는 "_id"로 반환하는지?

-> MongoDB는 각 문서에 대해 자동으로 생성되는 고유 식별자를 "_id"필드에 할당합니다.

그러므로 @id 어노테이션을 통해 엔티티에 사용할 고유 식별자를 지정하면

ChatMessage 엔티티의 chatMessageId 필드는 "_id" 필드에 매핑됩니다.

Entity문서에 @id 어노테이션을 지정하지 않는경우 MongoDB가 자동으로 "_id"필드를 생성해서 관리합니다.

조건 연산자 사용


MongoDB 쿼리에서 조건 연산자를 사용할수있습니다.

    $gt (greater than, 초과)
    
    $lt (less than, 미만)
    
    $in (in, 포함되어 있는지)

조건 연산자 사용예시


    db.chatRooms.find({
      "memberIdList": { "$in": ["123", "456"] } 
    })

: 사용자 ID가 123과 456인 채팅방을 찾습니다.

    db.chatMessages.find({
      "chatRoomId": "123_456"
    })

: 채팅메시지에서 채팅방아이디가 "123_456"인 메시지를 찾습니다.

    db.chatRooms.find({
      "memberIdList": { "$in": ["123"] }
    }).sort({ "lastChatMessageAt": -1 })

: memberIdList안에 memberId가 "123"이 포함된 ChatRoom의 문서를 조회합니다.

    .sort({ "lastChatMessageAt": -1 }) 

-> 이부분은 lastChatMessageAt 의 내림차순으로 정렬합니다. ( 최신날짜가 먼저나오게 정렬합니다. )

이부분을 관계형 데이터베이스에서 JPA를 이용해서 구현하려면
findByMemberIdListContainsAndOrderByLastChatMessageAtDesc

를 사용해야합니다.

하지만 MongoDB에서 배열요소내의 검색기능이나 정렬하는 직접적인 방식은 SpringData MongoDB의 메소드 명명규칙으로는 한계가있으며 지원하지 않을 수 있습니다.

$in 연산자 사용이나 여러 문서에 대한 정렬기능을 사용할때는 @query 어노테이션을 사용해야합니다.

@Query(value = "{ 'memberIdList': ?0 }", sort = "{ 'lastChatMessageAt': -1 }")
List<ChatRoom> findByMemberIdSortedByLastChatMessageAtDesc(String memberId);

다음과 같이 사용할수있습니다.
?0 부분은 메소드의 매게변수 첫번째값인 memberId를 대입한다는 것을 의미합니다.


## 📚 References https://steemit.com/kr-dev/@kormanocorp/spring-boot-mongodb-query

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions