mongodb 설치, mongoose로 mongodb 연결 및 스키마 정의, create, read까지 정리한 글
mongodb 설치 mac 기준 homebrew로 편하게 ㄱㄱ
1 2 3 4 5 brew install mongodb sudo mkdir -p /data/db whoami sudo chown 터미널에 나온 사용자명 /data/db brew services start mongo
사용한 mongoose 버전은 5.6.8이다. 버전 앞이 바뀌엇구먼 허허
코드는 귀찬으니 mongoose 쪽 부분만.
연결 설정 연결은 4.X.X랑 아주 쪼금 바뀌엇다.
1 2 3 4 5 6 7 8 9 10 const mongoose = require ('mongoose' );mongoose.Promise = global .Promise; const mongodb = mongoose.connect('mongodb://localhost:27017/localtest' , { useNewUrlParser: true }); mongodb.then(() => { console .log('mongodb has been connected' ); }).catch((err ) => { console .log('mongodb connect err' , err); });
스키마 정의 스키마를 정의해두면 직접 db 안을 안 봐도 구조가 어떠케 생겨먹엇는지 알 수가 잇고
연결이 잘 되어 잇으면 실제 컬렉션에 데이터 삽입할 떄 정의한대로 컬렉션 생성을 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 'use strict' const mongoose = require ('mongoose' );const Schema = mongoose.Schema;const userSchema = new Schema({ userName: { type: String , required: [true , '아이디는 필수입니다.' ] }, userNum: { type: Number , required: [true , '등번호는 필수입니다.' ] }, position: String , userMail: String , user: { type: Schema.Types.ObjectId, ref: 'teams' }, createAt: { type: Date , default : Date .now() } }); module .exports = mongoose.model('users' , userSchema);
여전히 컬렉션을 생성 할 때 user 이런식으로 정의하면 복수형인 users로 자동 변환 된다.
스키마에서 쓸 수 잇는 데이터 타입은 정리를 해두자.
String = 문자열
Number = 숫자
Date = 날짜형
Boolean = true / false
Mixed = 잡종, 어떤 형이든 다 가능.
ObjectId = mongodb에서 쓰는 고유 아디 형식 같음.
Array = 배열
document 생성 mongodb는 하나의 document가 mysql에서 하나의 row와 같음.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const userModel = require ('../models/users' ); router.get('/create' , (req, res ) => { let user = new userModel({ userName: '크리스티아누 날강두' , userNum: 7 , position: '공격수' , team: '5d4c412caf60c707c225565d' , userMail: '날강두@유벤투스.com' }); user.save((err, result ) => { if (err) { res.json(err); } else { res.json(result); } }); });
실행하면 다음과 같은 결과를 볼 수 잇다.
1 2 3 4 5 6 7 8 9 10 { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 }
여러 개 document 생성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 router.get('/manyCreate' , (req, res ) => { let users = [ { userName: '모하메드 살라' , userNum: 11 , position: '공격수' , team: '5d4c412caf60c707c225565e' , userMail: '이집트메시@리버풀.com' }, { userName: '피에르에메릭 오바메양' , userNum: 17 , position: '공격수' , team: '5d4c412caf60c707c225565f' , userMail: '득점왕@아스날.com' }, { userName: '메수트 외질' , userNum: 11 , position: '미드필더' , team: '5d4c412caf60c707c225565f' , userMail: '노랑머리@아스날.com' } ]; userModel.insertMany(users, (err, result ) => { if (err) { res.json(err); } else { res.json(result); } }); });
배열 안에 object로 삽입할 데이터를 적어두고 insertMany를 호출하면 끝.
아 예전에 중간에 데이터가 빠져서 엿나.. 그 밖 여러 요인으로 인해 정상적인 처리가 안되엇을 때
컬렉션에 있는 데이터들이 아주 개판이 되엇던 적이 잇엇던 걸로 기억 한다 암튼 조심.
mongoose는 삽입하면 바로바로 뭐가 들어간지 리턴이 되서 참 편하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315e9" , "userName" : "모하메드 살라" , "userNum" : 11 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565e" , "userMail" : "이집트메시@리버풀.com" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315ea" , "userName" : "피에르에메릭 오바메양" , "userNum" : 17 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "득점왕@아스날.com" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315eb" , "userName" : "메수트 외질" , "userNum" : 11 , "position" : "미드필더" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "노랑머리@아스날.com" , "__v" : 0 } ]
아 그리고 아까 스키마에 정의 해놓은 required에 대한 처리는 어떻게 하는지 적어둔다.
새로 생성하는 document에 required를 걸어논 키의 데이터를 빼먹고 실행해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 router.get('/validation' , (req, res ) => { var user = new userModel({ userName: '해리 맥과이어' , userNum: null , position: '수비수' , userMail: '비싼몸@맨유.com' }); let validateErr = user.validateSync(); user.save((err, result ) => { if (err) { res.json(err); } else if (result && !validateErr) { res.json(result); } else { res.json(validateErr); } }); });
required에 걸리므로 에러를 할당 받는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { "errors" : { "userNum" : { "message" : "등번호는 필수입니다." , "name" : "ValidatorError" , "properties" : { "message" : "등번호는 필수입니다." , "type" : "required" , "path" : "userNum" , "value" : null }, "kind" : "required" , "path" : "userNum" , "value" : null } }, "_message" : "users validation failed" , "message" : "users validation failed: userNum: 등번호는 필수입니다." , "name" : "ValidationError" }
문서 읽기 1 2 3 4 5 6 7 8 9 10 router.get('/find' , (req, res ) => { userModel.find({}, (err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
find를 호출하면 된다 {} 안이 where 절이다. 다양한 조건을 걸 수가 이씀.
현재는 조건이 업으니 모두 출력 한다. 그리고 find류는 모두 Array타입으로 리턴 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315e9" , "userName" : "모하메드 살라" , "userNum" : 11 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565e" , "userMail" : "이집트메시@리버풀.com" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315ea" , "userName" : "피에르에메릭 오바메양" , "userNum" : 17 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "득점왕@아스날.com" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315eb" , "userName" : "메수트 외질" , "userNum" : 11 , "position" : "미드필더" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "노랑머리@아스날.com" , "__v" : 0 } ]
where 조건이 뭐뭐 잇는지 적어두자.
$eq는 ==
$gt는 >
$gte는 >=
$in은 Array타입 시 해당 데이터가 안에 포함되엇는지
$lt는 <
$lte는 <=
$ne는 !=
$nin은 Array타입 시 해당 데이터가 안에 포함 안되어잇는지
조건절은 다 일일히 적긴 귀찬으니까 하나만 예로.
1 2 3 4 5 6 7 8 9 10 11 12 router.get('/or' , (req, res ) => { userModel.find({ $or: [{ userName : '메수트 외질' }, { userName : '크리스티아누 날강두' }] }).exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
결과는 아래와 같음
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315eb" , "userName" : "메수트 외질" , "userNum" : 11 , "position" : "미드필더" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "노랑머리@아스날.com" , "__v" : 0 } ]
특정 필드만 받을 경우는 아래 처럼 하면 된다.
1 2 3 4 5 6 7 8 9 10 11 router.get('/find-attr' , (req, res ) => { userModel.find({}).select('userName userMail' ) .exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
아 sql의 select username, userMail from … 뭐 이런 거.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ { "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userMail" : "날강두@유벤투스.com" }, { "_id" : "5d4c42242a822207ecb315e9" , "userName" : "모하메드 살라" , "userMail" : "이집트메시@리버풀.com" }, { "_id" : "5d4c42242a822207ecb315ea" , "userName" : "피에르에메릭 오바메양" , "userMail" : "득점왕@아스날.com" }, { "_id" : "5d4c42242a822207ecb315eb" , "userName" : "메수트 외질" , "userMail" : "노랑머리@아스날.com" } ]
sql의 like 같은 기능이 mongo에선 regex다.
1 2 3 4 5 6 7 8 9 10 router.get('/regex' , (req, res ) => { userModel.find({ userName : { $regex : new RegExp ('날강두' , 'i' ) } }).exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
날강두가 포함한 이름을 검색하는건데 아직 자주 안써봐서 좀.. like문도 기억이 잘..
mongodb regex
필요할 땐 공식 문서를 번역기로 돌려보자 암튼 결과는 이러하다.
1 2 3 4 5 6 7 8 9 10 11 12 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 } ]
리턴 받는 개수를 정해서 받고 싶으면 아래 처럼 하면 된다.
1 2 3 4 5 6 7 8 9 10 router.get('/limit' , (req, res ) => { userModel.find({}).limit(2 ).exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
sql에서 select top 2 … 또는 select * … limit 2; 이런 거
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315e9" , "userName" : "모하메드 살라" , "userNum" : 11 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565e" , "userMail" : "이집트메시@리버풀.com" , "__v" : 0 } ]
조회 햇을 시 해당 숫자만큼 건너뛰려면 아래 처럼 하면 된다.
1 2 3 4 5 6 7 8 9 10 router.get('/skip' , (req, res ) => { userModel.find({}).skip(2 ).exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
글쎄 이거 페이징 구현할 때 말곤 써 본 기억이 업다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315ea" , "userName" : "피에르에메릭 오바메양" , "userNum" : 17 , "position" : "공격수" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "득점왕@아스날.com" , "__v" : 0 }, { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c42242a822207ecb315eb" , "userName" : "메수트 외질" , "userNum" : 11 , "position" : "미드필더" , "team" : "5d4c412caf60c707c225565f" , "userMail" : "노랑머리@아스날.com" , "__v" : 0 } ]
아 정렬이 업군
1 2 3 4 5 6 7 8 9 10 router.get('/sort' , (req, res ) => { userModel.find({}).sort({ id : -1 }).exec((err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
필드: 1이면 오름차순, 필드: -1이면 내림차순이다.
정렬 할 필드 기준이 여러 개면 {필드: 1, 필드: -1 …} 이런식으로 추가를 하면 되는데
정렬 순도 역시 적힌대로 앞에 꺼 부터 먼저 정렬 후 그 뒤에 다음 정렬임.
아 조건에 맞게 조회했을 때 첫 번째 문서만 조회하는 기능이 따로 잇다.
1 2 3 4 5 6 7 8 9 10 router.get('/findOne' , (req, res ) => { userModel.findOne({ userName : '크리스티아누 날강두' }, (err, data ) => { if (err) { res.json(err); } else { res.json(data); } }); });
특이점은 find류는 다 Array 안에 Object가 포함이지만 얘는 단독 Object로 넘어옴.
1 2 3 4 5 6 7 8 9 10 { "createAt" : "2019-08-08T15:37:45.028Z" , "_id" : "5d4c41cc2a822207ecb315e8" , "userName" : "크리스티아누 날강두" , "userNum" : 7 , "position" : "공격수" , "userMail" : "날강두@유벤투스.com" , "team" : "5d4c412caf60c707c225565d" , "__v" : 0 }
엇 글이 너무 길어진다. 나눠서 내일 2편을 작성 하자.