반응형

샘플 데이터

  • "맛있는 MongoDB"
    • 샘플 데이터 공유
$ git clone https://github.com/Karoid/mongodb_tutorials.git
$ ec2-user@mongodb:~$ cd mongodb_tutorials/car_accident/

ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c area --file area.json
2021-03-02T13:29:00.380+0000    connected to: localhost
2021-03-02T13:29:00.403+0000    imported 228 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_month --file by_month.json
2021-03-02T13:29:32.252+0000    connected to: localhost
2021-03-02T13:29:32.309+0000    imported 227 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_road_type --file by_road_type.json
2021-03-02T13:29:50.266+0000    connected to: localhost
2021-03-02T13:29:50.309+0000    imported 227 documents
ec2-user@mongodb:~/mongodb_tutorials/car_accident$
ec2-user@mongodb:~/mongodb_tutorials/car_accident$ mongoimport -d car_accident -c by_type --file by_type.json
2021-03-02T13:30:08.372+0000    connected to: localhost
2021-03-02T13:30:08.408+0000    imported 687 documents

> show dbs
admin        0.000GB
car_accident  0.000GB
config        0.000GB
local        0.000GB
test          0.000GB
>
> use car_accident
switched to db car_accident
> show tables
area
by_month
by_road_type
by_type

 

  • Find 관련 명령어에는 여러 개가 있으며, 그 중에 많이 사용하는 위주로 가이드 하며, 필요 시 추가 가이드 진행 예정

명령어

내역

find()

검색

findAndModify()

검색 후 수정

Update, upset, remove 모두 가능

new : true를 설정하여 update 이후 값을 리턴

new : false 또는 미적용 시 update 이전 값을 리턴

 

db.monsters.findAndModify({

    query: { name: "Dragon" },

    update: { $inc: { att: 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},

    upsert: true,

    new : true

})

findOne()

한건만 검색

findOneAndDelete()

한건만 검색 후 삭제

findOneAndReplace() > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

Replace 와 Update의 경우 Update는 명시한 필드만 변경 되지만, Replace의 경우는 명시한 필드 변경 외에는 나머지 필드는 모두 삭제 됨

가급적이면 Update만 사용 해야함

findOneAndUpdate()  > v3.2

한건만 검색 후 변경

returnNewDocument : true 설정하여 변경 전후 확인 가능

 

[Find]

  • Find명령어 사용 시 필요한 filed 명을 사용하여 검색하시기 바랍니다.(Covered Query)
  • Ex) db.bios.find( {조건}, {_id:0, name:1 , money:1})
    • 쿼리가 요구하는(리턴되는) 내용의 모든 필드가 하나의 Index에 포함되어 있으므로, Index만 조회하기 때문에 Document를 조회하는 것보다 훨씬 빠름(일반적으로 인덱스 키는 RAM에 적재되 있거나 디스크에 순차적으로 위치 하기 때문)
    • _id 필드를 0으로 명시하지 않는 경우, 결과 값에서 표현이 되기 때문에, covered query 조건이 되지 않으므로, 반드시 _id 필드를 0으로  명시
    • 쿼리에 필드가 없거나 필드의 값이 null 이 되면 안된다. i.e. {"field" : null} or {"field" : {$eq : null}}
    • v3.6 부터 embedded document의 경우도 적용 (이전 버전 에서는 불가능?)
      • db.userdata.find( { "user.login": "tester" }, { "user.login": 1, _id: 0 } )
      • 테스트 결과 안되요ㅠ(알고 계신 분 알려주세요)
    • 제한
      • Geospatial Index 에서는 사용 못함
      • Multikey Index에서 배열 필드에 대해서는 Covered index 가 불가능(v3.6부터 non-array field들에 대해서는 사용 가능)
    • Covered Query
  • field 명시 방법
    • 필드에 _id 여부는 항상 명시해야 함(_id를 쿼리 결과 내에서 표현하거나 표현하지 않는 것에 대해 명시를 반드시 해야 함)
    • _id에 대해서만 혼용 사용 가능하며, 다른 필드들에 대해서는 보여 주고 싶은 것에 대해서만 1로 명시, 0으로 명시하면 에러 발생
    • Ex ) > db.thing.find( { }, {_id:0, empno: 1} )  // empno만 표시하고, _id 및 다른 필드는 표시 안함. // 여기서 중요한건 _id는 항상 명시해 줘야 하며, 보고 싶은 필드만 1로 설정해서 표시 // 다른 필드의 경우 birth 필드가 있더라도 birth:0 으로 하면 에러 발생...왜???모르겠음
    • 만약 empno만 빼고 다 보고자 하면 그때는 > db.thing.find( { }, {empno: 0} ) 이런 식으로 표시
    • 보고 싶은 field가 있다면, field를 명시할 때 보겠다는 field 들만 명시를 하고,field 명시 여부를 혼용해서 명시하게 되면, 에러가 발생
    • 반대로 보고 싶지 않은 field가 있다면 명시 안하겠다는 field 들만 명시를 해야 함

