Database/MongoDB 2019. 9. 19. 10:38

 

MongoDB의 Insert 문은 2개의 인자가 들어간다. 첫번째 인자는 삽입할 문서, 두번째 인자는 선택적인 옵션이다. 여기서 오늘 알아볼 것은 두번째 인자중 ordered에 대해서 알아볼 것이다. 

 

우선 몽고디비에서는 디폴트로 ordered가 true인 상태에서 삽입을 수행한다. ordered가 true라는 것은 무슨 뜻일까? 만약 삽입하는 문서가 단일 문서가 아니고, insertMany를 사용하여 여러 문서를 한번에 삽입하는 상황을 생각해보자. 만약 ordered가 true라면 싱글 스레드로 삽입하려는 여러개의 문서(배열)를 명시된 순서대로 하나씩 삽입할 것이다. 순서가 중요한 상황이라면 유용할 것이다. 하지만 ordered가 false라면 멀티스레드로 여러 문서를 병렬로 삽입한다. 

 

그렇다면 당연히 ordered를 false로 하는 것이 좋지 않냐라는 생각이 들 수 있다. 답은 상황에 따라 다르다는 것이다. 아래와 같은 상황을 생각해보자.

 

> db.insertcollection.createIndex({name:1},{unique:true})

> db.insertcollection.insertMany([{name:"a"},{name:"a"},{name:"c"},{name:"b"}],{ordered:true})

 

 

유니크한 인덱스 필드로 "name"필드를 지정하였고 벌크로 4개의 문서를 삽입하는 상황이다.(ordered:true는 생략가능) 이 삽입 쿼리는 어떤 결과를 반환할까? 아마 유니크한 인덱스인데 중복된 값이 들어왔다는 예외를 반환할 것이다. ordered가 true라면 몽고디비는 내부적으로 예외가 발생한 시점에서 삽입연산을 마치게 된다. 그 말은 위의 쿼리에서 name:"a"라는 문서하나만 삽입되고 나머지는 삽입연산 수행이 중지 될 것이다. 그렇다면 아래와 같은 쿼리는 어떻게 될까?

 

> db.insertcollection.insertMany([{name:"a"},{name:"a"},{name:"c"},{name:"b"}],{ordered:false})

 

 

분명 중복 예외가 발생했음에도 불구하고 예외가 발생한 문서를 제외하고 모두 삽입이 되었다. 이것이 바로 ordered 옵션의 차이점이다. ordered가 true라면 단일스레드가 순차적으로 문서를 삽입하다 예외가 발생하면 그 상태에서 작업을 멈추지만, ordered가 false라면 예외가 발생하여도 그 문서만 무시하고 나머지 작업을 계속 수행하게 된다.

 

무엇이 좋고 나쁨을 사실 이야기하기 힘들다. 상황에 맞게 사용하는 것이 좋을 듯하다.

posted by 여성게
:
Database/MongoDB 2019. 9. 16. 23:26

 

MongoDB는 문자열 내용의 텍스트 검색을 수행하는 쿼리를 지원한다. 텍스트 검색을 수행하기 위해 몽고디비는 텍스트 인덱스와 $text 연산자를 사용한다.(View는 텍스트 검색을 지원하지 않는다.)

 

예제 진행을 위해 아래 문서들을 삽입한다.

 

