그저 내가 되었고

🌱mongoDB:: relation 설정하기(populate 이용) 본문

개발/DB

🌱mongoDB:: relation 설정하기(populate 이용)

hyuunii 2022. 11. 26. 23:37

 

 

1. 데이터베이스(db)의 종류

https://hanamon.kr/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-sql-vs-nosql/

  • 관계형 데이터베이스 vs 비관계형 데이터베이스
  • SQL 기반 데이터베이스 vs. NoSQL 기반 데이터베이스
  • 관계형 데이터베이스 vs. NoSQL
  • SQL vs. NoSQL

 

 

2. 몽고db

  • NoSQL 기반 db
  • 그러므로 모델끼리의 관계를 직접 설정할 수 없음
  • 하지만 다른 documents들이 서로를 참조할 수 있게끔 설정하기는 가능
  • SQL에 Join 이 있다면, NoSQL에는 Schema Implant가 있는 것. SQL에서 테이블을 정의할 때 자료형을 정의하듯이, NoSQL에서는 서로 관계를 맺고 싶은 스키마를 다른 스키마에 삽입하면 됨.
  • populate 메소드를 사용할 것. populate method는 몽구스 ODM의 핵심 요소로, 몽고디비 컬렉션 간에 동적 관계 맺어주는 역할을 함.

 

 

3. 설정하고자 하는 부분의 관계

* 게시글에는 1인 1댓글만 작성 가능

* 게시글 글쓴이는 댓글러 차단 가능

* 댓글 get할때 차단된 사용자인지 아닌지 같이 보내달라고 함

* 차단된 사용자 여부는 post 테이블 참조하면 찾아올 수 있음(banUser column 존재)

 

 

4. 코드

1) 스키마 설정

* schema/comments.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema

const CommentsSchema = new mongoose.Schema({
  postId: {
    type: Schema.Types.ObjectId,
    ref: 'Posts',  //🌟참조할 모델명을 적음. reference 없이 스키마 이식하면 _id만 갖고옴. ref를 해야 해당_id의 포스트에 해당하는 모든 정보 갖고옴.
    required: true,
  },
  userId: {
    type: String,
  },
  nickName: {
    type: String,
  },
  birth: {
    type: String,
  },
  gender: {
    type: String,
  },
  myPlace: {
    type: Array,
  },
  comment: {
    type: String
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  },
});

module.exports = mongoose.model("Comments", CommentsSchema);

 

* repositories/comments.js

const Comments = require('../schema/comments');
const Posts = require('../schema/posts');

class CommentsRepository {
    //댓글 전체 목록 보기
    findAllComments = async (postId) => {
        const allCommentsData = await Comments
            .find({postId})
            .populate('postId') //🌟populate() 이용하여 _id에 해당하는 포스트의 정보 몽땅 불러오기.
                                //이때 ('postId', 'banUser')하면 해당하는 두개만 잘 불러와지는데 두개 넘어가면 그냥 오류나고 안찾아짐....
            .sort({updatedAt: -1});
        return allCommentsData;
    };

····
}
module.exports = CommentsRepository;

