{"id":18577606,"url":"https://github.com/pjt3591oo/mongodb-guide","last_synced_at":"2026-05-19T06:04:38.826Z","repository":{"id":100331952,"uuid":"467384733","full_name":"pjt3591oo/mongodb-guide","owner":"pjt3591oo","description":null,"archived":false,"fork":false,"pushed_at":"2022-03-11T05:34:59.000Z","size":28,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-16T01:34:04.416Z","etag":null,"topics":["mongodb","nosql"],"latest_commit_sha":null,"homepage":"https://blog.naver.com/pjt3591oo/222667237917","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pjt3591oo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-08T06:04:11.000Z","updated_at":"2022-03-11T05:35:02.000Z","dependencies_parsed_at":"2023-05-13T22:00:27.201Z","dependency_job_id":null,"html_url":"https://github.com/pjt3591oo/mongodb-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pjt3591oo/mongodb-guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjt3591oo%2Fmongodb-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjt3591oo%2Fmongodb-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjt3591oo%2Fmongodb-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjt3591oo%2Fmongodb-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pjt3591oo","download_url":"https://codeload.github.com/pjt3591oo/mongodb-guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pjt3591oo%2Fmongodb-guide/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265552946,"owners_count":23786992,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mongodb","nosql"],"created_at":"2024-11-06T23:29:44.869Z","updated_at":"2026-05-19T06:04:38.779Z","avatar_url":"https://github.com/pjt3591oo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mongodb \n\n### 구성요소\n\n1. 환경구성\n\n2. CRUD\n\n3. aggregate\n\n4. index\n\n5. dump, restore\n\n6. lock \u0026 transaction\n\n7. mapReduce\n\n## 준비\n\n* 컨테이너 생성\n\n```sh\n$ docker run --name mongodb.test.com -v data:/data/db -d -p 27017:27017 mongo\n```\n\n* 컨테이너 접속\n\n```sh\n$ docker exec -it mongodb.test.com bash\n\nroot@12sd924:/$ mongo\n```\n\n* 계정생성\n\n```\n\u003e use admin\n\n\u003e db.createUser({user: \"admin\", pwd: \"admin\", roles: [\"root\"]})\nSuccessfully added user: { \"user\" : \"admin\", \"roles\" : [ \"root\" ] }\n\n\u003e exit\n\n# 추가한 계정으로 접속\nroot@12sd924:/$ mongo -u admin -p admin\n```\n\n* connect url schema\n\n```\nmongodb://admin:admin@127.0.0.1:27017\n```\n\n## 디비쿼리 (CRUD)\n\n몽고디비 단위 database -\u003e collection(table) -\u003e document(row)\n\n* 데이터베이스 생성\n\n```\n\u003e use [데이터베이스 이름]\n```\n\n1개 이상의 collection이 있어야 데이터베이스 리스트에서 보임\n\n* 디비, 콜렉션 조회\n\n```\n\u003e show databases // 데이터베이스 리스트 조회\n\n\u003e show dbs // 데이터베이스 리스트 조회 \n\n\u003e db // 현재 사용중인 데이터베이스 확인\n\n\u003e db.stats() // 데이터베이스 정보 조회\n\n\u003e show collections // collection 조회\n```\n\n* collection(table) 생성\n\n```\n\u003e db.createCollection(name, [options])\n```\n\noptions는 해당 컬렉션의 설정값을 정의하는 객체. options는 다음과 같은 속성을 가진다.\n\n```js\n{\n  capped: \u003cboolean\u003e,\n  timeseries: {                  // Added in MongoDB 5.0\n    timeField: \u003cstring\u003e,        // required for time series collections\n    metaField: \u003cstring\u003e,\n    granularity: \u003cstring\u003e\n  },\n  expireAfterSeconds: \u003cnumber\u003e,\n  autoIndexId: \u003cboolean\u003e,\n  size: \u003cnumber\u003e,\n  max: \u003cnumber\u003e,\n  storageEngine: \u003cdocument\u003e,\n  validator: \u003cdocument\u003e,\n  validationLevel: \u003cstring\u003e,\n  validationAction: \u003cstring\u003e,\n  indexOptionDefaults: \u003cdocument\u003e,\n  viewOn: \u003cstring\u003e,              // Added in MongoDB 3.4\n  pipeline: \u003cpipeline\u003e,          // Added in MongoDB 3.4\n  collation: \u003cdocument\u003e,         // Added in MongoDB 3.4\n  writeConcern: \u003cdocument\u003e\n}\n```\n\n* collections 삭제\n\n```\n\u003e db.컬렉션이름.drop()\n```\n\n* collections 이름변경\n\n```\n\u003e db.컬렉션이름.renameCollection(\"바꿀 컬렉션 이름\")\n```\n\n* document(row) 생성\n\n```\n\u003e db.컬렉션이름.insert({})\n\n\u003e db.컬렉션이름.insert([{}, {}, {}])\n```\n\n```\n\u003e db.post.insert({title: \"title1\", author: \"mung1\"})\nWriteResult({ \"nInserted\" : 1 })\n\n\u003e db.post.insert({title: \"title2\", author: \"mung2\"})\nWriteResult({ \"nInserted\" : 1 })\n\n\u003e db.post.insert({title: \"title3\", author: \"mung3\"})\nWriteResult({ \"nInserted\" : 1 })\n\n\u003e db.post.insert({title: \"title3\", author: \"mung3\"})\nWriteResult({ \"nInserted\" : 1 })\n\n\u003e db.post.insert({title: \"title2\", author: \"mung2\"})\nWriteResult({ \"nInserted\" : 1 })\n\n\u003e db.post.insert({title: \"title1\", author: \"mung1\"})\nWriteResult({ \"nInserted\" : 1 })\n```\n\n* document(row) 조회\n\n```\n\u003e db.컬렉션이름.find(query, projection)\n\n\u003e db.컬렉션이름.find(query, projection).pretty()\n```\n\n```\n\u003e db.post.find()\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n\n\u003e db.post.find({}, {title: true})\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\" }\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\" }\n\n\u003e db.post.find({title: \"title1\"}, {title: true})\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\" }\n```\n\n* 비교 연산자 , 논리 연산자\n\n```\n$eq : (equals) 주어진 값과 일치하는 값\n$gt : (greater than) 주어진 값보다 큰 값\n$gte : (greather than or equals) 주어진 값보다 크거나 같은 값\n$lt : (less than) 주어진 값보다 작은 값\n$lte : (less than or equals) 주어진 값보다 작거나 같은 값\n$ne : (not equal) 주어진 값과 일치하지 않는 값\n$in : 주어진 배열 안에 속하는 값\n$nin : 주어빈 배열 안에 속하지 않는 값\n```\n\n```\n\u003e db.post.find( { likes: { $gt: 10, $lt: 30 } } )\n```\n\n```\n$or\n$and\n$not\n$nor\n```\n\n```\n\u003e db.post.find({ $or: [ { title: \"title2\" }, { title: \"title3\" } ] })\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n```\n\n* regexp\n\n다음과 같은 형태로 정규식을 이용할 수 있다.\n\n```\n{ \u003cfield\u003e: /pattern/\u003coptions\u003e }\n```\n\n```\n\u003e db.post.find( { \"title\" : /title[1-2]/ } )\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n```\n\n* where\n\nwhere절을 이용하면 javascript 표현식 사용가능\n\n```\n\u003e db.post.find( { $where: \"this.title === 'title1'\" } )\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n```\n\n* sort, limit, skip\n\nfind()는 cursor를 반환한다. 커서는 sort, limit, offset을 사용할 수 있다.\n\n```\n\u003e db.post.find().sort({_id: 1}) // 오름차순\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n\n\u003e db.post.find().sort({_id: -1}) // 내림차순\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n```\n\n```\n\u003e db.post.find().limit(2)\n{ \"_id\" : ObjectId(\"6226b56f32dbdd01d1ee9b10\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n{ \"_id\" : ObjectId(\"6226b57332dbdd01d1ee9b11\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n```\n\n```\n\u003e db.post.find().skip(2)\n{ \"_id\" : ObjectId(\"6226b57732dbdd01d1ee9b12\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57b32dbdd01d1ee9b13\"), \"title\" : \"title3\", \"author\" : \"mung3\" }\n{ \"_id\" : ObjectId(\"6226b57c32dbdd01d1ee9b14\"), \"title\" : \"title2\", \"author\" : \"mung2\" }\n{ \"_id\" : ObjectId(\"6226b57d32dbdd01d1ee9b15\"), \"title\" : \"title1\", \"author\" : \"mung1\" }\n```\n\n* document(row) 갯수 조회\n\n```\n\u003e db.post.find().count()\n```\n\n* document(row) 수정\n\n```\n\u003e db.collection.update( filter, update, options )\n```\n\n옵션은 다음과 같은 구조를 가진다.\n\n```js\n{\n  projection: \u003cdocument\u003e,\n  sort: \u003cdocument\u003e,\n  maxTimeMS: \u003cnumber\u003e,\n  upsert: \u003cboolean\u003e,\n  returnNewDocument: \u003cboolean\u003e,\n  collation: \u003cdocument\u003e,\n  arrayFilters: [ \u003cfilterdocument1\u003e, ... ]\n}\n```\n\n```\n\u003e db.post.update({author: \"mung1\"}, {author: \"mung-mung1\"}, {upsert: true})\n```\n\n* document(row) 삭제\n\n```\n\u003e db.컬렉션이름.remove(criteria, justOne)\n\n\u003e db.컬렉션이름.remove({}, true)\n```\n\njustOne이 true로 설정되면 criteria와 일치하는 데이터 하나만 삭제 false일 경우 전부 삭제 기본값은 false이다.\n\n## aggregate\n\n몽고디비의 연산지 및 stage는 공식문서에서 확인할 수 있다.\n\nhttps://docs.mongodb.com/manual/meta/aggregation-quick-reference\n\n* stage operator\n\n```\n$match\n\n$group\n\n$project\n\n$unwind\n\n$out, $lookup\n\n$bucket, $bucketAuto\n\n$sort, $sortByCount\n\n$limit, $skip\n\n$merge\n```\n\n* operator\n\n```\n$eq : (equals) 주어진 값과 일치하는 값\n$gt : (greater than) 주어진 값보다 큰 값\n$gte : (greather than or equals) 주어진 값보다 크거나 같은 값\n$lt : (less than) 주어진 값보다 작은 값\n$lte : (less than or equals) 주어진 값보다 작거나 같은 값\n$ne : (not equal) 주어진 값과 일치하지 않는 값\n$in : 주어진 배열 안에 속하는 값\n$nin : 주어빈 배열 안에 속하지 않는 값\n$exists : 주어진 값이 존재하는지 여부\n\n$or\n$and\n$not\n$nor\n```\n\n### 통계 집계\n\n$count, $max, $mix, $avg, $sum, $mul, $dateToString, $multiply, $accumulator\n\n```\n\u003e db.post.aggregate([\n  {$match: {title: \"title3\"}},\n])\n```\n\n$match는 조건에 맞는 데이터만 추출하는 역할을 한다.\n\n```\n\u003e  db.post.aggregate([  \n  {$group: {\n    _id: \"$author\",\n    count: { $count: { } }\n  }}\n])\n\n{ \"_id\" : \"mung3\", \"count\" : 2 }\n{ \"_id\" : \"mung55\", \"count\" : 5 }\n{ \"_id\" : \"mung555\", \"count\" : 5 }\n{ \"_id\" : \"mung4\", \"count\" : 8 }\n{ \"_id\" : \"mung2\", \"count\" : 2 }\n{ \"_id\" : \"mung5\", \"count\" : 5 }\n{ \"_id\" : \"mung1\", \"count\" : 2 }\n```\n\n_id를 기준으로 그룹핑 작업을 진행한다. $컬럼명 형태로 조회된 document(row)에서 컬럼을 접근할 수 있다. \"$컬럼\"은 조회 결과에서 해당 컬럼값을 가져오는 것을 의미한다.\n\n$count는 document(row)의 수를 카운팅한다.\n\n$group은 다음과 같은 포맷으로 작성한다.\n\n***`{결과컬럼: 연산: 연산대상}`***\n\n```\n\u003e  db.post.aggregate([  \n  {$group: {\n    _id: \"$author\",\n    count: { $count: { } }\n  }},\n  {\n    $match: { \"count\": { $gte: 5 } }\n  }\n])\n\n{ \"_id\" : \"mung55\", \"count\" : 5 }\n{ \"_id\" : \"mung4\", \"count\" : 8 }\n{ \"_id\" : \"mung555\", \"count\" : 5 }\n{ \"_id\" : \"mung5\", \"count\" : 5 }\n\n\u003e db.post.aggregate([  \n  {$group: {\n    _id: {author: \"$author\"},\n    authorCount: { $count: { } }\n  }},\n  {\n    $match: { \"authorCount\": { $gte: 5 } }\n  }\n])\n\n{ \"_id\" : { \"author\" : \"mung55\" }, \"authorCount\" : 5 }\n{ \"_id\" : { \"author\" : \"mung4\" }, \"authorCount\" : 8 }\n{ \"_id\" : { \"author\" : \"mung555\" }, \"authorCount\" : 5 }\n{ \"_id\" : { \"author\" : \"mung5\" }, \"authorCount\" : 5 }\n```\n\n$group는 _id에 여러 컬럼을 명시하여 여러 컬럼을 기준으로 그룹핑이 가능하다.\n\n```\n\u003e db.post.aggregate([  \n  {$group: {\n    _id: \"$author\",\n    count: { $count: { } }\n  }},\n  {\n    $match: { \"count\": { $gte: 5 } }\n  },\n  {\n    $sort : { count: -1 }\n  }\n])\n\n{ \"_id\" : \"mung4\", \"count\" : 8 }\n{ \"_id\" : \"mung55\", \"count\" : 5 }\n{ \"_id\" : \"mung555\", \"count\" : 5 }\n{ \"_id\" : \"mung5\", \"count\" : 5 }\n```\n\n```\n\u003e db.post.aggregate([  \n  {$group: {\n    _id: {author: \"$author\"},\n    count: { $count: { } }\n  }},\n  {\n    $match: { \"count\": { $gte: 5 } }\n  }\n]).map(item =\u003e item.count * 10)\n\n[\n\t{\n\t\t\"count\" : 50\n\t},\n\t{\n\t\t\"count\" : 50\n\t},\n\t{\n\t\t\"count\" : 80\n\t},\n\t{\n\t\t\"count\" : 50\n\t}\n]\n```\n\nsql을 aggregate 표현하는 것은 아래와 같다.\n\nhttps://docs.mongodb.com/manual/reference/sql-aggregation-comparison/\n\n몽고디비는 BSON(Binary JSON) 형태로 row를 관리한다.\n\n## 인덱스\n\n인덱스타입: 고유인덱스, 희소인덱스, 다중키 인덱스, 해시 인덱스, 지리 공간적 인덱스, 단일컬럼 인덱스\n\n* 단일컬럼인덱스(Single Field Index)\n\n```\n\u003e db.콜렉션이름.createIndex({컬럼명: 정렬방향})\n```\n\n```\n\u003e db.post.createIndex({title: 1})\n```\n\n1은 오름차순, -1은 내림차순\n\n* 복합인덱스(Compound Index)\n\n```\n\u003e db.콜렉션이름.createIndex({컬럼명: 정렬방향, 컬럼명: 정렬방향})\n```\n\n복합 인덱스 시 주의사항\n\n```\n\u003e db.user.createIndex({userid:1, score:-1})\n\n# 실행 시 문제 없이 진행 \n\u003e db.user.find({}).sort({ userid:1,score:-1})\n\u003e db.user.find({}).sort({ userid:-1,score:1})\n\n# RAM exceeded error 발생\n\u003e db.user.find({}).sort({ userid:1,score:1})\n\u003e db.user.find({}).sort({ score:-1,userid:1})\n```\n\n복합 인덱스는 정렬 시 다른 정렬방향을 사용할 경우 문제발생\n\n* 고유 인덱스(Unique Index)\n\n```\n\u003e db.post.createIndex({컬럼명: 정렬방향}, {unique: true})\n```\n\n특정 필드가 유니크한 속성을 추가함\n\n* 해시 인덱스(Hashed Index)\n\n```\n\u003e db.post.createIndex({컬럼명: \"hashed\"})\n```\n\n해시 된 샤드 키를 사용하여 샤딩 컬렉션을 지원\n\n필드의 해시 인덱스를 샤딩된 클러스터에서 데이터를 분할하기 위한 샤딩키로 사용\n\n해시 된 샤드 키를 사용해서 컬렉션을 샤딩하면 데이터를 더 고르게 분산\n\n다중 키 해시 인덱스는 허용하지 않음\n\n* 희소 인덱스(Sparse Index)\n\n```\n\u003e db.post.createIndex({컬럼명:1}, {sparse: true, unique: false})\n```\n\nnull값을 가질 수 있는 엔티티가 있을 때\n\n많은 document가 인덱스 키를 가지고 있지 않은데 키를 가진 대상으로 질의를 하는경우\n\n\n* 인덱스 조회\n\n```\n\u003e db.post.getIndexes()\n[\n\t{\n\t\t\"v\" : 2,\n\t\t\"key\" : {\n\t\t\t\"_id\" : 1\n\t\t},\n\t\t\"name\" : \"_id_\"\n\t},\n\t{\n\t\t\"v\" : 2,\n\t\t\"key\" : {\n\t\t\t\"title\" : 1\n\t\t},\n\t\t\"name\" : \"title_1\"\n\t},\n\t{\n\t\t\"v\" : 2,\n\t\t\"key\" : {\n\t\t\t\"title\" : -1\n\t\t},\n\t\t\"name\" : \"title_-1\"\n\t}\n]\n```\n\n* 인덱스에 대한 통계정보 조회\n\n```\n\u003e db.post.aggregate( [ { $indexStats: { } } ] ).pretty()\n\n{\n\t\"name\" : \"_id_\",\n\t\"key\" : {\n\t\t\"_id\" : 1\n\t},\n\t\"host\" : \"040bcda7f541:27017\",\n\t\"accesses\" : {\n\t\t\"ops\" : NumberLong(4),\n\t\t\"since\" : ISODate(\"2022-03-08T01:05:34.892Z\")\n\t},\n\t\"spec\" : {\n\t\t\"v\" : 2,\n\t\t\"key\" : {\n\t\t\t\"_id\" : 1\n\t\t},\n\t\t\"name\" : \"_id_\"\n\t}\n}\n```\n\n* 쿼리결과 분석\n\n```\n\u003e db.post.find({author: \"mung3\"}).explain(\"executionStats\").executionStats\n```\n\nexplain(\"queryPlanner\"): 가장 효율적인 쿼리를 찾기 위해 쿼리 최적화를 제공합니다.\n\nexplain(\"executionStats\"): 특정 질의에서 실제로 실행한 결과의 세부사항을 제공합니다.\n\nexplain(\"allPlansExecution mode\"): queryPlanner + executionStats 내용 모두 포함\n\n***`totalDocsExamined`***는 참조된 document 횟수를 의미한다.\n\n***`executionStages`***는 쿼리 수행 상세 정보를 의미한다. 상세 정보는 여러 단계에 나눠 진행되는 쿼리의 상세를 표시한다. executionStages의 stage는 ***COLLSCAN***, ***IXSCAN***이 있으며 ***COLLSAN***은 전체스캔, IXSCAN은 인덱스 스캔을 의미한다.\n\n* 인덱스 사이즈\n\n인덱스도 저장 공간을 차지함\n\n```\n\u003e db.[콜렉션이름].totalIndexSize()\n```\n\n* 인덱스 제거\n\n```\n\u003e db.[콜렉션이름].dropIndex(필드이름)\n```\n\n\n* 주의사항\n\n```\n1. 한 컬렉션에 2~3개의 인덱스를 가지지 않는것이 좋다. 만약 여러 컬럼의 쿼리라면 복합 인덱스를 사용\n\n2. 최적화는 스캔(참조)한 document 수를 줄임으로써 가능. explain()의 nscanned 확인\n\n3. 인덱스 구축이 완료될 때까지 데이터베이스는 모든 read/write 작업중단한다. background 옵션을 이용한다면 인덱스 작업을 백그라운드에서 동작시킬 수 있다.\n```\n\n* 인덱스 작동방식\n\nindex prefix, index intersection\n\nindex prefix란 왼쪽 인덱스부터 적용되는 부분집합 인덱스를 의미\n\n```\n생성된 인덱스:  { \"item\": 1, \"location\": 1, \"stock\": 1 }\n```\n\n만약 item, location, stock이 인덱스로 적용되는 경우\n\n```\n- 지원되는 쿼리: { item: 1 }\n- 지원되는 쿼리: { item: 1, location: 1 }\n\n- 지원하지 않는 조회 쿼리: \"item\" 필드 없이 \"location\" 필드만 존재 혹은 \"stock\" 필드만 존재 \n- 지원하지 않는 조회 쿼리: \"item\" 필드 없이 \"location\", \"stock\" 필드만 존재 \n```\n\nitem이 없을경우 location, stock은 인덱싱이 적용되지 않는다.\n\nsort 연산은 prefix 조건에 맞지 않아도 지원함. 하지만 find에 포함하는 쿼리는 equality 조건에서 prefix를 포함해야 함\n\n```\n생성된 인덱스: { a:1, b: 1, c: 1, d: 1 }\n```\n\na, b, c, d 인덱스가 생성되었을 경우\n\n```\n\u003e db.data.find( { a: { $gt: 2 } } ).sort( { c: 1 } ) # 인덱스 적용 X: find(쿼리)쿼리에 equality 조건이 없음\n\u003e db.data.find( { c: 5 } ).sort( { c: 1 } )          # 인덱스 적용 X: find(쿼리)쿼리에 prefix 만족하지 않음\n```\n\n다음으로 index intersection이 있다. 인덱스가 교차해서 쿼리에 자동으로 적용되는 것을 의미한다.\n\n```\n- 인덱스 1: { qty: 1 }\n- 인덱스 2: { item: 1 }\n\n\u003e db.orders.find( { item: \"abc123\", qty: { $gt: 15 } } )\n```\n\nindex prefix는 복합 인덱스에서 동작하지만 index intersection은 단일 인덱스에서 적용된다. index intersection이 이루어지면 explain() 결과에서 AND_SORTED 또는 AND_HASH를 발견할 수 있다. 이 둘이 존재한다면 교차 인덱스가 작동된것이다.\n\n## 백업(dump), 복구(restore)\n\n* 전체백업\n\n```bash\n$ mongodump --out [dump data path] --host 127.0.0.1 --port 27017 -u [username] -p [password]\n```\n\n* 특정 디비 백업\n\n```bash\n$ mongodump --out [dump data path] --host 127.0.0.1 --port 27017 -u [username] -p [password] --db [덤프할 db명]\n```\n\n* 특정 디비의 컬렉션 백업\n\n```bash\n$ mongodump --out [dump data path] --host [dbhost] --port 27017 -u [username] -p [password] --db [dbname] --collection [collectionName]\n```\n\n* example\n\n```bash\n$ mongodump --out t --host 127.0.0.1 --port 27017 -u admin -p admin\n\n2022-03-08T06:11:03.507+0000\twriting admin.system.users to t/admin/system.users.bson\n2022-03-08T06:11:03.509+0000\tdone dumping admin.system.users (1 document)\n2022-03-08T06:11:03.509+0000\twriting admin.system.version to t/admin/system.version.bson\n2022-03-08T06:11:03.511+0000\tdone dumping admin.system.version (2 documents)\n2022-03-08T06:11:03.512+0000\twriting testdb.post to t/testdb/post.bson\n2022-03-08T06:11:03.514+0000\tdone dumping testdb.post (29 documents)\n2022-03-08T06:11:03.515+0000\twriting testdb.collections to t/testdb/collections.bson\n2022-03-08T06:11:03.517+0000\tdone dumping testdb.collections (2 documents)\n\n$ ls\nt\n\n$ cd t\n$ ls\nadmin  testdb\n\n$ cd testdb\n$ ls\ncollections.bson  collections.metadata.json  post.bson  post.metadata.json\n```\n\n* 복구(restore) 명령어 구조\n\n```bash\n$ mongorestore --host 127.0.0.1 --port 27017 -u [username] -p [password --drop [drop db name] --db [복구할 db name] [복구할 덤프데이터가 있는 디렉토리]\n```\n\n* 전체 복구(restore)\n\n```bash\n$ mongorestore --host 127.0.0.1 --port 27017 [dump data가 있는 디렉토리]\n```\n\n* 특정 컬렉션 복구(restore)\n\n```bash\n$ mongorestore --host \u003cdbhost\u003e --port 27017 --db [dbname] --collection [collectionName] [data-dump-path/dbname/collection.bson] --drop [drop db name]\n```\n\n컬렉션 단위로 복구하기 위해 --collection 옵션을 사용하여 collection.bson까지 경로를 입력해야한다.\n\n## 잠금(lock) 및 트랜잭션(Transaction)\n\n몽고디비는 2가지 잠금이 존재.\n\n1. 명시적 잠금\n\n2. 묵시적 잠금: 데이터베이스와 컬렉션을 잠그는 목적으로 사용. 쿼리 수행시 자동으로 잠금을 획득하고 반환함\n\n* 잠금수행\n\n```\n\u003e db.fsyncLock({ fsync: 1, lock: true })\n{\n\t\"info\" : \"now locked against writes, use db.fsyncUnlock() to unlock\",\n\t\"lockCount\" : NumberLong(1),\n\t\"seeAlso\" : \"http://dochub.mongodb.org/core/fsynccommand\",\n\t\"ok\" : 1\n}\n```\n\n잠긴 상태에서 insert 및 update를 수행하면 대기상태가 된다.\n\n```\n\u003e db.post.insert({title: \"title123\", author: \"mung123\" })\n```\n\n* 잠금상태 확인\n\n```\n\u003e db.currentOp()\n```\n\n* 잠금풀기\n\n```\n\u003e db.fsyncUnlock()\n{ \"info\" : \"fsyncUnlock completed\", \"lockCount\" : NumberLong(1), \"ok\" : 1 }\n```\n\n* 엔진에 따른 잠금개념\n\n몽고디비는 2가지 형태의 스토리지를 제공한다\n\n```\nMMAPv1 스토리지 : collection 수준의 잠금 제공\n\nWiredTiger 스토리지: document 수준 잠금 제공\n```\n\n* 트랜잭션\n\n\n몽고디비는 트랜잭션이 주된 목적은 아닙니다. MMAPv1 스토리지 엔진은 트랜잭션을 사용하지 않는다. \n\n4.x에서 WiredTiger 스토리지 엔진이 추가되면서 스냅샷 기반인 Repeatable-Read 기반의 트랜잭션을 제공한다\n\n\n또한 몽고디비에서 트랜잭션을 사용하기 위해선 replica sets 또는 shared clusters 환경이어야한다. \n\n하지만 기본적으론 standalone으로 구동하므로 트랜잭션을 사용할 순 없다.\n\n* replica set 모드 변경\n\ndocker-compose를 이용하여 3개의 몽고디비 서버를 실행한다.\n\n```yaml\nversion: '3'\n\nservices:\n\n  cfgsvr1:\n    container_name: mongodb.mung1.com\n    image: mongo\n    command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db\n    ports:\n      - 40001:27017\n    volumes:\n      - mung1:/data/db\n\n  cfgsvr2:\n    container_name: mongodb.mung2.com\n    image: mongo\n    command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db\n    ports:\n      - 40002:27017\n    volumes:\n      - mung2:/data/db\n\n  cfgsvr3:\n    container_name: mongodb.mung3.com\n    image: mongo\n    command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db\n    ports:\n      - 40003:27017\n    volumes:\n      - mung3:/data/db\n\nvolumes:\n  mung1: {}\n  mung2: {}\n  mung3: {}\n```\n\n도커 컴포즈를 실행한다.\n\n```bash\n$ docker-compose up\n```\n\n```bash\n$ docker ps\n\nCONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                           NAMES\n4b4ed212c177   mongo     \"docker-entrypoint.s…\"   18 seconds ago   Up 17 seconds   0.0.0.0:40003-\u003e27017/tcp, :::40003-\u003e27017/tcp   mongodb.mung3.com\nfcb6babd03c4   mongo     \"docker-entrypoint.s…\"   18 seconds ago   Up 17 seconds   0.0.0.0:40001-\u003e27017/tcp, :::40001-\u003e27017/tcp   mongodb.mung1.com\nca32cc56c5da   mongo     \"docker-entrypoint.s…\"   18 seconds ago   Up 17 seconds   0.0.0.0:40002-\u003e27017/tcp, :::40002-\u003e27017/tcp   mongodb.mung2.com\n```\n\n컨테이너를 접속한다.\n\n```\n$ docker exec -it mongodb.mung1.com /bin/bash\n```\n\n```js\n\u003e rs.initiate(\n  {\n    _id: \"cfgrs\",\n    configsvr: true,\n    members: [\n      { _id : 0, host : \"mongodb.mung1.com:27017\" },\n      { _id : 1, host : \"mongodb.mung2.com:27017\" },\n      { _id : 2, host : \"mongodb.mung3.com:27017\" }\n    ]\n  }\n)\n\n{\n\t\"ok\" : 1,\n\t\"$gleStats\" : {\n\t\t\"lastOpTime\" : Timestamp(1646784051, 1),\n\t\t\"electionId\" : ObjectId(\"000000000000000000000000\")\n\t},\n\t\"lastCommittedOpTime\" : Timestamp(1646784051, 1),\n\t\"$clusterTime\" : {\n\t\t\"clusterTime\" : Timestamp(1646784051, 1),\n\t\t\"signature\" : {\n\t\t\t\"hash\" : BinData(0,\"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\"),\n\t\t\t\"keyId\" : NumberLong(0)\n\t\t}\n\t},\n\t\"operationTime\" : Timestamp(1646784051, 1)\n}\n\ncfgrs:SECONDARY\u003e\n```\n\n도커컴포즈로 실행된 컨테이너는 같은 네트워크 브릿지를 사용하기 떄문에 컨테이너 이름으로 통신이 가능하다. \n\nreplica-set 설정이 마쳐지면 터미널의 `\u003e` 은 `replica-set 이름:primary|secondary|arbeiter` 형태로 바뀝니다.\n\n* replica-set 정보확인\n\n```js\ncfgrs:PRIMARY\u003e rs.conf()\n\n{\n\t\"_id\" : \"cfgrs\",\n\t\"version\" : 1,\n\t\"term\" : 1,\n\t\"members\" : [\n\t\t{\n\t\t\t\"_id\" : 0,\n\t\t\t\"host\" : \"mongodb.mung1.com:27017\",\n\t\t\t\"arbiterOnly\" : false,\n\t\t\t\"buildIndexes\" : true,\n\t\t\t\"hidden\" : false,\n\t\t\t\"priority\" : 1,\n\t\t\t\"tags\" : {\n\n\t\t\t},\n\t\t\t\"secondaryDelaySecs\" : NumberLong(0),\n\t\t\t\"votes\" : 1\n\t\t},\n\t\t{\n\t\t\t\"_id\" : 1,\n\t\t\t\"host\" : \"mongodb.mung2.com:27017\",\n\t\t\t\"arbiterOnly\" : false,\n\t\t\t\"buildIndexes\" : true,\n\t\t\t\"hidden\" : false,\n\t\t\t\"priority\" : 1,\n\t\t\t\"tags\" : {\n\n\t\t\t},\n\t\t\t\"secondaryDelaySecs\" : NumberLong(0),\n\t\t\t\"votes\" : 1\n\t\t},\n\t\t{\n\t\t\t\"_id\" : 2,\n\t\t\t\"host\" : \"mongodb.mung3.com:27017\",\n\t\t\t\"arbiterOnly\" : false,\n\t\t\t\"buildIndexes\" : true,\n\t\t\t\"hidden\" : false,\n\t\t\t\"priority\" : 1,\n\t\t\t\"tags\" : {\n\n\t\t\t},\n\t\t\t\"secondaryDelaySecs\" : NumberLong(0),\n\t\t\t\"votes\" : 1\n\t\t}\n\t],\n\t\"configsvr\" : true,\n\t\"protocolVersion\" : NumberLong(1),\n\t\"writeConcernMajorityJournalDefault\" : true,\n\t\"settings\" : {\n\t\t\"chainingAllowed\" : true,\n\t\t\"heartbeatIntervalMillis\" : 2000,\n\t\t\"heartbeatTimeoutSecs\" : 10,\n\t\t\"electionTimeoutMillis\" : 10000,\n\t\t\"catchUpTimeoutMillis\" : -1,\n\t\t\"catchUpTakeoverDelayMillis\" : 30000,\n\t\t\"getLastErrorModes\" : {\n\n\t\t},\n\t\t\"getLastErrorDefaults\" : {\n\t\t\t\"w\" : 1,\n\t\t\t\"wtimeout\" : 0\n\t\t},\n\t\t\"replicaSetId\" : ObjectId(\"6227ee3305cb095b08afb2a4\")\n\t}\n}\n```\n\n```js\ncfgrs:PRIMARY\u003e rs.status()\n\n{\n\t\"set\" : \"cfgrs\",\n\t\"date\" : ISODate(\"2022-03-09T00:06:05.284Z\"),\n\t\"myState\" : 1,\n\t\"term\" : NumberLong(1),\n\t\"syncSourceHost\" : \"\",\n\t\"syncSourceId\" : -1,\n\t\"configsvr\" : true,\n\t\"heartbeatIntervalMillis\" : NumberLong(2000),\n\t\"majorityVoteCount\" : 2,\n\t\"writeMajorityCount\" : 2,\n\t\"votingMembersCount\" : 3,\n\t\"writableVotingMembersCount\" : 3,\n\t\"optimes\" : {\n\t\t\"lastCommittedOpTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784365, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"lastCommittedWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\"readConcernMajorityOpTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784365, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"appliedOpTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784365, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"durableOpTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784365, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"lastAppliedWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\"lastDurableWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\")\n\t},\n\t\"lastStableRecoveryTimestamp\" : Timestamp(1646784351, 1),\n\t\"electionCandidateMetrics\" : {\n\t\t\"lastElectionReason\" : \"electionTimeout\",\n\t\t\"lastElectionDate\" : ISODate(\"2022-03-09T00:01:02.257Z\"),\n\t\t\"electionTerm\" : NumberLong(1),\n\t\t\"lastCommittedOpTimeAtElection\" : {\n\t\t\t\"ts\" : Timestamp(1646784051, 1),\n\t\t\t\"t\" : NumberLong(-1)\n\t\t},\n\t\t\"lastSeenOpTimeAtElection\" : {\n\t\t\t\"ts\" : Timestamp(1646784051, 1),\n\t\t\t\"t\" : NumberLong(-1)\n\t\t},\n\t\t\"numVotesNeeded\" : 2,\n\t\t\"priorityAtElection\" : 1,\n\t\t\"electionTimeoutMillis\" : NumberLong(10000),\n\t\t\"numCatchUpOps\" : NumberLong(0),\n\t\t\"newTermStartDate\" : ISODate(\"2022-03-09T00:01:02.325Z\"),\n\t\t\"wMajorityWriteAvailabilityDate\" : ISODate(\"2022-03-09T00:01:03.290Z\")\n\t},\n\t\"members\" : [\n\t\t{\n\t\t\t\"_id\" : 0,\n\t\t\t\"name\" : \"mongodb.mung1.com:27017\",\n\t\t\t\"health\" : 1,\n\t\t\t\"state\" : 1,\n\t\t\t\"stateStr\" : \"PRIMARY\",\n\t\t\t\"uptime\" : 1010,\n\t\t\t\"optime\" : {\n\t\t\t\t\"ts\" : Timestamp(1646784365, 1),\n\t\t\t\t\"t\" : NumberLong(1)\n\t\t\t},\n\t\t\t\"optimeDate\" : ISODate(\"2022-03-09T00:06:05Z\"),\n\t\t\t\"lastAppliedWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"lastDurableWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"syncSourceHost\" : \"\",\n\t\t\t\"syncSourceId\" : -1,\n\t\t\t\"infoMessage\" : \"\",\n\t\t\t\"electionTime\" : Timestamp(1646784062, 1),\n\t\t\t\"electionDate\" : ISODate(\"2022-03-09T00:01:02Z\"),\n\t\t\t\"configVersion\" : 1,\n\t\t\t\"configTerm\" : 1,\n\t\t\t\"self\" : true,\n\t\t\t\"lastHeartbeatMessage\" : \"\"\n\t\t},\n\t\t{\n\t\t\t\"_id\" : 1,\n\t\t\t\"name\" : \"mongodb.mung2.com:27017\",\n\t\t\t\"health\" : 1,\n\t\t\t\"state\" : 2,\n\t\t\t\"stateStr\" : \"SECONDARY\",\n\t\t\t\"uptime\" : 313,\n\t\t\t\"optime\" : {\n\t\t\t\t\"ts\" : Timestamp(1646784364, 1),\n\t\t\t\t\"t\" : NumberLong(1)\n\t\t\t},\n\t\t\t\"optimeDurable\" : {\n\t\t\t\t\"ts\" : Timestamp(1646784364, 1),\n\t\t\t\t\"t\" : NumberLong(1)\n\t\t\t},\n\t\t\t\"optimeDate\" : ISODate(\"2022-03-09T00:06:04Z\"),\n\t\t\t\"optimeDurableDate\" : ISODate(\"2022-03-09T00:06:04Z\"),\n\t\t\t\"lastAppliedWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"lastDurableWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"lastHeartbeat\" : ISODate(\"2022-03-09T00:06:04.733Z\"),\n\t\t\t\"lastHeartbeatRecv\" : ISODate(\"2022-03-09T00:06:03.715Z\"),\n\t\t\t\"pingMs\" : NumberLong(0),\n\t\t\t\"lastHeartbeatMessage\" : \"\",\n\t\t\t\"syncSourceHost\" : \"mongodb.mung1.com:27017\",\n\t\t\t\"syncSourceId\" : 0,\n\t\t\t\"infoMessage\" : \"\",\n\t\t\t\"configVersion\" : 1,\n\t\t\t\"configTerm\" : 1\n\t\t},\n\t\t{\n\t\t\t\"_id\" : 2,\n\t\t\t\"name\" : \"mongodb.mung3.com:27017\",\n\t\t\t\"health\" : 1,\n\t\t\t\"state\" : 2,\n\t\t\t\"stateStr\" : \"SECONDARY\",\n\t\t\t\"uptime\" : 313,\n\t\t\t\"optime\" : {\n\t\t\t\t\"ts\" : Timestamp(1646784364, 1),\n\t\t\t\t\"t\" : NumberLong(1)\n\t\t\t},\n\t\t\t\"optimeDurable\" : {\n\t\t\t\t\"ts\" : Timestamp(1646784364, 1),\n\t\t\t\t\"t\" : NumberLong(1)\n\t\t\t},\n\t\t\t\"optimeDate\" : ISODate(\"2022-03-09T00:06:04Z\"),\n\t\t\t\"optimeDurableDate\" : ISODate(\"2022-03-09T00:06:04Z\"),\n\t\t\t\"lastAppliedWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"lastDurableWallTime\" : ISODate(\"2022-03-09T00:06:05.020Z\"),\n\t\t\t\"lastHeartbeat\" : ISODate(\"2022-03-09T00:06:04.734Z\"),\n\t\t\t\"lastHeartbeatRecv\" : ISODate(\"2022-03-09T00:06:03.717Z\"),\n\t\t\t\"pingMs\" : NumberLong(0),\n\t\t\t\"lastHeartbeatMessage\" : \"\",\n\t\t\t\"syncSourceHost\" : \"mongodb.mung1.com:27017\",\n\t\t\t\"syncSourceId\" : 0,\n\t\t\t\"infoMessage\" : \"\",\n\t\t\t\"configVersion\" : 1,\n\t\t\t\"configTerm\" : 1\n\t\t}\n\t],\n\t\"ok\" : 1,\n\t\"$gleStats\" : {\n\t\t\"lastOpTime\" : Timestamp(1646784051, 1),\n\t\t\"electionId\" : ObjectId(\"7fffffff0000000000000001\")\n\t},\n\t\"lastCommittedOpTime\" : Timestamp(1646784365, 1),\n\t\"$clusterTime\" : {\n\t\t\"clusterTime\" : Timestamp(1646784365, 1),\n\t\t\"signature\" : {\n\t\t\t\"hash\" : BinData(0,\"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\"),\n\t\t\t\"keyId\" : NumberLong(0)\n\t\t}\n\t},\n\t\"operationTime\" : Timestamp(1646784365, 1)\n}\n```\n\n* 마스터 확인\n\n```js\ncfgrs:PRIMARY\u003e db.isMaster()\n\n{\n\t\"topologyVersion\" : {\n\t\t\"processId\" : ObjectId(\"6227eb7b05cb095b08afb25f\"),\n\t\t\"counter\" : NumberLong(6)\n\t},\n\t\"hosts\" : [\n\t\t\"mongodb.mung1.com:27017\",\n\t\t\"mongodb.mung2.com:27017\",\n\t\t\"mongodb.mung3.com:27017\"\n\t],\n\t\"setName\" : \"cfgrs\",\n\t\"setVersion\" : 1,\n\t\"ismaster\" : true,\n\t\"secondary\" : false,\n\t\"primary\" : \"mongodb.mung1.com:27017\",\n\t\"me\" : \"mongodb.mung1.com:27017\",\n\t\"electionId\" : ObjectId(\"7fffffff0000000000000001\"),\n\t\"lastWrite\" : {\n\t\t\"opTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784324, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"lastWriteDate\" : ISODate(\"2022-03-09T00:05:24Z\"),\n\t\t\"majorityOpTime\" : {\n\t\t\t\"ts\" : Timestamp(1646784324, 1),\n\t\t\t\"t\" : NumberLong(1)\n\t\t},\n\t\t\"majorityWriteDate\" : ISODate(\"2022-03-09T00:05:24Z\")\n\t},\n\t\"configsvr\" : 2,\n\t\"maxBsonObjectSize\" : 16777216,\n\t\"maxMessageSizeBytes\" : 48000000,\n\t\"maxWriteBatchSize\" : 100000,\n\t\"localTime\" : ISODate(\"2022-03-09T00:05:25.631Z\"),\n\t\"logicalSessionTimeoutMinutes\" : 30,\n\t\"connectionId\" : 1,\n\t\"minWireVersion\" : 0,\n\t\"maxWireVersion\" : 13,\n\t\"readOnly\" : false,\n\t\"ok\" : 1,\n\t\"$gleStats\" : {\n\t\t\"lastOpTime\" : Timestamp(1646784051, 1),\n\t\t\"electionId\" : ObjectId(\"7fffffff0000000000000001\")\n\t},\n\t\"lastCommittedOpTime\" : Timestamp(1646784324, 1),\n\t\"$clusterTime\" : {\n\t\t\"clusterTime\" : Timestamp(1646784324, 1),\n\t\t\"signature\" : {\n\t\t\t\"hash\" : BinData(0,\"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\"),\n\t\t\t\"keyId\" : NumberLong(0)\n\t\t}\n\t},\n\t\"operationTime\" : Timestamp(1646784324, 1)\n}\n```\n\n* 2, 3번 서버에서 몽고디비 서버 접속\n\n```\n$ docker exec -it mongodb.mung2.com /bin/bash\n$ mongo\n\ncfgrs:SECONDARY\u003e\n```\n\n```\n$ docker exec -it mongodb.mung2.com /bin/bash\n$ mongo\n\ncfgrs:SECONDARY\u003e\n```\n\n2, 3번 몽고디비 서버는 SECONDARY 입니다.\n\n* 트랜잭션 수행\n\n트랜잭션 수행을 위해 다음과 같은 절차를 수행한다.\n\n1. 세션 생성\n\n2. startTransaction 호출\n\n3. 쿼리수행\n\n4. commitTransaction 호출\n\n5 세션 종료\n\n다음 쿼리는 PRIMARY에서 수행해야 한다.\n\n```js\n\u003e session = db.getMongo().startSession()\n\u003e session.startTransaction({readConcern: {level: 'snapshot'}, writeConcern: {w: 'majority'}});\n\u003e post = session.getDatabase('데이터베이스 이름').getCollection('콜렉션 이름')\n\n// 수행하기 원하는 작업들\n\n\u003e session.commitTransaction();\n\u003e session.endSession();\n```\n\n```js\ncfgrs:PRIMARY\u003e use admin \ncfgrs:PRIMARY\u003e session = db.getMongo().startSession()\ncfgrs:PRIMARY\u003e session.startTransaction({readConcern: {level: 'snapshot'}, writeConcern: {w: 'majority'}});\ncfgrs:PRIMARY\u003e post = session.getDatabase('admin').getCollection('post')\n\ncfgrs:PRIMARY\u003e post.insert({title:\"1\", author: \"2\"}, {session: session}) // 해당 세션에서 스냅샷이 생성됨\n\ncfgrs:PRIMARY\u003e session.commitTransaction(); // 커밋시\ncfgrs:PRIMARY\u003e session.abortTransaction(); // 롤백시\ncfgrs:PRIMARY\u003e session.endSession();\n```\n\n추가된 데이터는 SECONDARY에서도 확인가능\n\n```js\ncfgrs:SECONDARY\u003e rs.secondaryOk() # 수행하지 않으면 에러발생\n\ncfgrs:SECONDARY\u003e db.post.find()\n{ \"_id\" : ObjectId(\"6227f43699c75817098098d8\"), \"title\" : \"1\", \"author\" : \"2\" }\n{ \"_id\" : ObjectId(\"6227f4fa99c75817098098dc\"), \"title\" : \"1\", \"author\" : \"2\" }\n{ \"_id\" : ObjectId(\"6227f52699c75817098098dd\"), \"title\" : \"1\", \"author\" : \"2\" }\n```\n\n## mapReduce\n\nmap + reduce 개념\n\n```js\n\u003e db.collection.mapReduce(\n  \u003cmap\u003e\n  ,\u003creduce\u003e\n  {\n    out: \u003ccollection\u003e\n    ,query:\u003cdocument\u003e\n    ,sort:\u003cdocument\u003e\n    ,limit:\u003cnumber\u003e\n    ,finalize:\u003cfunction\u003e\n    ,scope:\u003cdocument\u003e\n    ,jsMode:\u003cboolean\u003e\n    ,verbose:\u003cboolean\u003e\n  }\n)\n```\n\n* document reset\n\n```js\n\u003e db.post.remove({})\n\n\u003e db.post.insert([\n  {like: 1, category: 'a'},\n  {like: 2, category: 'a'},\n  {like: 3, category: 'a'},\n  {like: 2, category: 'a'},\n  {like: 2, category: 'a'},\n  {like: 5, category: 'a'},\n  {like: 6, category: 'a'},\n  {like: 9, category: 'a'},\n  {like: 0, category: 'a'},\n  {like: 1, category: 'b'},\n  {like: 2, category: 'b'},\n  {like: 3, category: 'c'},\n  {like: 2, category: 'c'},\n  {like: 2, category: 'c'},\n  {like: 5, category: 'c'},\n  {like: 6, category: 'd'},\n  {like: 9, category: 'd'},\n  {like: 0, category: 'd'},\n  {like: 2, category: 'c'},\n  {like: 2, category: 'c'},\n  {like: 5, category: 'c'},\n  {like: 6, category: 'd'},\n  {like: 9, category: 'd'},\n  {like: 0, category: 'd'},\n])\n```\n\n```js\n\u003e var map = function() {\n    // category를 키로 like를 배열로 만들어줌\n    // 첫 번째 인자는 key, 두 번째 인자는 values\n    emit(this.category, this.like)    \n}\n\n// map에서 emit으로 호출한 값 전달받음\n\u003e var reduce = function (key, values) {\n  return {\n    category: key, likes: Array.sum(values)\n  }\n}\n\n\u003e db.post.mapReduce(\n    map, reduce,\n    \"like_total_cnt\"\n)\n\n{ \"result\" : \"like_total_cnt\", \"ok\" : 1 }\n```\n\n```js\n\u003e db.like_total_cnt.find()\n\n{ \"_id\" : \"a\", \"value\" : { \"category\" : \"a\", \"likes\" : 30 } }\n{ \"_id\" : \"b\", \"value\" : { \"category\" : \"b\", \"likes\" : 3 } }\n{ \"_id\" : \"d\", \"value\" : { \"category\" : \"d\", \"likes\" : 30 } }\n{ \"_id\" : \"c\", \"value\" : { \"category\" : \"c\", \"likes\" : 21 } }\n```\n\nmap은 document(row) 갯수 만큼 emit을 호출\n\n여기서 this는 참조중인 document를 의미\n\nemit은 첫 번째 인자로 key를 두 번째 인자로 value를 전달\n\nemit은 동일한 key에 대해서 value를 배열로 만듬\n \ndocument에서 category와 like를 emit에 전달하면 다음과 같은 형태가 됨\n\n```js\n첫 번째 map 호출\n{\n  a: [1]\n}\n\n두 번째 map 호출\n{\n  a: [1, 2]\n}\n\n세 번째 map 호출\n{\n  a: [1, 2, 3]\n}\n```\n\ndocument의 수만큼 map이 호출되어 이러한 구조로 데이터가 만들어 짐\n\ncategory: [like] 형태로 오브젝트가 생성 \n\n이 결과는 reduce로 들어감 \n\nreduce는 key 갯수만큼 호출되며 key와 그 배열을 reduce로 전달\n\nreduce의 return값은 value에 매핑\n\n그 결과를 mapReduce의 세 번째 인자에 전달된 out에 따라 새로운 컬렉션을 만들거나 병합(merge)\n\n* finalize 활용\n\n```js\n\u003e var finalize = function (key, value) {\n  return value.likes * 10;\n};\n\n\u003e db.post.mapReduce(\n    map, reduce,\n    {\n      out: { merge: \"like_total_cnt\" },\n      finalize: finalize,\n    }\n)\n\n{ \"result\" : \"like_total_cnt\", \"ok\" : 1 }\n```\n\nmerge 옵션은 넣거나 뺼 수 있음\n\n```js\n\u003e db.like_total_cnt.find()\n{ \"_id\" : \"a\", \"value\" : 300 }\n{ \"_id\" : \"b\", \"value\" : 30 }\n{ \"_id\" : \"d\", \"value\" : 300 }\n{ \"_id\" : \"c\", \"value\" : 210 }\n```\n\n* query 활용\n\n```js\n\u003e db.post.mapReduce(\n    map, reduce,\n    {\n      out: \"like_total_cnt\",\n      query: { like : 0 },\n      finalize: finalize,\n    }\n)\n\n{ \"result\" : \"like_total_cnt\", \"ok\" : 1 }\n```\n\n```js\n\u003e db.like_total_cnt.find()\n{ \"_id\" : \"a\", \"value\" : 0 }\n{ \"_id\" : \"d\", \"value\" : 0 }\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjt3591oo%2Fmongodb-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpjt3591oo%2Fmongodb-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpjt3591oo%2Fmongodb-guide/lists"}