1
2
3
4
5
6
7
8
9
db.stores.insert(
   [
     { _id: 1, name: "Java Hut", description: "Coffee and cakes" },
     { _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
     { _id: 3, name: "Coffee Shop", description: "Just coffee" },
     { _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing" },
     { _id: 5, name: "Java Shopping", description: "Indonesian goods" }
   ]
)
cs

 

Text Index

몽고디비는 문자열 컨텐츠에 대한 텍스트 검색 쿼리를 지원하기 위해 텍스트 인덱스을 제공한다. text 인덱스은 값이 문자열 또는 문자열 요소 배열인 필드를 포함할 수 있다.

 

텍스트 검색 쿼리를 수행하려면 컬렉션에 text 인덱스가 있어야한다. 컬렉션은 하나의 텍스트 검색 인덱스만 가질 수 있지만 해당 인덱스는 여러 필드를 포함할 수 있다.

 

> db.stores.createIndex({name:"text",description:"text"})

 

위 명령은 name 필드와 description 필드를 text 타입으로 인덱스를 생성한다. 위는 두 개의 텍스트 인덱스 필드를 지정하였다.

 

$text Operation

$text 쿼리 연산자를 이용하여 텍스트 인덱스가 있는 컬렉션에서 텍스트 검색을 할 수 있다.

 

$text는 공백과 대부분의 구두점을 구분 기호로 사용하여 검색 문자열을 토큰화하고 모든 토큰에 대해 OR 논리조건으로 쿼리를 수행한다.

 

> db.stores.find( { $text: { $search: "java coffee shop" } } )

 

위 텍스트 검색의 결과이다.

 

 

검색 조건의 "java coffee shop"이 java / coffee / shop 으로 토크나이징되고 각각의 토큰이 하나라도 포함되어 있다면 해당 문서를 결과값에 포함시킨다.

 

Exact Phrase

문자열 검색에 사용된 조건에 해당하는 문자열을 정확하게 일치하는 문서를 검색할 수도 있다.

 

> db.stores.find( { $text: { $search: "\"coffee shop\"" } } )

 

큰 따옴표로 검색 조건의 문자열을 묶으면 대소구분없이 "coffee shop"과 정확히 일치하는 문서만 결과값에 포함시킨다.

 

Term Exclustion

"-"연산자를 사용하여 검색에 제외할 텀을 지정할 수 있다. 밑의 명령은 java 또는 shop을 포함하고 coffee는 포함하지 않는 텍스트를 검색한다.

 

> db.stores.find( { $text: { $search: "java shop -coffee" } } )

 

Sort

몽고디비는 기본적으로 정렬되지 않은 검색 결과를 반환한다. 그러나 텍스트 검색 쿼리는 문서가 쿼리와 얼마나 잘 일치 하는지를 지정하는 각 문서에 대한 스코어를 계산하기 때문에 스코어링 기준으로 정렬이 가능하다.

 

> db.stores.find(

...    { $text: { $search: "java coffee shop" } },

...    { score: { $meta: "textScore" } }

... ).sort( { score: { $meta: "textScore" } } )

 

위 쿼리의 결과값이다. 결과값에 스코어값이 포함된 것을 볼 수 있다.

 

 

간단하게 몽고디비의 텍스트 검색에 대해 다루어봤습니다. 추후에 집계 부분에서도 또 다루어보겠지만, 텍스트 검색에 대해 다루어보지 않은 부분이 몇개 있으므로 나중에 다시 다루어 보겠습니다.

posted by 여성게
:
Database/MongoDB 2019. 9. 16. 22:57

 

이번 포스팅 내용은 자주 사용되는 SQL문과 MongoDB와의 쿼리를 비교하는 포스팅입니다. 

 

SQL Schema Statements MongoDB Schema Statements

CREATE TABLE people(

id MEDIUMINT NOT NULL AUTO_INCREMENT,

user_id varchar(30),

age number,

status char(1),

PRIMARY KEY(id)

)

db.people.insertOne({

user_id:"abc123",

age:55,

status:"A"

})

암시적으로 삽입 작업에서 _id를 생략했다면 내부적으로 _id에 값을 추가한다. 컬렉션 또한 삽입시점에 생성된다.

 

그러나 컬렉션을 명시적으로 생성가능하다.

 

db.createCollection("people")

 

ALTER TABLE people ADD join_date DATETIME

컬렉션은 문서 구조를 설명하거나 강제하지 않는다. 

그러나, 문서 레벨에서 updateMany() 오퍼레이션은 $set 오퍼레이션을 사용하여 존재하는 문서에 새로운 필드를 추가할 수 있다.

 

db.people.updateMany({},{$set:{join_date:new Date()}})

ALTER TABLE people DROP COLUMN join_date

컬렉션은 문서 구조를 설명하거나 강제하지 않는다.

 

그러나, 문서 레벨에서 updateMany() 오퍼레이션은 $unset 오퍼레이션을 사용하여 이미 존재하는 문서의 필드를 제거할 수 있다.

 

db.people.updateMany({},{$unset:{"join_date":""}})

CREATE INDEX idx_user_id_asc ON people(user_id) db.people.createIndex({user_id:1})
CREATE INDEX idx_user_id_asc_age_desc ON people(user_id,age DESC) db.people.createIndex({user_id:1,age:-1})
DROP TABLE people db.people.drop()

 

위는 테이블 수준에서 자주 사용되는 SQL과 비교한 몽고디비 명령이다.

 

Insert

 

SQL Insert Statements MongoDB insertOne() Statements
INSERT INTO people(user_id,age,status) VALUES ("bcd001",45,"A") db.people.insertOne({user_id:"bcd001",age:45,status:"A"})

 

Select

암묵적으로 몽고디비는 _id 필드를 결과에 포함시킨다. 만약 _id 필드를 결과에서 제외시키고 싶다면 명시적으로 옵션을 지정해야한다.

SQL Select Statements MongoDB find() Statements
SELECT * FROM PEOPLE db.people.find()
SELECT id,user_id,status FROM people db.people.find({},{user_id:1,status:1})
SELECT * FROM people WHERE status = "A" db.people.find({status:"A"})
SELECT user_id,status FROM people WHERE status = "A" db.people.find({status:"A"},{user_id:1,status:1,_id:0})
SELECT * FROM people WHERE status != "A" db.people.find({status:{$ne:"A"}})
SELECT * FROM people WHERE status = "A" AND age = 50 db.people.find({status:"A",age:50})
SELECT * FROM people WHERE status = "A" OR age = 50 db.people.find({$or:[{status:"A"},{age:50}]})
SELECT * FROM people WHERE age > 25 AND age <=50 db.people.find({age:{$gt:25,$lte:50}})
SELECT * FROM people WHERE user_id LIKE "%bc%"

db.people.find({user_id:/bc/})

or

db.people.find({user_id:{$regex:/bc/}})

SELECT * FROM people WHERE user_id LIKE "bc%"

db.people.find({user_id:/^bc/})

or

db.people.find({user_id:{$regex:/^bc/}})

SELECT * FROM people WHERE status = "A" ORDER BY user_id ACS db.people.find({status:"A"}).sort({user_id:1})
SELECT * FROM people WHERE status="A" ORDER BY user_id DESC db.people.find({status:"A"}).sort({user_id:-1})
SELECT COUNT(*) FROM people

db.people.count()

or

db.people.find().count()

SELECT COUNT(user_id) FROM people

db.people.count({user_id:{$exist:true}})

or

db.people.find({user_id:{$exist:true}}).count()

SELECT COUNT(*) FROM people WHERE age > 30

db.people.count({age:{$gt:30}})

or

db.people.find({age:{$gt:30}}).count()

SELECT DISTINCT(status) FROM people

db.people.aggregate([{$group:{_id:"$status"}}])

or

db.people.distinct("status")

SELECT * FROM people LIMIT 1

db.people.findOne()

or

db.people.find().limit(1)

SELECT * FROM people LIMIT 5 SKIP 10 db.people.find().limit(5).skip(10)

 

Update Records

 

SQL Update Statements MongoDB updateMany() Statements
UPDATE people SET status = "C" WHERE age > 25 db.people.updateMany({age:{$gt:25}},{$set:{status:"C"}})
UPDATE people SET age = age +3 WHERE status = "A" db.people.updateMany({status:"A"},{$inc:{age:3}})

 

Delete Records

 

SQL Delete Statements MongoDB deleteMany() Statements
DELETE FROM people WHERE status = "D" db.people.deleteMany({status:"D"})
DELETE FROM people db.people.deleteMany({})

 

여기까지 자주 사용되는 SQL에 대한 MongoDB 쿼리 비교였습니다. 이보다 더 많은 기능들이 있지만 추후 천천히 다루어보겠습니다.

posted by 여성게
:
Database/MongoDB 2019. 9. 16. 21:16

 

이번 포스팅은 몽고디비 CRUD 3번째 글입니다. 이번 내용은 문서 update부터 다루어볼 예정입니다. 혹시나 이전 포스팅을 못 보신분들은 간단히 아래 링크에서 참고 부탁드립니다.

 

 

DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 1

이번 포스팅은 간단하게 MongoDB 사용법에 대해 다루어봅니다. 모든 쿼리는 특정 클라이언트 드라이버를 이용하는 것이 아니라, Shell을 이용하여 직접 쿼리를 작성해보는 내용입니다. 실습 이전에 혹시나 몽고디..

coding-start.tistory.com

 

DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 2

몽고디비 CRUD 사용방법을 다루는 포스팅 2번째 글입니다. 만약 첫번째 글을 못보신 분은 아래 링크를 참조하시길 바랍니다. DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 1 이번 포스팅은 간단하게 MongoDB..

coding-start.tistory.com

Update Documents

예제에 앞서 아래 문서들을 삽입해줍니다. 아래 문서들을 이용해서 예제를 진행할 것입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
db.inventory.insertMany( [
   { item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" },
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" },
   { item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
   { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" }
] );
cs

 

-Update Documents in a Collection

몽고디비에선 문서 업데이트를 위해 $set와 같은 업데이트 연산자를 제공하여 필드값을 수정한다. $set과 같은 몇몇 수정 연산자는 필드가 존재하지 않을 경우 필드를 생성해준다.

 

-Update a Single Document

db.collection.updateOne() 명령어를 이용해서 단일 문서 수정을 수행한다.

 

> db.inventory.updateOne({item:"paper"},{$set:{"size.uom":"cm",status:"P"},$currentDate:{lastModified:true}})

 

위 쿼리는 item이 paper인 첫번째 문서의 임베디드 문서 필드인 size 필드의 uom값을 cm으로 status필드의 값을 P로 바꾸어준다. 그리고 $currentDate 연산자를 사용하여 lastModified 필드의 값을 현재 날짜로 업데이트한다. 만약 lastModified 필드가 없으면 $currentDate가 필드를 만들어 값을 넣어준다.

 

-Update Multiple Documents

db.collection.updateMany() 명령어를 이용해서 다수의 문서를 한번에 수정한다.

 

> db.inventory.updateMany({qty:{$lt:50}},{$set:{"size.uom":"in",status:"P"},$currentDate:{lastModified:true}})

 

위 쿼리는 qty 필드의 값이 50보다 작은 모든 문서에 대해 $set 연산자에 대한 필드를 수정하고, lastModified 필드에 변경 날짜와 시간을 넣어준다.

 

-Replace a Document

_id 필드를 제외한 문서의 전체 내용을 바꾸려면 완전히 새로운 문서를 두 번째 인수로 db.collection.replaceOne()에 전달한다.

 

문서를 교체 할 때 교체 문서는 필드 / 값 쌍으로 만 구성되어야한다. 즉, 업데이트 연산자 표현식을 포함하면 안된다.

 

대체 문서는 원본 문서와 다른 필드를 가질 수 있다. 대체 문서에서 _id 필드는 변경할 수 없으므로 _id 필드를 생략 할 수 있다. 그러나 _id 필드를 포함 시키면 현재 값과 동일한 값을 가져야한다.

 

> db.inventory.replaceOne({item:"paper"},{ item:"paper", instock:[{warehouse:"A",qty:60},{warehouse: "B", qty: 40}]})

 

위 쿼리는 item이 paper인 첫번째 문서를 두번째 인자인 문서로 교체해준다. _id 필드를 생략하였으므로 기존과 동일한 _id필드를 가지며, 혹시나 _id 필드를 넣더라도 대체하길 원하는 문서의 _id값과 동일하게 넣어줘야한다. 

 

여기까지 간단히 수정 쿼리을 다루어봤는데, 조금더 자세히 수정쿼리에 대해 다루어보자.

 

Method 설명
db.collection.updateOne()

여러 문서가 지정된 필터와 일치하더라도 지정된 필터와 일치하는 최대 하나의 문서를 업데이트한다.

버전 3.2의 새로운 기능.

db.collection.updateMany() 지정된 필터와 일치하는 모든 문서를 업데이트한다.
db.collection.replaceOne()

여러 문서가 지정된 필터와 일치하더라도 지정된 필터와 일치하는 최대 하나의 문서를 대체한다.

 

버전 3.2의 새로운 기능

db.collection.update()

지정된 필터와 일치하는 단일 문서를 업데이트하거나 지정된 필터와 일치하는 모든 문서를 업데이트한다. 

기본적으로 해당 Method는 단일 문서를 업데이트한다. 여러 문서를 업데이트하려면 multi 옵션을 사용한다.

 

-추가적인 업데이트 Method

Method
db.collection.findOneAndReplace()
db.collection.findOneAndUpdate()
db.collection.findAndModify()
db.collection.save()
db.collection.bulkWrite()

 

Delete Documents

문서 삭제에 대한 예제를 다루어봅니다.

 

-Delete All Documents

특정 컬렉션의 모든 문서를 삭제하기 위해서는 필터 값에 아무런 값을 넣지 않고 db.collection.deleteMany() 메서드를 작성하면 된다.

 

> db.inventory.deleteMany({})

 

해당 컬렉션에 모든 문서를 삭제하고 삭제대상이 된 문서의 갯수를 결과로 반환한다.

 

-Delete All Documents that Match a Condition

삭제할 문서를 식별하는 기준 또는 필터를 지정할 수 있다. 필터는 조회 쿼리와 동일한 구문을 사용한다.

 

> db.inventory.deleteMany({status:"A"})

 

위 쿼리는 status 값이 A인 모든 문서를 삭제한다.

 

-Delete Only One Document that Matches a Condition

지정된 필터와 일치하는 문서를 하나만 삭제할때 db.collection.deleteOne()을 사용한다.

 

> db.inventory.deleteOne({status:"D"})

 

Delete Behavior

-Indexes 

컬렉션에서 모든 문서를 삭제하더라도 인덱스는 삭제하지 않는다. (이것은 인덱스의 내부 구조에 대한 내용까지 들어갈 듯한데, 보통 B+Tree에서 인덱스 삭제 연산이 있더라도 삭제 플래그값만 남기고 값은 바로 삭제하지 않았던 걸로 기억. 정확히 아시는 분은 댓글로 좀 부탁드립니다...)

 

삭제 연산에 사용되는 Method들은 아래와 같다.

 

Method 설명
db.collection.deleteOne()

여러 문서가 지정된 필터와 일치하더라도 지정된 필터와 일치하는 문서를 하나만 삭제한다.

버전 3.2의 새로운 기능.

db.collection.deleteMany()

지정된 필터와 일치하는 모든 문서를 삭제한다.

버전 3.2의 새로운 기능.

db.collection.remove() 지정된 필터와 일치하는 단일 문서 또는 모든 문서를 삭제한다. 

 

-추가적인 문서삭제 Method

Method 설명
db.collection.findOneAndDelete() 해당 메서드는 정렬 옵션을 제공한다. 이 옵션을 사용하면 지정된 순서대로 정렬된 첫번째 문서를 삭제한다.
db.collection.findAndModify() 해당 메서드는 정렬 옵션을 제공한다. 이 옵션을 사용하면 지정된 순서대로 정렬된 첫번째 문서를 삭제할 수 있다.
db.collection.bulkWrite()  

 

Bulk Write Operations

몽고디비는 클라이언트에게 벌크작업을 수행할 수 있는 기능을 제공한다. 벌크작업은 단일 컬렉션에 대해서만 수행가능하다. 몽고디비는 애플리케이션이 벌크작업에 필요한 수용 가능한 Acknowledgement 수준을 결정할 수 있도록한다.

 

db.collection.bulkWrite() 메소드는 대량 삽입, 업데이트 및 제거 작업을 수행하는 기능을 제공한다. MongoDB는 db.collection.insertMany()를 통한 대량 삽입도 지원한다.

 

-Ordered vs Unordered Operations

벌크작업은 작업에 순서가 있거나 순서가 맞지 않을 수 있다.

 

순서가 지정된 작업 목록을 사용하여 MongoDB는 작업을 순차적으로 실행한다. 쓰기 작업 중 하나를 처리하는 동안 오류가 발생하면 MongoDB는 목록에 남아있는 쓰기 작업을 처리하지 않고 반환한다.

 

정렬되지 않은 작업 목록을 사용하면 MongoDB가 작업을 병렬로 실행할 수 있지만이 동작은 보장되지 않는다. 쓰기 작업 중 하나를 처리하는 동안 오류가 발생하면 MongoDB는 목록에 남아있는 쓰기 작업을 계속 처리한다.

 

분할 된 컬렉션에서 정렬 된 작업 목록을 실행하면 정렬 된 목록을 사용하여 정렬되지 않은 목록을 실행하는 것보다 일반적으로 속도가 느려진다. 각 작업은 이전 작업이 완료 될 때까지 기다려야한다.

 

기본적으로 bulkWrite ()는 순서가 지정된 작업을 수행한다. 순서가없는 쓰기 작업을 지정하려면 옵션 문서에서 ordered : false를 설정하면 된다.

 

즉, 벌크 연산은 아래와 같은 오퍼레이션과 동일한 역할을 수행할 수 있다.

 

insertOne
insertMany
updateOne
updateMany
replaceOne
deleteOne
deleteMany

 

1
2
3
{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },
{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },
{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }
cs

 

characters 컬렉션에는 위와 같은 문서가 있다고 가정한다. 이 컬렉션에 아래와 같이 벌크 연산을 수행할 수 있다.

 

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
try {
   db.characters.bulkWrite(
      [
         { insertOne :
            {
               "document" :
               {
                  "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
               }
            }
         },
         { insertOne :
            {
               "document" :
               {
                  "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
               }
            }
         },
         { updateOne :
            {
               "filter" : { "char" : "Eldon" },
               "update" : { $set : { "status" : "Critical Injury" } }
            }
         },
         { deleteOne :
            { "filter" : { "char" : "Brisbane"} }
         },
         { replaceOne :
            {
               "filter" : { "char" : "Meldane" },
               "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
            }
         }
      ]
   );
}
catch (e) {
   print(e);
}
cs

 

벌크 연산의 결과는 아래와 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
   "acknowledged" : true,
   "deletedCount" : 1,
   "insertedCount" : 2,
   "matchedCount" : 2,
   "upsertedCount" : 0,
   "insertedIds" : {
      "0" : 4,
      "1" : 5
   },
   "upsertedIds" : {
 
   }
}
cs

 

샤드 컬렉션에 벌크 연산 전략

초기 데이터 삽입 또는 일상적인 데이터 가져오기를 포함한 벌크삽입 연산 작업은 샤드 클러스터 성능에 영향을 줄 수 있다. 벌크삽입의 경우 다음 전략들을 고려할 수 있다.

 

-컬렉션 사전 분할

샤딩된 컬렉션이 비어 있다면, 컬렉션은 하나의 초기화된 청크만 갖고 있는다. 그런 다음 몽고디비는 데이터를 수신한 이후에 분할을 생성하고 분할 청크를 사용 가능한 샤드에 분배한다. 즉, 리소스가 어느정도 큰 작업이 될것이다. 이 성능 비용을 줄이기위해 벌크작업 이전에 컬렉션을 사전 분할 할 수 있다.

 

Split Chunks in a Sharded Cluster — MongoDB Manual

Split Chunks in a Sharded Cluster Normally, MongoDB splits a chunk after an insert if the chunk exceeds the maximum chunk size. However, you may want to split chunks manually if: you have a large amount of data in your cluster and very few chunks, as is th

docs.mongodb.com

-순서없는 벌크 연산

샤드 클러스터에 대한 쓰기 성능을 향상시키려면 벌크연산의 선택적인 매개변수인 orderded를 false로 설정하면 된다. 이 설정은 순서없이 작업을 수행하는데, 병렬로 수행하기 때문에 여러 샤드에 동시에 작업수행이 가능하다. 하지만 이 작업을 하기 전에는 컬렉션이 미리 분할되어 있어야한다.

 

여기까지 수정,삭제,벌크 연산에 대해 다루어봤습니다. 이후 포스팅에서 더 다양한 연산들을 다루어보겠습니다.

posted by 여성게
:
Database/MongoDB 2019. 9. 16. 16:42

 

몽고디비 CRUD 사용방법을 다루는 포스팅 2번째 글입니다. 만약 첫번째 글을 못보신 분은 아래 링크를 참조하시길 바랍니다.

 

 

DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 1

이번 포스팅은 간단하게 MongoDB 사용법에 대해 다루어봅니다. 모든 쿼리는 특정 클라이언트 드라이버를 이용하는 것이 아니라, Shell을 이용하여 직접 쿼리를 작성해보는 내용입니다. 실습 이전에 혹시나 몽고디..

coding-start.tistory.com

 

예제를 위해서 아래 문서들을 삽입합니다.

 

1
2
3
4
5
6
7
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 } ] }
]);
cs

 