postman 실행 결과(http://localhost:3007/comments/637f504fe29cecfaa4d986ea);

{
    "comments": [
        {
            "_id": "63815b50ea21792854cf2b28",
            "postId": {
                "_id": "637f504fe29cecfaa4d986ea",
                "userId": "bbbb",
                "nickName": "원지2입니다",
                "title": "고인물 김지용님 찾습니다",
                "content": "고인물 김지용님 찾습니다",
                "location": {
                    "x": "126.927583207158",
                    "y": "37.4836907160217"
                },
                "cafe": "서울 관악구 남부순환로 1592",
                "date": "2022-11-29",
                "time": [
                    "2022-11-29T10:00:00.000Z",
                    "2022-11-29T14:00:00.000Z"
                ],
                "map": "관악구",
                "partyMember": 2,
                "participant": [
                    "원지입니다",
                    "1234",
                    "jade"
                ],
                "confirmMember": [
                    "원지2입니다"
                ],
                "banUser": [],
                "closed": 0,
                "expireAt": "2022-11-29T14:00:00.000Z",
                "createdAt": "2022-11-24T11:06:55.573Z",
                "updatedAt": "2022-11-24T11:06:55.573Z",
                "__v": 0
            },
            "userId": "jade",
            "nickName": "jade",
            "gender": "female",
            "myPlace": [
                "경북",
                "영주시"
            ],
            "comment": "댓글",
            "createdAt": "2022-11-26T00:18:24.672Z",
            "updatedAt": "2022-11-26T00:18:24.672Z",
            "__v": 0
        }
    ]
}

 

잘 불러와지긴 함. 근데 저만큼 다 필요 없고 banUser만 갖고 오면 되니까 특정 부분만 populate하는 문법으로 변경.

const Comments = require('../schema/comments');
const Posts = require('../schema/posts');

class CommentsRepository {
    //댓글 전체 목록 보기
    findAllComments = async (postId) => {
        const allCommentsData = await Comments
            .find({postId})
            .populate('postId', 'banUser')  //🌟populate() 이용하여 _id에 해당하는 포스트의 정보 몽땅 불러오기
            .sort({updatedAt: -1});
        return allCommentsData;
    };

····
}
module.exports = CommentsRepository;

↳postman 실행 결과(http://localhost:3007/comments/637f504fe29cecfaa4d986ea);

{
    "comments": [
        {
            "_id": "63815b50ea21792854cf2b28",
            "postId": {
                "_id": "637f504fe29cecfaa4d986ea",
                "banUser": [
                    "너"
                ]
            },
            "userId": "jade",
            "nickName": "jade",
            "gender": "female",
            "myPlace": [
                "경북",
                "영주시"
            ],
            "comment": "댓글",
            "createdAt": "2022-11-26T00:18:24.672Z",
            "updatedAt": "2022-11-26T00:18:24.672Z",
            "__v": 0
        }
    ]
}

 

 

5. 특정 댓글 작성자가 밴 유저인지 아닌지 조회하기

- 일단 populate 이용해서 댓글 조회하면서 해당 댓글이 달린 post의 postId 이용하여 post scheme의 banUser도 함께 가져오도록 한 상태.

- comment의 모든 데이터를 불러오면서 map을 돌려서 필요한 정보들을 추출하고, 거기다가 banOrNot을 추가함.

- 그러면 해당 comment 작성자의 nickName을 해당 comment가 작성된 포스트의 banUser목록과 비교하여 존재하는지 체크

- 존재하면(즉 ban된 user면) true, 존재하지 않으면(즉 ban된 유저가 아니면) false 반환.

 

* services/comments.js

const CommentsRepository = require('../repositories/comments');

class CommentsService {
    commentsRepository = new CommentsRepository();
    //댓글 전체 목록 보기
    findAllComments = async (postId) => {
        const findAllCommentResult = await this.commentsRepository.findAllComments(postId);
        const mapComments = findAllCommentResult.map((item) => {
            return {
                _id: item._id,
                postId: item.postId,
                userId: item.userId,
                nickName: item.nickName,
                gender: item.gender,
                myPlace: item.myPlace,
                comment : item.comment,
                createdAt: item.createdAt,
                updatedAt: item.updatedAt,
                banOrNot: item.postId.banUser.includes(item.nickName)  //🌟
            }
        })
        return mapComments;
    };
····
}
module.exports = CommentsService;

postman 실행 결과; http://localhost:3007/comments/637f504fe29cecfaa4d986ea

{
    "comments": [
        {
            "_id": "63822ec6e1699208d1d50187",
            "postId": {
                "_id": "637f504fe29cecfaa4d986ea",
                "banUser": [
                    "jade",
                    "kim"
                ]
            },
            "userId": "jade",
            "nickName": "뀽",
            "gender": "female",
            "myPlace": [
                "경북",
                "영주시"
            ],
            "comment": "11111댓글",
            "createdAt": "2022-11-26T15:20:38.073Z",
            "updatedAt": "2022-11-26T15:20:38.073Z",
            "banOrNot": false
        },
        {
            "_id": "63822ec4e1699208d1d50182",
            "postId": {
                "_id": "637f504fe29cecfaa4d986ea",
                "banUser": [
                    "jade",
                    "kim"
                ]
            },
            "userId": "jade",
            "nickName": "호호",
            "gender": "female",
            "myPlace": [
                "경북",
                "영주시"
            ],
            "comment": "11111댓글",
            "createdAt": "2022-11-26T15:20:36.868Z",
            "updatedAt": "2022-11-26T15:20:36.868Z",
            "banOrNot": false
        },
        {
            "_id": "63815b50ea21792854cf2b28",
            "postId": {
                "_id": "637f504fe29cecfaa4d986ea",
                "banUser": [
                    "jade",
                    "kim"
                ]
            },
            "userId": "jade",
            "nickName": "jade",
            "gender": "female",
            "myPlace": [
                "경북",
                "영주시"
            ],
            "comment": "댓글",
            "createdAt": "2022-11-26T00:18:24.672Z",
            "updatedAt": "2022-11-26T00:18:24.672Z",
            "banOrNot": true
        }
    ]
}