[Filed] 

  • Covered Query Test
# semester 에 대한 Index 확인
> db.employee.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_"
        },
        {
                "v" : 2,
                "key" : {
                        "semester" : 1
                },
                "name" : "semester_1"
        }
]

# _id 필드를 명시적으로 0 처리 하지 않으면, 자연스럽게 _id 필드도 같이 표시되기 때문에 명시적으로 _id 필드를 표시 안한다고 해야, Convered query 적용 여부를 확인 가능
> db.employee.find({semester:3},{_id:0, semester:1}).explain()

# 필드를 표시 안하게 되는 경우 _id 필드가 표현 되기에 covered query가 되지 않음
> db.employee.find({semester:3},{"field" : null}).explain()

#  covered index 확인

# 필드를 표시 안 하게 되는 경우 _id 필드가 표현 되기에 covered query가 되지 않음 (단순 인덱스 스캔 - fetch 진행)

#실패 - embedded Document 에 대한 covered query 실패
> db.inventory.insertMany( [
    { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
    { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
    { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
    { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
    { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
{
        "acknowledged" : true,
        "insertedIds" : [
                ObjectId("603cf504850313ab26e27ced"),
                ObjectId("603cf504850313ab26e27cee"),
                ObjectId("603cf504850313ab26e27cef"),
                ObjectId("603cf504850313ab26e27cf0"),
                ObjectId("603cf504850313ab26e27cf1")
        ]
}
>
> db.inventory.createIndex({item:1, "instock.qty":1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
# 데이터 확인
> db.inventory.find({item:"journal", "instock.qty":5}, {_id:0,item:1, "instock.qty":1})
{ "item" : "journal", "instock" : [ { "qty" : 5 }, { "qty" : 15 } ] }

# covered 체크
> db.inventory.find({item:"journal", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

# 단일로 진행 실패
> db.inventory.insertOne({ item: "test_item", instock: [{warehouse: "A", qty: 5}]})
> db.inventory.find({item:"test_item", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

# 단일로 진행 실패 2
> db.inventory.insertOne({ item: "test_item_2", instock: [{qty: 5}]})
> db.inventory.find({item:"test_item_2", "instock.qty":5}, {_id:0,item:1, "instock.qty":1}).explain()

비교(Comparison) 연산자

operator

설명

 

$eq

(equals) 주어진 값과 일치하는 값

find({ 대상필드:{원하는연산자:조건값}})

 

 

 

 

 

$gt

(greater than) 주어진 값보다 큰 값

$gte

(greather than or equals) 주어진 값보다 크거나 같은 값

$lt

(less than) 주어진 값보다 작은 값

$lte

(less than or equals) 주어진 값보다 작거나 같은 값

$ne

(not equal) 주어진 값과 일치하지 않는 값

$in

주어진 배열 안에 속하는 값

반드시 배열 형태로 질의

find({필드:{$in / $nin :[ { 원하는값 A } , { 원하는값 B } ] } })

$nin

주어진 배열 안에 속하지 않는 값

논리 연산자

operator

설명

 

$or

주어진 조건중 하나라도 true 일 때 true

배열 형태로 질의 진행

find({$or / $and / $not / $nor : [{조건A},{조건B}]})

$and

주어진 모든 조건이 true 일 때 true

$not

주어진 조건이 false 일 때 true

$nor

주어진 모든 조건이 false 일때 true

> db.inventory.find({"instock.qty":{$eq:5}})
{ "_id" : ObjectId("603cf504850313ab26e27ced"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("603cf504850313ab26e27cee"), "item" : "notebook", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf504850313ab26e27cf0"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf675850313ab26e27cf2"), "item" : "test_item", "instock" : [ { "warehouse" : "A", "qty" : 5 } ] }
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

# county 가 종로구,중구 또는 population 이 3,552,490 보다 큰 경우
> db.area.find({$or:[{county: "종로구"},{county:"중구"},{population:{$gte:3552490}}]})

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a850775278e"), "city_or_province" : "부산", "county" : "중구", "population" : 45208 }
{ "_id" : ObjectId("5c88f9f70da47a8507752830"), "city_or_province" : "대구", "county" : "중구", "population" : 79712 }
{ "_id" : ObjectId("5c88f9f70da47a850775283f"), "city_or_province" : "인천", "county" : "중구", "population" : 115249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

# county 가 종로구,중구 가운데, population 이 125,249 보다 큰 경우 (and / or 연산이 공존)
> db.area.find({$and:[{$or:[{county: "종로구"},{county:"중구"}]},{population:{$gte:125249}}] })

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

# county 가 종로구,중구 가운데, population 이 125,249 보다 큰 경우 ($and / $in 연산이 공존)
> db.area.find({$and:[{county:{$in:["종로구","중구"]}}, {population:{$gte:125249}}] })

{ "_id" : ObjectId("5c88f9f70da47a8507752775"), "city_or_province" : "서울", "county" : "종로구", "population" : 152737 }
{ "_id" : ObjectId("5c88f9f70da47a8507752776"), "city_or_province" : "서울", "county" : "중구", "population" : 125249 }
{ "_id" : ObjectId("5c88f9f70da47a8507752848"), "city_or_province" : "대전", "county" : "중구", "population" : 252490 }
{ "_id" : ObjectId("5c88f9f70da47a850775284c"), "city_or_province" : "울산", "county" : "중구", "population" : 242536 }

$regex 연산자

  • $regex 연산자(정규표현식)를 이용하여, Document를  찾을 수 있음
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }
{ <field>: /pattern/<options> }
  • 4번쨰 라인 처럼 $regex 를 작성하지 않고 바로 정규식을 쓸 수도 있으며,  $options 정보 를 이용도 가능

option

설명

i

대소문자 무시

m

정규식에서 anchor(^) 를 사용 할 때 값에 \n 이 있다면 무력화

x

정규식 안에있는 whitespace를 모두 무시

s

dot (.) 사용 할 떄 \n 을 포함해서 매치

  • 정규식 test_item_[1-2] 에 일치하는 값이 item 조회
    • 조회하는 데이터의 "" 사용 안함.
> db.inventory.find({item : /test_item_[1-2]/})
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

#대소문자 무시 i 옵션 이용
> db.inventory.find({item : /Test_item_[1-2]/i})
{ "_id" : ObjectId("603cf6d7850313ab26e27cf3"), "item" : "test_item_2", "instock" : [ { "qty" : 5 } ] }

$text 연산자

  • Text 검색으로 단어 형태만 검색이 가능 (띄어쓰기 단위 -> 가령 한 단어 내에 포함된 것은 검색이 안됨)
  • 대 소문자 구분 안함
  • 각 나라별 언어에 맞춰 검색이 지원되고 있지만, 한글은 지원되지 않음.
  • 문자열 인덱스를 만들어야 사용 가능
    • 해당 Collection 의 텍스트 인덱스 안에서만 작동하기 때문

필드

설명

$search

검색하려는 내용을 담는다. 구절로 설정되지 않으면 띄어 쓴 단어를 포함한 모든 Document반환

$language

Option. 검색하는 언어를 설정

MongoDB가 지원하는 언어를 설정할 수 있으며, 설정되지 않으면 인덱스에 설정된 내용을 따름

$caseSensitive

Option. Bollean 값.

문자의 대,소문자 구분을 결정하며, Default는 구분하지 않음(False)

$diacriticSensitive

Option. Bollean 값.

e`와 e 와 같이 알파벳의 위아래에 붙이는 기호를 무시할지를 정함. Default로는 false (무시 하지 않음)

> db.inventory.createIndex({ item:"text" })

> db.inventory.find( {$text:{$search:"test"}} )
{ "_id" : ObjectId("603e498748883f8cbbc18bd9"), "item" : "keep Test" }
{ "_id" : ObjectId("603e47f148883f8cbbc18bd7"), "item" : "keep test" }
{ "_id" : ObjectId("603e47e248883f8cbbc18bd6"), "item" : "test keep" }

  • 문자열 내 test 를 검색하는데, test_item 같은 것은 검색이 안됨. keep test / test keep 같이 띄어쓰기 된 것에 대해서만 검색

$where 연산자

  • $where 연산자를 통하여 javascript expression 을 사용 가능
#comments field 가 비어있는 Document 조회

> db.articles.find( { $where: "this.comments.length == 0" } )
{ "_id" : ObjectId("56c0ab6c639be5292edab0c4"), "title" : "article01", "content" : "content01", "writer" : "Velopert", "likes" : 0, "comments" : [ ]

 

$elemMatch 연산자

  • $elemMatch 연산자는 Embedded Documents 배열을 쿼리할때 사용
# comments 중 "Charlie" 가 작성한 덧글이 있는 Document 조회를 했을때, 게시물 제목과 Charlie의 덧글부분만 읽고싶은 경우
# 이렇게 해보면 의도와는 다르게  Delta 의 덧글도 출력
> db.articles.find(
    {
        "comments": {
            $elemMatch: { "name": "Charlie" }
        }
    },
    {
        "title": true,
        "comments.name": true,
        "comments.message": true
    }
)
{
        "_id" : ObjectId("56c0ab6c639be5292edab0c6"),
        "title" : "article03",
        "comments" : [
                {
                        "name" : "Charlie",
                        "message" : "Hey Man!"
                },
                {
                        "name" : "Delta",
                        "message" : "Hey Man!"
                }
        ]
}

 

  • Embedded Document 배열이 아니라 아래 Document의 "name" 처럼 한개의 Embedded Document 경우
> db.users.find({
    "username": "velopert",
    "name": { "first": "M.J.", "last": "K."},
    "language": ["korean", "english", "chinese"]
  }
)
> db.users.find({ "name.first": "M.J."})
  • Document의 배열이아니라 그냥 배열일 시에는 다음과 같이 Query
> db.users.find({ "language": "korean"})

projection

  • find() 메소드의 두번째 parameter 인 projection
  • 쿼리의 결과값에서 보여질 field를 정할 대 사용
# article의 title과 content 만 조회

> db.articles.find( { } , { "_id": false, "title": true, "content": true } )
{ "title" : "article01", "content" : "content01" }
{ "title" : "article02", "content" : "content02" }
{ "title" : "article03", "content" : "content03" }

$slice 연산자

  • projector 연산자 중 $slice 연산자는 Embedded Document 배열을 읽을때 limit 설정
# title 값이 article03 인 Document 에서 덧글은 하나만 보이게 출력
$slice 가 없었더라면, 2개를 읽어와야하지만 1개로 제한을 두었기에 한개만 출력

> db.articles.find({"title": "article03"}, {comments: {$slice: 1}}).pretty()
{
        "_id" : ObjectId("56c0ab6c639be5292edab0c6"),
        "title" : "article03",
        "content" : "content03",
        "writer" : "Bravo",
        "likes" : 40,
        "comments" : [
                {
                        "name" : "Charlie",
                        "message" : "Hey Man!"
                }
        ]
}

 

 

$elemMatch

  • 위의 elemMatch 는 조건 연산자에서 사용하는 것이며,
  • 해당 elemMatch 는 필드 내, 즉 projection 파라메터 입니다.
# $elemMatch 연산자를 projection 연산자로 사용하면 이를 구현 가능
# comments 중 "Charlie" 가 작성한 덧글이 있는 Document 중 제목, 그리고 Charlie의 덧글만 조회 (필드 선언 시 다시 한번 더 eleMatch를 진행하게 되면 Delta 의 댓글은 안보임
> db.articles.find(
...     {
...         "comments": {
...             $elemMatch: { "name": "Charlie" }
...         }
...     },
...     {
...         "title": true,
...         "comments": {
...             $elemMatch: { "name": "Charlie" }
...         },
...         "comments.name": true,
...         "comments.message": true
...     }
... )
{ "_id" : ObjectId("56c0ab6c639be5292edab0c6"), "title" : "article03", "comments" : [ { "name" : "Charlie", "message" : "Hey Man!" } ] }

 

[findAndModify]

  • Single Document 에 대한 수정하고 반환
  • 반환 된 Document는 수정에 대한 결과가 아님
  • 반환 된 Document 에 수정한 내역을 확인하고 싶으면 new Option을 이용
    • new 옵션을 true로 하는 경우 변경 후의 결과 값을 보여주며, Default 로 new 옵션을(false) 선언하지 않는 경우 변경 되기 전 값을 출력
# mosters 에서 name이 'Demon' 의 att를 350으로 변경하고 변경한 후 결과 확인
> db.monsters.findAndModify({ query: { name: 'Demon' }, update: { $set: { att: 350 } }, new: true })

db.collection.findAndModify({
    query: <document>,
    sort: <document>,
    remove: <boolean>,
    update: <document or aggregation pipeline>, // Changed in MongoDB 4.2
    new: <boolean>,
    fields: <document>,
    upsert: <boolean>,
    bypassDocumentValidation: <boolean>,
    writeConcern: <document>,
    collation: <document>,
    arrayFilters: [ <filterdocument1>, ... ]
});

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: { $inc: { "att": 1000 } ,$set :{"name":"Dragon","hp":4000,"att":1000}},
    upsert: true,
    new : true
})

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":2000,"att":2000}},
    upsert: true,
    new : true
})

> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":3000,"att":2000}},
    upsert: true
})

# upsert / new
> db.monsters.find( {query:{ name: "Dragon_Baby" }} )
> db.monsters.findAndModify({
    query: { name: "Dragon_Baby" },
    update: {$set :{"hp":1000,"att":500}},
    upsert: true,
    new : true
})

# remove
> db.monsters.findAndModify({
    query: { name: "Dragon_Baby" },
    remove: true
})

# 여러개일 경우
> db.monsters.findAndModify({
    query: { name: "Dragon" },
    update: {$set :{"hp":4000,"att":5000}},
    upsert: true
})
  • new Option : true

  • upsert: true / new : true

  • remove : true
    • new:true
      • return 이 없기 때문에 에러
    • update : {xxxx}
      • 당연히 remove 되는데 update 에러 진행
    • remove 가 정상적으로 진행 시
      • 진행 이전 데이터 return  후 삭제

 

  • 여러 개일 경우
    • 역시 한 건만 변경 (update 처럼)

참고 : https://velopert.com/479

 

 

Cursor

 

반응형

+ Recent posts