Query for a Document Nested in an Array

 

> db.inventory.find({instock:{warehouse:"A",qty:5}})

 

위 쿼리는 instock라는 중첩 필드에 조건에 일치하는 배열 요소가 있다면 쿼리 결과에 포함시킨다. 이전 포스팅에서도 간단하게 얘기했듯이 중첩 필드가 특정 문서이고 문서 전체의 일치를 조건에 넣으면 필드의 순서가 결과에 영향을 미친다. 위 쿼리에서 필드의 순서를 바꾸면 결과값은 나오지 않는다.

 

db.inventory.find({instock:{qty:5,warehouse:"A"}})

 

Specify a Query Condition on a Field Embedded in an Array of Document

중첩된 필드가 배열이며 배열의 요소가 특정 문서일 경우 "배열이름.배열요소의필드"로 조회조건을 걸수 있다.

 

> db.inventory.find({"instock.qty":{$lte: 20}})

 

위 쿼리는 instock 필드 안에 들어있는 배열 요소(문서) qty 필드 값중 20보다 작거나 같은 문서가 하나라도 포함되어 있다면 결과값에 포함시킨다.

 

Use the Array Index to Query for a Field in the Embedded Document

배열 요소 인덱스에 접근하여 쿼리 조건을 작성할 수 있다.

 

