본문으로 바로가기

⚡ 유저 생성

 

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/

 

Array Update Operators — MongoDB Manual

Note For details on specific operator, including syntax and examples, click on the specific operator to go to its reference page.

docs.mongodb.com

 

$ 만 사용하기 => 발견된 배열의 첫번째 원소만 바뀜

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({}) // 전체 삭제

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체