⚡ 유저 생성
mongod 데몬을 켠 후 mongo로 접속이 가능할 터입니다. 이는 아무런 유저없이, 모두가 접속할 수 있는 상태라는 겁니다.
단순 개발 테스트 용이라면 별 다른 조치를 취하지 않아도 되지만 프로덕션으로 올라가게 되면 유저 권한을 명시해줄 필요가 있습니다.
mongod // 데몬 켜기
mongo // mongodb 접속
접속한 후 기본적으로 설정되어 있는 db를 출력해보시면, admin이 보이실겁니다.
use admin // admin DB 사용
db.createUser({user: 'root', pwd: '0000', roles:['root']}) // 유저 생성
root 권한을 가진 유저를 생성했습니다.
만약 로그인을 해야만 접속이 가능하게 만들려면 아래와 같이 mongod --auth를 주시면 됩니다. 쉽죠?
근데 전 개발할 땐 그냥 모두 열어두려구요... 귀찮음.
mongod --auth // 로그인이 필요한 환경의 데몬
mongo admin -u [이름] -p [비번] // 접속
⚡ DB
💻 DB 생성, 조회, 사용, 삭제
show dbs // DB의 목록을 출력합니다
db // 현재 사용 중인 DB
use [DB 이름] // DB를 생성 혹은 사용. 데이터를 최소 하나 이상 넣어야 show dbs에 표시됨
db.dropDatabase() // 현재 사용중인 DB 삭제
db.getCollectionInfos() //현재 db의 컬렉션들의 정보를 리스트로 반환합니다.
db.stats() // 현재 사용 중인 db의 컬렉션, 뷰, 오브젝트 갯수 등 정보를 제공합니다.
⚡ DB 관리용 메서드
db.enableFreeMonitoring() // 무료 모니터링 기능 켜기
db.serverStatus() // 호스트, 스토리지 엔진 등 서버와 관련된 사항 살펴보기
db.stats() // DB내 컬렉션 수, 오브젝트 갯수 등에 대한 통계
⚡ Collection
💻 컬렉션 생성 및 조회
show collections // 현재 사용하고 있는 DB의 컬렉션을 출력합니다.
db.createCollections('[컬렉션 이름]') // 컬렉션을 생성합니다.
ex - db.createCollection("secondCollection")
db.createCollection("employees", {capped:true, size:10000}) // capped를 줄 수 있다. capped를 쓸거면 size도 같이.
info - (https://darrengwon.tistory.com/222)
db.[컬렉션이름].drop() // collection 삭제
ex - db.employees.drop()
db.[바꿀 컬렉션].renameCollection("[새로운 이름]") // 컬렉션 이름 변경
ex - db.myCollection.renameCollection("yourCollection")
db.[컬렉션이름].find() // 해당 컬렉션의 도큐먼트 다 가져와
ex - db.employees.find()
💻 컬렉션 상태 조회
db.[컬렉션 이름].isCapped() // 컬렉션이 Capped인지 불린값으로 표현합니다.
db.[컬렉션 이름].stats() // 컬렉션 상태 출력
db.[컬렉션 이름].storageSize() // 컬렉션 스토리지 크기 반환
db.[컬렉션 이름].totalIndexSize() // 컬렉션 인덱스 크기 반환
db.[컬렉션 이름].totalSize() // 컬렉션 스토리지 + 인덱스 크기 반환
⚡ Document CRUD
많이 알아둘 필요 없고 다음에 익숙해 진 후 범위를 차차 넓혀가면 됩니다.
C : insertOne, insertMany
R : find
U : updateOne, updateMany
D : deleteOne, deleteMany
💻 다큐먼트 삽입(생성) : insertOne, insertMany
db.[컬렉션 이름].insertOne({x: 1}) // 컬렉션에 x: 1이라는 도큐먼트 삽입
ex - db.user.insertOne({_id: 5, username: "darren", password:"what"})
* 다큐먼트를 생성하기 위해 미리 컬렉션을 생성할 필요는 없습니다. 지정한 컬렉션이 없으면 자동 생성합니다.
* 생성 후에는 _id(프라이머리 키)가 자동 생성됩니다. _id값을 명시적으로 지정하고 싶다면 입력해주면 됩니다.
🚧 WriteConcern, ReadConcern 옵션
WriteConcern은 insertOne, insertMany 명령어를 통해 도큐먼트를 컬렉션에 넣으려고 할 때 해당 컬렉션이 쓰기작업(도큐먼트 생성, 수정, 삭제) 중이라면 어떻게 할 것인지에 대한 것입니다. ReadConern은 find 명령어를 통해 읽기 작업을 할 때 더 정확한 값을 얻어오기 위한 설정값이다.
이러한 설정은 꼼꼼하고 보수적으로 데이터를 다룰 수 있다는 장점이 있지만 성능이 떨어진다는 문제가 있다.
db.[컬렉션 이름].insertMany([{x: 1}, {y: 2}]) // 컬렉션에 여러 도큐먼트를 삽입합니다.
db.users.insertMany([
{subject: "coffee", author: "xyz", views: 50},
{subject: "capucino", author: "ijk", views: 13},
{subject: "orange juice", author: "dff", views: 335},
{subject: "bread", author: "abc", views: 23},
])
* 배열 내부에 도큐먼트를 넣어야 함에 주의합시다. [{도큐먼트 1}, {도큐먼트 2}]
그런데 만약 다음과 같이 _id를 겹치게 만들어 오류가 나게 하면 어떻게 될까요?
db.user.insertMany([{_id: 2, x: 5}, {_id: 2, x : 7}, {_id: 3, x : 9}])
{_id: 2, x: 5}는 저장이 되지만 {_id: 2, x: 7} 부터는 저장이 안됩니다. 즉, {_id: 3, x: 9}도 저장이 안되는 것입니다.
🚧 원자성
원자성이란, 중간의 상태가 없는, 모 아니면 도의 상태만 존재하는 것입니다.
즉, 데이터 처리 전/후 만 존재하는 것이지 데이터 처리 중은 존재하지 않는 것입니다.
ATM에 돈을 넣는 도중 전원이 나가 서버가 죽었다고 생각합시다. 이 때 ATM에 넣은 돈은 반영이 된 것인지, 되지 않은 것인지 불명확합니다. 이러한 불상사를 막기 위해 원자성을 추구해야 합니다.
insertOne의 경우 원자성을 가지고 있습니다 (당연히 처리해야 할 도큐먼트가 1개 뿐이므로)
그러나 insertMany와 같은 다중 작업의 경우 위의 예시에서 살펴볼 수 있듯 원자성을 가지고 있지 않습니다. 원자성이 적용되었다면, 오류가 난 즉시 아무것도 저장이 안되어야 했었는데, {_id: 2, x: 5}가 저장이 되었으니까요.
mongoDB에서는 이러한 원자성을 성립하게 만들어주기 위해 '트랜잭션(Transaction) 기능을 지원합니다. (4.0 버전 이상 부터 가능)
💻 다큐먼트 조회: find, findOne({조건}, {projection})
db.[조회할 컬렉션].find({}) // 해당 컬렉션의 모든 내용을 출력합니다.
아래 예시는 age가 18보다 큰 도큐먼트 중 name과 address를 가져옵니다. 최대 5개 까지만 출력하도록 limit를 걸어놓았습니다.
ex - db.CappedCollection.find()
* db.collection.find({query}, {projection}) 과 같이 쿼리와 프로젝션을 통해 특정 도큐먼트의 특정 필드를 출력할 수도 있습니다.
ex - db.article.find({"comment.author": "jason"}, {title: 1, content: 1})는 해당 쿼리를 만족하는 도큐먼트 중 title과 content 필드만을 출력하라고 하는 것입니다. 1외에도 true 값도 가능합니다. 단, _id는 기본값이 true라서 따로 프로젝션에 지정하지 않아도 출력됩니다. _id를 출력하지 않으려면 _id: 0을 명시적으로 줘야 합니다.
db.monsters.find({'item.backpack': "reere"})는, 객체 내부의 값을 기준으로 도큐먼트를 찾고 싶을 때 사용합니다.
다음과 같은 도큐먼트가 있을 때, item의 backpack이라는 속성을 통해 값을 가져오고 싶다면 위와 같이 작성하면 됩니다.
{
"_id" : ObjectId("5f197a0ad3a565bdcab2b042"),
"name" : "player",
"item" : {
"backpack" : "reere",
"any" : "redis"
}
}
특정 필드를 출력하기 싫다면 0이나 false를 줘야 합니다. 그러나 id를 제외한 다른 필드에 1과 0을 혼용하면 오류가 납니다. 예를 들어 다음과 같은 파라미터, {title: 1, content: 0}는 그 외의 다른 필드의 기본값이 1인지 0인지 알 수 없기 때문에 오류가 납니다.
따라서 특정 필드를 0으로 제외하거나, 특정 필드를 1로 특정하거나 둘 중 하나만 하도록 합시다.
- 조건 걸어두기
여러 가지 조건을 걸어두고 싶다면 다음과 같이 가져오는 것도 가능합니다.
[]로 감싼 후 각 조건을 {}로 또 감싸야 합니다.
// people 컬렉션 중에서 status가 A거나 age가 50인 도큐먼트를 출력하라
db.people.find({ $or: [ { status: "A" } , { age: 50 } ] })
// monsters 컬렉션 중 name이 Slime이거나 hp가 100보다 큰 도큐먼트 중 최초로 발견되는 도큐먼트를 가져와라
db.monsters.findOne({$or: [{name: "Slime"}, {hp: {$gt: 100}}]})
// 만약 도큐먼트 내부의 객체 속성을 통해 값을 가져오고 싶다면 ''로 감싸고 .을 찍어 가져오면 됩니다.
db.monsters.find({'item.backpack': "reere"})
db.employees.find({ age: { $nin : [15, 5, 45] }}, {})
db.employees.find({$and : [{age: { $eq: 50 }, status: "A"}]}, {user_id: 1, _id:0})
사용되는 쿼리 연산자를 나열해보자면 다음과 같습니다.
$eq = Matches values that are equal to a specified value.
$ne != Matches all values that are not equal to a specified value.
$gt > Matches values that are greater than a specified value.
$gte >= Matches values that are greater than or equal to a specified value.
$lt < Matches values that are less than a specified value.
$lte <= Matches values that are less than or equal to a specified value.
$in Matches any of the values specified in an array.
$nin Matches none of the values specified in an array
$and 조건 모두를 만족하는
$or 조건 중 하나라도 만족하는
$nor 조건 중 모든 것을 만족하지 않는
$not 조건을 만족하지 않는
정규식 사용도 가능합니다.
db.employees.find({user_id: /^bc/})
🚧 커서
find 명령어는 도큐먼트를 직접 반환하지 않고 커서를 반환한다. 쿼리 결과값으로 도출된 도큐먼트의 갯수가 1억개라고 가정한다면 직접 반환하는 것은 많은 시간이 소요될 것으로 예상할 수 있다. 때문에 batch와 cursor를 이용해서 find 결과를 반납하게 된다.
구체적으로 접근해보자. find 명령에 해당하는 도큐먼트가 1억개라고 가정해보자. 이 때 find 명령어를 사용하면 첫번째 batch는 101개의 도큐먼트를 가지고 있고, 커서는 이 중 20개를 가리킨다. 커서는 빠르게 20개씩 도큐먼트를 출력한다. 101개가 넘어가는 순간 두번째 batch로 커서가 이동하여 또 20개씩 가리킨다.
이렇게 빠른 속도로 커서가 20개씩 도큐먼트를 읽어가는 제한 시간은 '10분'이다.
어쨌거나 find 명령문은 도큐먼트의 결과 전체를 가져오지 않기 때문에 만약 모든 도큐먼트를 가져오고 싶다면 toArray(), forEach()를 이용해야 한다.
▪ db.Collection.find().toArray() : find 문의 모든 결과를 담은 배열을 반환함. 메모리 초과 가능성 존재
▪ db.Collection.find().forEach(function (document) { 각 document에 대한 작업 })
각각의 도큐먼트를 순차적으로 불러와 작업함. forEach의 파라미터로 콜백함수를 주는데 콜백 함수를 이용해 각 도큐먼트마다 작업을 할 수 있다.
💻 다큐먼트 조회: distinct
db.Collection.distinct("status") : status가 가지고 있는 값의 종류를 출력하라.
💻 다큐먼트 교체: replaceOne
db.[컬렉션].replaceOne({쿼리}, {새로 쓸 내용}, {옵션}) // 도큐먼트를 대체합니다. 포인트는 수정이 아니라 교체이기 때문에 쓸 내용을 적지 않으면 기존 내용은 사라진다는 특성이 있습니다. 여기서 교체된 도큐먼트의 _id 필드 값은 바뀌지 않습니다.
기존 내용은 사라진다는 특성 때문에 저는 수정을 주로 쓰지 교체는 자주 안 씁니다.
ex - db.user.replaceOne({username: "julian"}, {password: "huddadaadaak!"})
ex - db.user.replaceOne({}, {username: "humannoid", password: "47345"}, {upsert: true})
upsert 옵션은 교체할 쿼리가 없으면 도큐먼트를 생성합니다.
💻 다큐먼트 수정 : update, updateOne, updateMany, findOneAndUpdate
먼저, update와 updateOne, updateMany의 차이가 무엇인지 말하자면,
update({조건}, {업데이트 내용}, {옵션})에서 옵션에 multi: true면 조건에 맞는 모든 것(updateMany), false면 조건에 맞는 것 중 최초로 발견되는 것(updateOne)이었습니다. 이러한 옵션을 메서드로 대체한 것이 updateOne, updateMany입니다.
db.[컬렉션].updateOne({쿼리}, {수정할 내용}, {옵션}) // 쿼리에 해당하는 첫번째 도큐먼트만 수정합니다.
* 다큐먼트 수정할 내용에는 반드시 연산자를 수정해야 합니다. "the update peration document must contain atomic operators"
db.user.updateOne({password: "what"}, {$set: {username:"makaroni"}})
$set은 필드의 값을 단순히 변성할 때 사용하는 수정 연산자 입니다. 외에도 다양한 수정 연산자가 있습니다.
만약 $set을 입력하지 않으면 다른 내용들은 전부 다 지워지고, 수정된 내용만 남게 됩니다.
$inc, $min, $max, $mul, $rename, $unset 등이 존재하며 각 특성에 맞게 활용하면 됩니다.
$inc는 값을 더하라는 것입니다.
// 조건에 맞는 도큐먼트의 age에 -3을 더하게 됩니다.
db.user.updateOne({password: "what"}, {$inc: {age: -3}})
db.[컬렉션].updateMany({쿼리}, {수정할 내용}, {옵션}) // 쿼리에 해당하는 모든 도큐먼트를 수정합니다.
- 필드 값이 배열일 경우 수정하는 방법
https://docs.mongodb.com/manual/reference/operator/update-array/
$ 만 사용하기 => 발견된 배열의 첫번째 원소만 바뀜
db.[컬렉션].updateMany({쿼리},{$set: {"필드명.$":"수정할 내용"}})
$ : Acts as a placeholder to update the first element that matches the query condition. |
$[] => 조건 없이 모두다 바꿔버림
db.[컬렉션].updateMany({쿼리},{$set: {"필드명.$[]":"수정할 내용"}})
db.[컬렉션].updateMany({inventory:"pen"},{$set: {"inventory.$[]":"pencil"}})
$[] : Acts as a placeholder to update all elements in an array for the documents that match the query condition. |
$[식별자], {arrayFilters: [{식별자 : 바꿀내용}]} => 배열 내 특정 조건을 만족하는 것만 바꿈
db.article.updateMany({items: 'pen'}, {$set: {'items.$[potato]': "pencil"}}, {arrayFilters: [{potato: 'pen'}]})
$[<identifier>] : Acts as a placeholder to update all elements that match the arrayFilters condition for the documents that match the query condition. |
이 명령어를 실행하면
{
"_id" : ObjectId("5f19cf7b37ee21075a55a829"),
"name" : "x",
"items" : [
"pen",
"cloth",
"choco"
]
}
{
"_id" : ObjectId("5f19cf7b37ee21075a55a82a"),
"name" : "y",
"items" : [
"ruler",
"bag",
"pen"
]
}
//==================위 내용이 아래로 바뀝니다========================
{
"_id" : ObjectId("5f19cf7b37ee21075a55a829"),
"name" : "x",
"items" : [
"pencil", // pen에서 pencil로
"cloth",
"choco"
]
}
{
"_id" : ObjectId("5f19cf7b37ee21075a55a82a"),
"name" : "y",
"items" : [
"ruler",
"bag",
"pencil" // pen에서 pencil로
]
}
findOneAndUpdate는 조건에 맞는 도큐먼트를 찾고, 업데이트하는 편리한 메서드입니다.
db.getCollection('monsters').findOneAndUpdate({name: "Slime"}, {$set: {hp: 100}})
💻 다큐먼트 삭제 : deleteOne, deleteMany
역시나 remove 메서드가 존재했지만 multi 옵션을 분리하여 deleteOne, deleteMany으로 만든 것입니다.
deleteOne, deleteMany를 사용하도록 합시다.
db.[컬렉션].deleteOne({쿼리}, {옵션})
ex - db.char.deleteOne({name: "y"})
db.getCollection('monsters').deleteOne({name: "player"})
db.[컬렉션].deleteMany({쿼리}, {옵션})
ex - db.board.deleteMany({}) // 전체 삭제
'DB, ORM > 🍃 mongoDB (shell)' 카테고리의 다른 글
원자성을 유지를 위한 Transaction(트랜잭션) + WriteConcern/ReadConcern (0) | 2020.07.24 |
---|---|
Robo 3T 설치 및 활용 (0) | 2020.06.08 |
ubuntu에서 mongodb 설치하기 (0) | 2020.06.08 |
Capped 컬렉션 (0) | 2020.04.06 |
mongoDB 설치 (window 기반) (0) | 2020.02.27 |