> db.inventory.find({"instock.0.qty":{$lte:20}})

 

위 쿼리는 instock 필드 배열의 첫번째 요소의 qty 필드 값이 20보다 작거나 같은 요소가 포함되어 있다면 결과에 포함시킨다.

 

두 개 이상의 조건을 모두 만족하는 배열의 요소를 조건으로 쿼리 작성이 가능하다.

 

> db.inventory.find({instock:{$elemMatch:{qty:5,warehouse:"A"}}})

 

위 쿼리는 instock 배열의 요소중 qty가 5이고 그리고 warehouse가 A 요소인 두 개의 조건을 모두 만족하는 배열요소가 있다면 결과에 포함시킨다.

 

> db.inventory.find({instock:{$elemMatch:{qty:{$gt:10,$lte:20}}}})

 

위 쿼리는 instock 배열의 요소의 필드인 qty의 값이 10보다 크거나 20보다 작거나 같은 필드 값이 하나라도 존재한다면 결과값에 포함시킨다.(AND)

 

> db.inventory.find( { "instock.qty": { $gt: 10,  $lte: 20 } } )

 

위 쿼리 같은 경우 바로 위에 쿼리랑은 다르게 10보다 크거나 20보다 작은 필드 조건을 하나씩이라도 만족하는 문서가 있다면 결과값에 포함시킨다.

 

 

결과값을 보면 4개의 문서 모두 10보다 큰 값을 가지고 있는 요소, 20보다 작거나 같은 값을 가진 요소를 하나씩 가지고 있다.

 

> db.inventory.find({"instock.qty":5,"instock.warehouse":"A"})

 

 

위 쿼리의 결과를 보면 qty가 5인 문서를 요소로 가지고 있고 warehouse가 A인 문서를 가지고 있는 배열요소를 포함하는 문서가 결과값으로 노출되고 있다. 사실 조금 헷갈릴 수 있다. 첫번째 결과를 보면 instock의 첫번째 요소에 warehouse == A, qty ==5라는 조건을 모두 가지고 있기에 결과에 포함됬고 두번째 결과는 각 조건이 서로다른 문서에 매칭이 되므로 결과값에 포함되었다.(첫번째 요소에 warehouse == A, 두번째 요소에 qty == 5)

 

 

Project Fields to Return from Query

쿼리의 결과로 문서의 모든 필드를 반환하는 것이 아니라 특정 필드만 결과값으로 내보낼 수 있다.

 

예제를 들어가기 앞서 아래의 문서를 삽입한다.

 

1
2
3
4
5
6
7
db.inventory.insertMany( [
  { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
  { item: "notebook", status: "A",  size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
  { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
  { item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
  { item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
cs

 

우선 결과값으로 모든 필드를 노출시키려면 아래와 같은 쿼리를 작성한다.

 

> db.inventory.find({status:"A"})

 

SQL에서는 위 쿼리가 SELECT * FROM inventory WHERE status = "A"와 같다.

 

만약 지정된 필드와 _id 필드만 반환하고 싶다면 아래와 같이 쿼리를 작성하면 된다.

 

> db.inventory.find({status:"A"},{item:1,status:1}) 

 

숫자 1이 의미하는 바는 쿼리 결과에 해당 필드를 포함시키겠다라는 뜻이다. 그렇다면 아래와 같은 쿼리의 결과는 어떻게 될까?

 

> db.inventory.find({status:"A"},{item:1,status:1,_id:0})

 

_id 필드를 포함시키지 않는 결과값을 반환한다. 즉, SQL에서는 SELECT item, status FROM inventory WHERE status ="A"와 같다.

 

임베디드된 문서의 특정 필드만 결과값으로 노출시킬 수 있다. 아래 쿼리를 참고하자.

 

> db.inventory.find({status:"A"},{item:1,status:1,"size.uom":1})

 

해당 쿼리의 결과값은 아래와 같으며 size라는 임베디드 문서 필드중 uom만 결과값으로 노출시킨다.

 

 

> db.inventory.find(

...    { status: "A" },

...    { "size.uom": 0 }

... )

 

만약 위와 같은 쿼리를 작성했다면 size의 uom 필드를 제외한 모든 필드를 결과값으로 반환한다.

 

 

Query for Null or Missing Fields

몽고디비의 쿼리에서 다루는 null은 우리가 생각하는 것과 조금 다르게 동작한다. 아래 문서를 삽입한 후에 다음 쿼리를 보자.

 

1
2
3
4
db.inventory.insertMany([
   { _id: 1, item: null },
   { _id: 2 }
])
cs

 

db.inventory.find( { item: null } )

 

우리는 위 쿼리가 어떻게 동작될 것이라고 예상할까? 필자는 item필드를 포함하며 item 필드의 값이 null인 문서가 결과값으로 나올 것이라 생각했다. 하지만 결과는 아래와 같다.

 

 

값이 null이거나 해당 필드 자체가 없는 문서도 결과값에 포함되게 된다.

 

> db.inventory.find( { item : { $type: 10 } } )

 

위 쿼리는 item 필드의 $type이 10인 필드를 조회하는 것인데 $type 10은 item 필드를 가지고 있으면서 값이 null인 문서만 결과값에 포함시킨다.

 

> db.inventory.find( { item : { $exists: false } } )

 

위 쿼리는 $type이 10인 쿼리와는 반대로(?) item 필드자체가 없는 문서를 결과값에 포함시킨다.

 

 

Iterate a Cursor in the mongo Shell

몽고디비에선 특정 조회의 결과 값으로 Cursor를 리턴한다. 하지만 var 키워드로 한 변수로 커서를 할당하지 않으면 몽고 디비는 결과 개수만큼 반복문을 통한 결과값 출력을 하게 된다. 그렇다면 커서를 특정 변수에 할당해서 우리가 직접 출력시켜보자.

 

 

위의 이미지 같이 우리는 커서를 특정 변수로 할당 받아 직접 반복문을 작성해 결과를 출력할 수 있다. 그리고 while 대신 아래와 같은 코드 작성도 가능하다.

 

> myCursor.forEach(printjson);

 

그리고 Iterator 결과값을 일일이 반복문으로 출력하는 것이 아니라, 배열 요소로 컨버팅해서 특정 요소에만 접근 할수도 있다.

 

 

toArray() 메서드는 cursor가 참조하고 있는 모든 문서를 RAM에 로드시킨다. 또한 cursor를 소진시키기 때문에 더 이상 cursor는 사용할 수 없게 된다.

 

또한 위와 같이 일부 드라이버 장치는 직접 커서객체에 인덱스로 요소에 접근할 수 있게 한다. 하지만 위 같은 경우는 동일한 커서를 계속 사용할 수 있다.(myCursor[0]을 두번 호출해도 같은 결과값 반환. 즉 myCursor[0]으로 사용해도 커서는 소모되지 않는다.)

 

기본적으로, 서버는 10 분 동안 활동이 없거나 클라이언트가 커서를 모두 사용한 경우 커서를 자동으로 닫는다. mongo 셸에서이 동작을 재정의하려면 cursor.noCursorTimeout () 메서드를 사용하면 된다.

 

> var myCursor = db.inventory.find().noCursorTimeout();

 

noCursorTimeout 옵션을 설정 한 후 cursor.close ()를 사용하여 커서를 수동으로 닫거나 커서 결과를 소진해야한다.

 

기타 커서에 관한 많은 내용이 있지만 나머지는 추후에 다루어보도록 할 것이다. 여기까지 두번째 CRUD 포스팅을 마친다. 다음 포스팅은 Update Document부터 다룰 예정이다.

posted by 여성게
:
Database/MongoDB 2019. 9. 13. 18:55

 

이번 포스팅은 간단하게 MongoDB 사용법에 대해 다루어봅니다. 모든 쿼리는 특정 클라이언트 드라이버를 이용하는 것이 아니라, Shell을 이용하여 직접 쿼리를 작성해보는 내용입니다. 실습 이전에 혹시나 몽고디비 설치가 되어 있지 않다면 설치가 필요합니다. 필자는 Docker를 이용하여 몽고디비를 설치하였으며 방법은 아래 링크를 참조하시면 됩니다. 또한 모든 예제는 몽고디비 공식 도큐먼트를 기준으로 작성하였습니다. (번역에 익숙치 않아 가끔 잘못된 번역이 있을 수도 있습니다.)

 

 

Docker - Docker로 MongoDB 설치하기.

이번 포스팅은 Docker를 이용하여 MongoDB를 설치하는 방법이다. 도커 실행 후 MongoDB 이미지를 받아준다. 이미지가 잘 받아진 것을 확인한다. 이제 내려받은 이미지를 실행시키자. 각 옵션은 이전 도커를 이..

coding-start.tistory.com

 

모든 실습환경은 MacOS 환경에서 작성되었습니다. 또한 몽고디비 버전은 v4.2.0 기준으로 작성하였습니다.

 

> docker exec -it mongodb bash

 

터미널에서 위의 명령으로 몽고디비 컨테이너로 접속합니다.

 

위의 이미지처럼 "mongo"라는 명령어를 입력하면 쉘을 이용해 몽고디비 서버에 접속하게 됩니다. 여기까지 실습을 위한 준비입니다.

 

 

몽고디비는 위와 같이 BSON구조의 도큐먼트로 데이터를 다루며, 해당 도큐먼트의 모음으로 Collection이라는 단어를 사용합니다. 그리고 이러한 Collection의 모음은 하나의 database라는 큰 공간에 존재하게 됩니다. 그럼 당연히 database를 생성하는 게 첫번째입니다.

 

Mongo RDBMS
database database
collection table
document row 혹은 record
field column

 

<User 생성>

기본적으로 몽고디비가 설치가 되면 별도의 인증없이 접속이 가능합니다. 그렇기 때문에 유저를 생성하여 해당 데이터베이스에 접속할때 간단한 아이디/패스워드로 인증설정을 해주어야합니다.

 

 

첫번째는 admin database에 유저를 생성해주는 것이고 두번째는 우리가 예제로 사용할 test_database의 유저를 생성해주는 예제입니다. 하지만 우리는 도커를 이용해 컨테이너를 실행시킬때 빼먹은 것이 있습니다. docker run 명령어 입력시 마지막에 --auth를 붙여주어야 원격에서 몽고디비 서버에 붙을 때 인증을 요구합니다.

 

그리고 인증된 유저로 데이터베이스를 이용하기 위해서는 여러가지 방법이 있지만 여기서는 "mongo"로 서버에 접속한 후 사용하기 위한 데이터베이스로 스위치합니다.

 

>use test_database

 

그리고 해당 데이터베이스를 활용하기 위해 인증을 해줍니다.

 

>db.auth("testuser","testuser") ->id/pwd

 

 

<Database 생성>

사실 몽고디비에는 database라는 생성 명령어가 존재하지 않습니다. database 생성 시점은 해당 database를 사용하고 첫번째 데이터가 삽입 혹은 인덱스가 생성된 시점에 자동으로 생성됩니다.

 

> use test_database

 

위 명령어를 입력하여 서버에게 데이터베이스 사용을 알려줍니다.

 

 

결과값입니다. 이것은 마치 Git에서 특정 branch로 checkout하는 것과 비슷한 결과값을 보여줍니다.

 

> db.testcollection.insertOne({name:"yeoseong",age:28,address:{si:"seoul",gu:"gangbuk"}})

 

위의 명령어로 데이터를 하나 삽입해줍니다.

 

 

위 이미지와 같은 결과값이 나왔다면 삽입성공입니다. 디폴트로 생성된 문서 아이디를 반환해줍니다. 그리고 해당 데이터베이스의 컬렉션으로 처음 데이터가 삽입된 시점에 database와 collection이 생성됩니다. 물론 collection 같은 경우는 다양한 옵션 조작을 위해 명시적으로 생성가능합니다.

 

여기서 하나의 명령어를 더 다루어봅니다.

 

> db.getCollectionInfos()

 

위 명령어는 해당 데이터베이스의 모든 컬렉션의 정보를 보여줍니다.

 

 

컬렉션의 UUID는 샤드 클러스터의 모든 Replica-Set 및 샤드에 동일하게 유지되는 불변값입니다. 아마 클러스터의 모든 컬렉션을 유일하게 식별하기 위한 아이디값이 아닐까 싶습니다.

 

<Index 생성>

RDBMS에 인덱스가 존재하듯 몽고디비에도 인덱스라는 개념이 존재합니다. 하지만 이러한 인덱스는 스키마 구조를 처음에 작성해주어야 하기 때문에 몽고디비가 100% schema-less한 NoSQL이 아니라고 합니다.

 

> db.testcollection.createIndex({name:1})

 

위의 명령으로 인덱스를 생성해줍니다. 인자로 인덱스로 사용할 필드 이름과 숫자가 들어갑니다. 여기서 숫자는 1,-1 값을 입력할 수 있습니다. 이 숫자가 의미하는 바는 인덱스의 내부 자료구조를 살펴봐야합니다. 기본적으로 몽고디비는 B-Tree라는 자료구조로 인덱스를 생성합니다. B-Tree는 내부적으로 정렬된 형태의 자료구조이므로 이 말은 인덱스의 데이터 값도 정렬되서 들어간다는 뜻입니다. 즉, 1은 오름차순, -1은 내림차순으로 인덱스 값을 정렬하는 설정입니다. 

 

그리고 인덱스는 하나가 아니고 여러개의 필드를 지정할 수 있습니다. 사실 이렇게 인덱스를 필요로하는 컬렉션은 명시적으로 스키마 정보를 입력해줘야 하지만 인덱스 이외의 문서의 필드는 schema-less한 특성을 갖습니다.

 

마지막으로 위에서 몽고디비는 인덱스 자료구조로 B-Tree를 이용한다 했는데, 사실 B-Tree만 사용하는 것이 아닙니다. 문서 특성에 따라 다양한 인덱스 자료구조를 갖습니다. 이 내용은 추후에 다루어보도록 하겠습니다.

 

<MongoDB CRUD>

 

1)Insert삽입

 

-단일 문서삽입

 

> db.testcollection.insertOne({name:"yeoseong",age:28,address:{si:"seoul",gu:"gangbuk"}})

 

만약 _id 필드를 명시하지 않으면 랜덤한 ObjectId를 자동으로 생성해줍니다. 필드 데이터 타입은 추후에 제대로 다루어볼 것이지만, 날짜,배열,오브젝트,문자열,숫자 등 다양한 타입을 지원합니다. 그나마 우리가 사용하는 JSON 구조이기에 이해하기가 편합니다.

 

위의 단일 삽입 명령어를 작성하면 할당된 ObjectId가 결과값으로 반환됩니다.

 

-벌크 문서삽입(다수 문서를 한번에 삽입)

 

> db.testcollection.insertMany([{name:"sora"},{name:"yoon"}])

 

위의 명령을 수행하면 2개의 문서를 한번에 삽입합니다.

 

여기서 하나 의문점이 드는 것이 트랜잭션인데, 과연 2개의 문서를 삽입하는 명령 하나가 하나의 트랜잭션으로 묶일까? 답은 아니다 입니다. 내부적으로는 2개의 문서를 삽입하는 명령을 각각 하나의 삽입문으로 스플릿해서 각각이 하나의 트랜잭션을 갖게 됩니다. 이 말은 첫번째 sora가 삽입성공하고 yoon삽입이 실패하면 서로 각각 다른 트랜잭션을 갖기때문에 sora는 삽입된 상태에서 롤백되지 않습니다. 왜냐하면 몽고디비는 단일 문서 수준의 트랜잭션을 제공하기 때문입니다.

(3.4버전 기준 트랜잭션 내용인데.. 현재 4.x는 어떻게 되는지 모르겠내요.. 혹시 아시는 분은 댓글 부탁드립니다.)

 

_id Field

몽고디비에서 각각의 도큐먼트는 기본키 역할을 하는 _id 필드를 꼭 가져야한다. 문서 삽입 오퍼레이션에서 _id 필드를 생략한다면, 몽고디비는 ObjectId를 자동으로 생성하여 _id 필드에서 넣어준 후에 결과값으로 해당 ObjectId를 반환한다.

 

Atomicity

몽고디비는 단일 문서 기준으로 원자성을 제공한다. 위에서도 얘기했던 다수 문서를 한번에 삽입하는 벌크 문장에서 하나의 명령으로 여러개의 문서를 삽입한다고 해도, 하나의 트랜잭션으로 묶이는 것이 아닌 각각의 오퍼레이션에 대한 트랜잭션이 각각 실행된다.

 

Insert Methods

몽고디비는 컬렉션에 문서를 삽입하기위한 메서드를 아래와 같이 제공한다.

 

Method 설명
db.collection.insertOne() 컬렉션에 단일 문서 삽입.
db.collection.insertMany() 컬렉션에 다수 문서 삽입.
db.collection.insert() 컬렉션에 단일 혹은 다수 문서 삽입.

 

또한 추가적으로 삽입에 사용되는 메서드이다.

 

Method 설명
db.collection.update() "upsert: true" 옵션 사용시
db.collection.updateOne() "upsert: true" 옵션 사용시
db.collection.updateMany() "upsert: true" 옵션 사용시
db.collection.findAndModify() "upsert: true" 옵션 사용시
db.collection.findOneAndUpdate() "upsert: true" 옵션 사용시
db.collection.findOneAndReplate() "upsert: true" 옵션 사용시
db.collection.save()  
db.collection.bulkWrite()  

 

위 메서드들은 따로 update 쿼리 부분에서 다루어볼 것이다.

 

 

2)Query Document

 

-Select All Document in a Collection

매개변수를 생략하고 단순히 조회 메서드를 이용하면 컬렉션 안에 있는 모든 문서를 조회할 수 있다. 매개변수로 들어가는 쿼리 필터 파라미터는 조회 기준을 결정한다.

 

> db.collection.find({}) / db.collection.find()

 

위의 메서드는 마치 SQL에서 "SELECT * FROM inventory" 와 같은 오퍼레이션과 같다.

 

-Specify Equality Condition

동치 조건을 지정하기 위해서, 조회 메서드 매개변수로 <field>:<value> 형식의 쿼리 필터 파라미터를 전달한다.

 

> db.collection.find( { <field>:<value> } )

 

> db.testcollection.find( { name:"yeoseong" } )

 

위 오퍼레이션에 대한 결과값이다.

 

 

위 메서드는 SQL에서 SELECT * FROM testcollection WHERE name = "yeoseong"과 같은 오퍼레이션과 같다.

 

-Specify Conditions Using Query Operators

조회 메서드에 쿼리 오퍼레이터를 이용하여 어떠한 조건을 지정할 수 있다.

 

> db.testcollection.find( { age:{ $in:[27,28,29] } } )

 

age가 27,28,29 인 문서를 조회한다. (OR) $or 조건을 사용할 수 있지만 같은 필드로 동치를 수행할 때는 $in을 사용하라고 몽고디비 문서에서는 권장함.

 

-Specify AND Conditions

복합 쿼리는 컬렉션의 문서를 조회하기 위해 하나 이상의 필드에 대해 다수의 조건을 지정할 수 있다. ","로 구분하면 암시적으로 AND 연산자가 사용되어 일치하는 문서를 찾는다.

 

>db.testcollection.find( { name:"yeoseong",age: { $lt:30 } })

 

위 쿼리절은 나이가 30살보다 어리면서 이름이 yeoseong인 문서를 찾는다. 이것은 SQL에서 SELECT * FROM testcollection WHERE name = "yeoseong AND age < 30 과 같다.

 

-Specify OR Conditions

$or 오퍼레이션을 이용해서 하나 이상의 조건과 일치하는 문서를 찾는 조회쿼리를 작성할 수 있다.

 

>db.testcollection.find( {$or:[{name:"yeoseong"},{name:"sora"}]} )

 

위 쿼리절은 이름이 yeoseong 이거나 sora인 문서를 찾는다. SQL에서는 SELECT * FROM testcollection WHERE name = "yeoseong OR name = "sora"와 같다.

 

-Specify AND as well as OR Conditions

AND와 OR 조건을 같이 사용하여 복합 쿼리 작성도 가능하다.

 

>db.testcollection.find({age:{$lt:30},$or:[{name:/^y/},{name:/^s/}]})

 

위 쿼리절은 나이가 30살보다 어리고 이름이 y로 시작하거나 s로 시작하는 문서를 찾는다. SQL에서는 SELECT * FROM testcollection WHERE age < 30 AND ( name LIKE "y%" OR name LIKE "s%" ) 와 같다. 여기서 사용되는 /^y/ 같은 연산자는 문자열 타입으로 지정된 인덱스 필드에 쿼리 성능 향상에 도움이 되는 연산자이다. 

 

 

DB - MongoDB 조건으로 일부 문자열만 사용하여 인덱스이용하기

이번 포스팅할 내용은 짧은 내용이다. 아래와 같은 Document가 있다고 생각해보자. {_id:"1234",name:"yeoseong",age:"28",address:"aaa"} 위의 도큐먼트에서 인덱스된 필드가 "name"필드라고 가정하자. 위의 도..

coding-start.tistory.com

 

> db.testcollection.findOne() 

 

위의 명령어는 단일 문서를 읽어오는 오퍼레이션이다.

 

-Query on Embedded/Nested Document

쿼리 필터 파라미터를 이용하여 embedded/nested한 필드에 대한 조건을 지정할 수 있다. 이 말은 하나의 필드안에 또 다른 문서가 들어간 중첩 필드에 대한 쿼리 조건절을 지정할 수 있다는 뜻이다.

 

db.testcollection.find({"address.si":"seoul"})

 

위 쿼리절은 address 필드 안에 있는 문서 중 si 필드가 seoul인 문서를 조회한다. 이 쿼리에 대한 결과이다.

 

 

여기서 하나 주의해야 할것이 있다. 아래와 같은 쿼리절 두개가 있다고 생각하자.

 

> db.testcollection.find({address:{si:"seoul",gu:"gangbuk"}}) 결과o

> db.testcollection.find({address:{gu:"gangbuk",si:"seoul"}}) 결과x

 

우리는 대게 위의 결과가 같다고 생각할 것이다. 하지만 하나는 결과값이 나오고, 하나는 결과값이 나오지 않는다. 그 이유는 임베디드된 문서 필드 전체에 대해 조건절을 수행할 경우 필드의 순서를 같게 넣어줘야 한다. 위의 결과 이미지를 보면 address 임베디드는 si,gu 순서로 필드가 되어 있으므로, 조회 조건에도 동일한 순서로 넣어줘야한다.

 

-Query on Nested Field

위는 임베디드되거나 중첩된 문서에 대한 매칭을 수행한 예제였다면, 이번은 임베디드되거나 중첩된 문서의 특정 필드를 매칭시키는 쿼리이다. 점 표기법을 사용한다. 점표기법을 사용하는 중첩된 필드를 다룰 때는 꼭 더블쿼테이션 마크 안에 필드를 넣어야한다.

 

db.testcollection.find({"address.si":"seoul"})

 

임베디드 되거나 중첩된 필드에도 여러 연산자 사용이 가능하다.

 

> db.testcollection.find({"address.si":{$gt:"a"}})

 

하나의 연산자 예제이며 다양한 연산자($or,and 등)적용이 가능하다.

 

-Query an Array

배열에 관한 쿼리를 다루기 전에 밑의 문서들을 삽입하자.

 

1
2
3
4
5
6
7
db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
   { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
   { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
   { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
   { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
cs

 

-Match an Array

배열에서 동등 조건을 지정하려면 쿼리 문서 {<field> : <value>}를 사용한다. 여기서 <value>는 요소의 순서를 포함하여 일치시킬 정확한 배열이다.

 

> db.inventory.find({tags:["red","blank"]})

 

위 쿼리는 tags라는 배열에 red와 blank가 포함되있으며 순서까지 일치하는 배열을 가진 문서를 조회한다. red와 blank 순서를 바꾸면 결과값이 다르게 나온다.

 

만약 순서와 상관없이 조건에 해당되는 배열 요소만 가지고 있으면 쿼리조건에 부합하는 결과를 가져오는 쿼리는 아래와 같은 연산자($all)를 이용한다.

 

> db.inventory.find({tags:{$all:["red","blank"]}})

 

이 쿼리는 red와 blank 요소를 가지기만 하면된다. 즉, 위 요소 이외에 다른 요소가 있어도 위 두개만 가지고 있으면 쿼리 결과로 반환된다.

 

-Query an Array for an Element

배열 필드에 조건에 명시한 요소 하나만 있어도 결과값으로 노출시키고 싶다면 아래와 같은 쿼리를 작성한다.

 

> db.inventory.find({tags:"red"})

 

tags의 요소로 "red"를 대괄호로 묶지 않으면 된다.

 

> db.inventory.find( {dim_cm:{$gt:25}} )

 

배열 필드도 역시 다양한 연산자 적용이 가능하다.

 

-Specify Multiple Conditions for Array Elements

배열도 다수의 조건을 조합한 쿼리 필터 지정이 가능하다. 간단한 예제를 살펴보자.

 

Query an Array with Compound Filter Conditions on the Array Elements

> db.inventory.find({dim_cm:{$gt:15,$lt:20}})

 

 

위 쿼리절은 dim_cm이 15보다 큰 요소가 있거나 20보다 작은 배열의 요소가 하나라도 매칭되면 쿼리 결과로 반환된다.(OR 조건이 된다.)

즉, 각 조건에 대한 or 조건이 되는 것이다. 하지만 모든 조건을 충족하는 요소를 찾길 원한다면 아래와 같은 연산자를 사용한다.

 

Query for an Array Element that Meets Multiple Criteria

>db.inventory.find({dim_cm:{$elemMatch:{$gt:22,$lt:30}}})

 

 

위 쿼리는 22보다 크거나 30보다 작은 두개의 조건을 모두 만족하는 배열의 요소가 하나라도 있으면 결과값에 포함시킨다.($elemMatch)

 

Query for an Element by the Array index Position

점 표기법을 사용하면 배열의 특정 인덱스 또는 위치에서 요소에 대한 쿼리 조건을 지정할 수 있다. 배열은 0부터 시작하는 인덱싱을 사용한다.

 

> db.inventory.find({"dim_cm.1":{$gt:25}})

 

Query an Array by Array Lenght

쿼리 조건으로 배열의 요소수로 조건 쿼리가 가능하다.

 

> db.inventory.find({tags:{$size:3}})

 

위 쿼리는 tags 필드 중 요소를 3개 가지고 있는 문서를 결과값에 포함시킨다.

 

 

생각보다 포스팅이 길어져 여기서 한번 끊고 2번 포스팅으로 넘어가려고 합니다. 다음 포스팅은 임베디드된 필드 배열에 관한 쿼리로 시작합니다.

posted by 여성게
:
인프라/Docker&Kubernetes 2019. 9. 13. 17:33

 

이번 포스팅은 Docker를 이용하여 MongoDB를 설치하는 방법이다.

 

 

도커 실행 후 MongoDB 이미지를 받아준다.

 

 

이미지가 잘 받아진 것을 확인한다.

 

 

이제 내려받은 이미지를 실행시키자. 

 

각 옵션은 이전 도커를 이용하여 오라클을 받는 옵션과 크게 다르지 않으므로 이전 포스팅을 참고하자. 위에서는 아니지만 만약 위에 실행한 명령에 마지막에 "--auth"를 붙인다면, 몽고디비 서버에 접속할때 인증이 필요하게 된다. 

 

> docker run --name mongodb -v localpath:containerpath -d -p 27017:27017 mongo --auth

 

유저를 만들기 위한 설명은 아래 링크참조를 참조하면 될듯하다.

 

 

DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 1

이번 포스팅은 간단하게 MongoDB 사용법에 대해 다루어봅니다. 모든 쿼리는 특정 클라이언트 드라이버를 이용하는 것이 아니라, Shell을 이용하여 직접 쿼리를 작성해보는 내용입니다. 실습 이전에 혹시나 몽고디..

coding-start.tistory.com

 

Mac OS - docker oracle11g 설치

mac os 환경에서 docker를 이용한 oracle 11g 설치 우선 진행하기 전에 자신의 os에 맞는 docker를 다운로드 해줍니다. 1)docker pull wnameless/oracle-xe-11g =>oracle 11g 도커이미지 다운로드, 여기서 이미지..

coding-start.tistory.com

 

 

잘 실행되고 있다 ! 역시 도커란 녀석은 아주 편하다.

 

이후 아래 명령어를 통해 컨테이너에 접속해 조작가능하다.

 

> docker exec -it mongodb bash

 

 

> mongo

 

mongo 명령으로 몽고디비서버를 실행시켜서 shell 환경에서 조작할 수 있다.

 

 

그리고 마운트한 디렉토리를 들어가보니 아래와 같은 파일들이 생겨있다.

 

 

요즘 막 몽고디비를 공부하고 있는데, 기본으로 WiredTiger를 스토리지 엔진으로 사용하고 있는 것 같다. 몽고디비가 플러그인해서 쓸 수있는 스토리지 엔진이 WiredTiger 말고도 더 있는데, 각각 구현되고 있는 방식이 달라 트랜잭션 등의 기능이 조금씩 다르게 제공된다는 데, 이런 것들은 추후에 다루어 봐야겠다.

posted by 여성게
:
Database/MongoDB 2019. 9. 13. 16:09

 

이번 포스팅할 내용은 짧은 내용이다. 아래와 같은 Document가 있다고 생각해보자.

 

{_id:"1234",name:"yeoseong",age:"28",address:"aaa"}

 

위의 도큐먼트에서 인덱스된 필드가 "name"필드라고 가정하자. 위의 도큐먼트를 검색하기 위해서는 아래와 같은 쿼리를 작성할 수 있을 것이다.

 

db.users.find({name:"yeoseong"})

 

위 쿼리의 조건에는 이미 인덱스로 스키마가 등록된 필드이기 때문에 데이터를 풀스캔하는 것이 아니라 인덱스를 서칭해서 결과값을 받아올 것이다. 하지만 조건 값에 일부로만 검색을 하면서 인덱스를 스캔하기 위해서는 어떻게 해야할까?

 

인덱스 프리픽스(Prefix) 스캔 : 문자열 좌측 일치 검색

문자타입의 필드 인덱스가 있고 일부 문자열로 인덱스를 검색하고 싶다면 아래와 같은 조건을 지켜주면 된다.

 

  1. 반드시 문자열의 처음부터 일치하도록 문자열 시작 표시("^")로 정규 표현식이 시작돼야 한다.
  2. 검색 문자열이 "시작 표시(^)" 이외의 정규 표현식을 포함하지 않아야 한다.
  3. 문자열의 마지막을 표현하는 "$" 표시가 없어야 한다.

이 조건들은 SQL에서 자주 사용되는 좌측 일치 LIKE 연산자( " name LIKE 'yeo%' ")와 같은 조건이다. 이렇게 정규 표현식이 나열되면 몽고디비 서버는 레인지 검색을 통해 인덱스를 스캔하게 된다. 즉, 풀 스캔이 아닌 인덱스 스캔을 이용하는 것이다. 이는 문자열만이 아니라 날짜 타입이나 숫자 타입의 필드에도 동일하게 적용된다.

posted by 여성게
: