
▪ Document DB : BSON(Binary JSON)
▪ Auto Sharding
▪ Replica Set
▪ Index : Geospatial(위치정보 처리 index), Hashed, Unique, Spars, Compound
- Embedded Document, Array 필드도 인덱싱 가능
- 구체(Sphere) 모델 적용한 위치 데이터 인덱싱 지원 → 지구는 둥그니까..
- Full Text Search Index(한글 형태소 분석x), elasticsearch 사용을 권장
- Index를 사용한 TTL Collection
▪ Map/Reduce, Aggregation 기능 내장
- 대부분의 NOSQL은 자체 집계 기능을 제공하지 않음
▪ GridFS : grid file system. 한개의 document를 여러개 document로 분리하여 streaming서비스 등 개발 가능
▪ Javascript Shell : 완벽한 js shell 지원
> var x=1;
> var y=2;
> x+y
3
> function add(x,y){return x+y;}
> add(5,6)
11
만약, RDB라면 order, customers, order lines, credit cards와 같이 4개 이상의 table이 필요하며, join query가 필요.
반면 mongoDB는 1개 document에 array와 embedded document로 조합 가능

▪ show dbs : 데이터베이스 목록을 보여줌
▪ show collections : 현재 선택된 데이터베이스내의 컬렉션 목록을 보여줌
▪ use <dbname> : 데이터베이스를 선택함. 존재하지 않은 데이터베이스도 선택이 가능하다. 존재하지 않는 데이터베이스를 선택하였을 경우 데이터베이스 관련 물리적인 파일은 데이터베이스내에 컬렉션이 생성될 때 할당된다.
▪ db.<collection>.find() : 조회기능. 여러건의 Document를 조회할 때 사용함. findOne()은 조건에 부합되는 첫번째 Document 한건만을 리턴함.
▪ db.<collection>.insert(doc) : Document를 추가함.
▪ db.<collection>.update(cond, doc, option) : 조건에 부합되는 Document를 찾아 업데이트함. option이 부여되지 않으면 조건에 부합되는 Document 들중 첫번째 한건만 수정함.
▪ db.createCollection(colName, option) : 컬렉션의 명시적 생성. 암시적으로도 생성 가능
▪ cls : 화면 지우기
> use test
switched to db test
> show collections
users
> db.users.insert({userid:"gdhong",name:"홍길동"})
WriteResult({ "nInserted" : 1 })
> db.users.find()
{ "_id" : ObjectId("5d36c316412126c94e66bdb4"), "userid" : "gdhong", "name" : "홍길동" }
▪ _id 필드 : 고유 키 역할 수행
- 명시적으로 입력하지 않으면 ObjectID 객체 값이 자동 부여
- 명시적으로 입력하면 입력한 값으로 부여됨. 유일성이 확보되지 않은 경우 입력 불가
- ObjectID

▪ cond : 조회 조건
▪ doc : 수정하려는 document
▪ option : { multi: false/true, upsert : false/true }
- multi(default false) : 한꺼번에 여러개 document 업데이트하지 않는다. 첫번째꺼 하나만 업데이트한다. noSQL이 atomic transaction이라는 개념을 유지하기 위해 default는 false이다.
- upsert(defult false) : 수정할 대상이 없는 경우 insert동작.
update 주의사항
▪ 원자적 업데이트
▪ 조건에 부합되는 document 들중 첫번째 것만 업데이트
- 여러건의 document를 업데이트하려면 multi:true 옵션을 부여함.
- 조건에 부합되는 document가 없을 경우 insert하려면 upsert:true 옵션 부여
▪ 내부적으로 문서 전체 치환이 일어남.
- 특정 필드만 업데이트하려면 적절한 제한자 사용
▪ _id 필드 값은 변경 불가
update : 문서 전체 치환
▪ 제한자의 필요성!!
- 특정 필드만 업데이트 하기 위해서...
- $set, $inc, $addToSet, $push, $pop, $pull 등
▪ upsert와 $inc : collection은 자동으로 생성됨.
db.peoples.update({ _id:"gdhong" }, { name:"홍길동" }) // 모든 필드 치환
db.peoples.update({ _id:"gdhong" }, {$set: { name:"홍길동" }}) // 해당 필드만 update
배열과 내장 문서 업데이트
▪ Document 구조의 강점
> db.users.insert({
_id : "A001", name : "홍길동",
phones : { home : "02-2211-5678", office : "02-3429-1234" }, email : [ "gdhong@hotmail.com", "gdhong@gmail.com" ]
})
WriteResult({ "nInserted" : 1 })
> db.users.update({ _id:"A001" }, { $set : { "phones.home" : "02-7788-9900" } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne({ _id:"A001" })
{
"_id" : "A001",
"name" : "홍길동",
"phones" : {
"home" : "02-7788-9900",
"office" : "02-3429-1234"
},
"email" : [
"gdhong@hotmail.com",
"gdhong@gmail.com"
]
}
> db.users.update({ _id:"A001" }, { $addToSet : { email : "gdhong7788@daum.net" } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne({ _id:"A001" })
{
"_id" : "A001",
"name" : "홍길동",
"phones" : {
"home" : "02-7788-9900",
"office" : "02-3429-1234"
},
"email" : [
"gdhong@hotmail.com",
"gdhong@gmail.com",
"gdhong7788@daum.net"
]
}
Multi Update : 다수 docuement 한번에 update
▪ db.<collection>. remove(cond) - 조건에 부합되는 데이터만 삭제
- 인덱스, 컬렉션은 그대로 유지
▪ db.<collection>.drop()
- 데이터 뿐만 아니라 컬렉션에 설정된 Secondary Index까지 삭제
▪ db.dropDatabase()
- 현재 데이터베이스 전체 삭제
▪ db.<collection>.find(cond, projection)
- cond : 조회 조건
- projection : 조회하고자 하는 필드 지정
▪ 조건 제한자
- $lt, $lte, $gt,$gte
- $or
db.scores.find({ score : { $gte : 91 } }, { name:1, score:1, _id: 0 })
db.scores.find({ score : { $gte : 80 } }) db.scores.find({ score : { $lte : 71, $gte : 80 } })
조회조건으로 null을 사용할 경우 필드가 존재하지 않는 document도 조회함
> db.scores.insert({_id:1, kor : 80 })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:2, kor : 90 })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:3, kor : null })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:4, eng : null })
WriteResult({ "nInserted" : 1 })
> db.scores.find({ kor : null })
{ "_id" : 3, "kor" : null }
{ "_id" : 4, "eng" : null }
> db.scores.find({ eng : null })
{ "_id" : 1, "kor" : 80 }
{ "_id" : 2, "kor" : 90 }
{ "_id" : 3, "kor" : null }
{ "_id" : 4, "eng" : null }
이 문제를 해결하기 위해 $exists 제한자를 사용하여 추가적인 조건을 부여하면 됨.
> db.scores.find({ eng : { $in : [ null ], $exists : true } });
{ "_id" : 4, "eng" : null }
like절이 없으므로 정규식(REGEXRegExr) query를 사용해야만 함
//도시 이름이 ABE로 시작하는 것들
db.zipcodes.find({ city : /^ABE/})
//도시 이름이 ABE로 시작하고 뒤에 5글자의 alphanumeric이 이어지는 경우만 조회
db.zipcodes.find({ city : /^ABE(\w{5})$/})
//도시 이름이 A또는 B로 시작하고 마지막이 C로 끝나는 것들
db.zipcodes.find({ city : /^(A|B)\w+C$/})
배열을 조회시, 포함여부에 대해 확인하는 구문과 분리하여 사용하여야 한다.
> db.food.insert({ _id : 01, fruit : ["apple","banana","peach"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 02, fruit : ["apple","pineapple","orange"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 03, fruit : ["apple","banana","strawberry"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 04, fruit : ["apple","banana"] })
WriteResult({ "nInserted" : 1 })
// list에 포함되면 모두 조회됨.
> db.food.find({ fruit : "banana"})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "apple", "banana", "strawberry" ] }
{ "_id" : 4, "fruit" : [ "apple", "banana" ] }
embedded document 검색시 따옴표(.) 을 사용하여 부분 값 비교 수행
> db.persons.insert({ _id : 1,
... name : { first: "John", last : "Kennedy" }, age : 40 });
db.persons.insert({ _id : 2, name : { first: "Will", last : "Smith" }, age : 31 });
db.persons.insert({ _id : 3, name : { first: "Barack", last : "Obama" }, age : 60 });
db.persons.insert({ _id : 4,
name : { first: "John", middle : "F", last : "Kennedy" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 2, name : { first: "Will", last : "Smith" }, age : 31 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 3, name : { first: "Barack", last : "Obama" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 4,
... name : { first: "John", middle : "F", last : "Kennedy" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.find({ name : { first : "John", last : "Kennedy" } });
{ "_id" : 1, "name" : { "first" : "John", "last" : "Kennedy" }, "age" : 40 }
> db.persons.find({ "name.first" : "John", "name.last" : "Kennedy" });
{ "_id" : 1, "name" : { "first" : "John", "last" : "Kennedy" }, "age" : 40 }
{ "_id" : 4, "name" : { "first" : "John", "middle" : "F", "last" : "Kennedy" }, "age" : 60 }
NOSQL 데이터 모델링
▪ findOne() : document 리턴
▪ find() : cursor객체 리턴
var cursor = db.persons.find(); while (cursor.hasNext()) {
var doc = cursor.next();
print(doc.name.first + " " + doc.name.last); }
▪ cursor 객체의 메서드
- sort(), limit(), skip()
- count()
- hasNext(), next()
//scores 컬렉션에서 name,score 필드만 조회하여 성적순으로 내림차순 정렬한 뒤
//앞에서 5개를 skip하고 10개만 조회함.
db.scores.find({ }, { name : 1, score : 1 }).sort({ score : -1 }).skip(5).limit(10);
- 설정방법은 아래 코드와 같음.
- embedded document 내부 value도 index설정이 가능.
// name 순 오름 차순 정렬된 인덱스 생성
db.scores.ensureIndex({ name : 1 });
// 복합 인덱스
db.scores.ensureIndex({ name : 1, scores : 1});
// 내장 문서 인덱스
db.persons.ensureIndex({ "name.last" : 1 });
// 배열 필드 인덱스(Multikeys 인덱스)
db.food.ensureIndex({ fruit:1 })
특수 인덱스
- sparse index
- unique index
- geospatial index - text index
- hashed Index
▪ 항상 이전 단계의 결과물이 다음 단계의 입력이 됨.
▪ shard key로 집계된 key로 집계를 수행하면 훨씬 성능이 빠름.
- 분산 reduce를 위해서는 shard가 반드시 필요.
Example) 1억건의 data가 있으면 4개 node에 2천5백개가 분산되어 정리하면 훨씬 빠름
▪ Map 결과가 1건이면 shuffling, reduce를 실행하지 않음
▪ shuffling은 mongodb 내부적으로 자동 수행됨
▪ finalize는 선택적임. reduce를 한번더 가공해야한다면 사용.

1) map으로 단어를 자름 + 셔플링
2) Reduce(group화)
요구사항 : words collection에서 sentence key의 field에 존재하는 단어의 개수를 map reduce로 count 조회
1) 데이터 입력
> db.words.insert({sentence : "peter Piper picked a peck of pickled peppers"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "A peck of pickled peppers Peter Piper picked"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "If Peter Piper picked a peck of Peppers"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "Where's the peck of pickled peppers Peter Piper picked"})
WriteResult({ "nInserted" : 1 })
2) map, reduce function 정의
var map1 = function(){
// 주의사항 : 좌측은 반드시 tab이 아닌 space
var arr = this.sentence.match(/\w+/g); // 영어로된 단어 추출
for(var i=0;i<arr.length;i++){
emit(arr[i].toLowerCase(),{count:1})
}
}
var reduce1 = function(key, values){ // values=[{count:1},...]
var result = {count:0}; // map의 values 형식과 동일하게 맞춰줘야함
for(var i=0;i<values.length; i++){
result.count += values[i].count;
}
return result;
}
3) mapReduce function 실행
> db.words.mapReduce(map1, reduce1, {
out : { replace : "mr" }
})
{
"result" : "mr",
"timeMillis" : 304,
"counts" : {
"input" : 4,
"emit" : 34,
"reduce" : 8,
"output" : 12
},
"ok" : 1
}
4) 결과물 조회 : mr collection 조회
> db.mr.find();
{ "_id" : "a", "value" : { "count" : 3 } }
{ "_id" : "if", "value" : { "count" : 1 } }
{ "_id" : "of", "value" : { "count" : 4 } }
{ "_id" : "peck", "value" : { "count" : 4 } }
{ "_id" : "peppers", "value" : { "count" : 4 } }
{ "_id" : "peter", "value" : { "count" : 4 } }
{ "_id" : "picked", "value" : { "count" : 4 } }
{ "_id" : "pickled", "value" : { "count" : 3 } }
{ "_id" : "piper", "value" : { "count" : 4 } }
{ "_id" : "s", "value" : { "count" : 1 } }
{ "_id" : "the", "value" : { "count" : 1 } }
{ "_id" : "where", "value" : { "count" : 1 } }
▪ pipeline 순서대로 처리
- $project, $match, $limit, $skip, $unwind, $group, $sort
▪ 이전 pipeline의 처리 결과는 다음 pipeline의 입력물이 됨.
요구사항 : nasdaq 데이터에서 평균 종가를 구해보자
1) 데이터 입력 : 외부 bson file을 mongodb에 import
in mongodb/4.0.2/bin
$ ./mongorestore -d nasdaq -c stock /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:12.749+0900 checking for collection data in /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:12.948+0900 restoring nasdaq.stock from /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:15.645+0900 [###.....................] nasdaq.stock 105MB/715MB (14.6%)
2019-07-24T11:30:18.648+0900 [#######.................] nasdaq.stock 217MB/715MB (30.3%)
2019-07-24T11:30:21.647+0900 [###########.............] nasdaq.stock 332MB/715MB (46.4%)
2019-07-24T11:30:24.645+0900 [##############..........] nasdaq.stock 429MB/715MB (59.9%)
2019-07-24T11:30:27.645+0900 [#################.......] nasdaq.stock 536MB/715MB (75.0%)
2019-07-24T11:30:30.646+0900 [#####################...] nasdaq.stock 631MB/715MB (88.2%)
2019-07-24T11:30:33.013+0900 [########################] nasdaq.stock 715MB/715MB (100.0%)
2019-07-24T11:30:33.013+0900 no indexes to restore
2019-07-24T11:30:33.013+0900 finished restoring nasdaq.stock (4308303 documents)
2019-07-24T11:30:33.013+0900 done
2) import 파일 확인
> db.stock.findOne()
{
"_id" : ObjectId("4d094f58c96767d7a0099d4a"),
"exchange" : "NASDAQ",
"stock_symbol" : "AACC",
"date" : "2008-03-06",
"open" : 9.03,
"high" : 9.03,
"low" : 8.41,
"close" : 8.56,
"volume" : 353600,
"adj close" : 8.56
}
3) index설정 : aggregate시 빠르게 동작을 위함. 없어도 동작이 되긴 됨
> db.stocks.createIndex({stock_symbol:1})
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
4) 기본 aggregate 테스트 : 전체 document가 다 return됨.
> db.stock.aggregate([ {$match:{stock_symbol:/^G/}} ])
{ "_id" : ObjectId("4d094f7cc96767d7a027cc8a"), "exchange" : "NASDAQ", "stock_symbol" : "GABC", "date" : "2008-03-07", "open" : 12.94, "high" : 12.94, "low" : 12.94, "close" : 12.94, "volume" : 100, "adj close" : 12.94 }
{ "_id" : ObjectId("4d094f7cc96767d7a027cc8c"), "exchange" : "NASDAQ", "stock_symbol" : "GABC", "date" : "2008-03-05", "open" : 12.89, "high" : 12.89, "low" : 12.89, "close" : 12.89, "volume" : 0, "adj close" : 12.89 }
...
5) aggregate 파이프라인 코드 작성
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
}
])
{ "_id" : "GYRO", "avgclose" : 29.26192069392813, "maxclose" : "GYRO" }
{ "_id" : "GTXI", "avgclose" : 12.439757516973811, "maxclose" : "GTXI" }
{ "_id" : "GTOP", "avgclose" : 7.405551181102362, "maxclose" : "GTOP" }
{ "_id" : "GRIN", "avgclose" : 1.9769006211180125, "maxclose" : "GRIN" }
{ "_id" : "GSOL", "avgclose" : 11.176468151216985, "maxclose" : "GSOL" }
{ "_id" : "GSIG", "avgclose" : 10.095608017817371, "maxclose" : "GSIG" }
{ "_id" : "GUID", "avgclose" : 12.86569579288026, "maxclose" : "GUID" }
{ "_id" : "GSIC", "avgclose" : 12.884677023712182, "maxclose" : "GSIC" }
{ "_id" : "GLUU", "avgclose" : 8.947736625514404, "maxclose" : "GLUU" }
{ "_id" : "GSBC", "avgclose" : 25.978493150684933, "maxclose" : "GSBC" }
...
6) aggregate 파이프라인 코드 + 정렬
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
}
// 평균종가 내림차순으로 정렬(평균종가가 큰게 상위)
,{$sort:{avgclose:-1}}
])
// 구글 1위
{ "_id" : "GOOG", "avgclose" : 389.0802908277405, "maxclose" : "GOOG" }
{ "_id" : "GFIG", "avgclose" : 57.10188461538462, "maxclose" : "GFIG" }
{ "_id" : "GRMN", "avgclose" : 47.309299503585216, "maxclose" : "GRMN" }
{ "_id" : "GENZ", "avgclose" : 45.8726703539823, "maxclose" : "GENZ" }
7) aggregate 파이프라인 코드 + 정렬 + collection output
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
},
// 평균종가 내림차순으로 정렬(평균종가가 큰게 상위)
{$sort:{avgclose:-1}},
// collectionOut 설정
{$out: "stocks_g_avg_max_close"}
])
> db.stocks_g_avg_max_close.find({_id:"GOOG"}) // stocks_g_avg_max_close collection
{ "_id" : "GOOG", "avgclose" : 389.0802908277405, "maxclose" : "GOOG" }
| 기능 | Map/reduce | Aggregate |
| 대상 데이터 | 비정형, 반정형, 정형 모두 가능 | 정형, 반정형만 처리 가능 |
| 작성방식 | JS 함수 코딩 | Stage(Pipeline) 과 Expression 조합 |
| 실행환경 | JS 런타임 | Native |
| 실행결과 | 1.Out Collection (집계결과를 다른 collection에 저장) 2.Document |
1.Cursor 2.Out Collection |
| 작업방식 | 주로 Batch Job | 실시간 처리, Batch Job 모두 처리 가능 |
| 분산집계 (shard 환경, multi node) |
지원 | 지원 |
| 속도 | 느림 | 몇몇 경우 제외하고 더 빠름 |
아주 불편함. 각 key의 value에 대한 type을 모두 지정해줘야함.
MongoClient mongo = new MongoClient("localhost:27017");
mongo.setWriteConcern(WriteConcern.SAFE);
MongoDatabase test = mongo.getDatabase("test");
MongoCollection<Document> persons = test.getCollection("persons");
//FindIterable<Document> cursor = persons.find(new Document("_id", "gdhong123"));
MongoCursor<Document> cursor = persons.find().iterator();
System.out.println("이름, 국어, 영어, 국사, 수학");
System.out.println("======================================");
while (cursor.hasNext()) {
Document doc = cursor.next();
String name = doc.getString("name");
Document scores = (Document)doc.get("scores");
int kor = scores.getInteger("kor");
int eng = scores.getInteger("eng");
int his = scores.getInteger("his");
int mat = scores.getInteger("mat");
System.out.println(name + ", " + kor + ", " + eng + ", " + his + ", " + mat);
}
mongo.close();
java 객체 ↔ bson
Jongo : Query in java as in mongo shell
SHELL : db.friends.find({age: {$gt: 18}})
JAVA DRIVER : friends.find(new BasicDBObject("age",new BasicDBObject("$gt",18)))
JONGO : friends.find("{age: {$gt: 18}}").as(Friend.class)
Jongo 예제코드
DB db = new MongoClient("localhost:27017").getDB("test2");
Jongo jongo = new Jongo(db);
MongoCollection persons = jongo.getCollection("persons");
List<String> emails1 = new ArrayList<String>();
emails1.add("gdhong@gmail.com");
emails1.add("gdhong123@yahoo.com");
Person p1 = new Person("gdhong", "홍길동", new Score(100,70,60,90), emails1);
persons.insert(p1);
List<String> emails2 = new ArrayList<String>();
emails2.add("mrlee@hotmail.com");
emails2.add("mrlee121@gmail.com");
Person p2 = new Person("mrlee", "이몽룡", new Score(80,90,70,70), emails2);
persons.insert(p2);
System.out.println("test2 에 쓰기 완료!!");
▪ 전형적인 Master/Slave 구조
- Master : 쓰기/읽기
- Slave : 읽기
▪ 자동장애조치 가능 : Replica Set(Primary/Secondary)
- Heartbeat 기반의 장애 노드 탐지, Primary 선출 기능
▪ 샤드키 직접 지정
- Cassandra, Hbase : Rowkey = 샤드키
- 특정 DB, Collection 만 샤딩하도록 지정
▪ 샤딩 설정 정보를 저장하는 Config DB를 별도로 둠
▪ mongos : 라우터 역할
- 자동 샤딩 : 밸런싱, 청크 분할
→ https://blog.voidmainvoid.net/241
NoSQL강의) mongoDB에서 data 모델링하는 방법. 예제포함.
MongoDB 주요 특징 Secondary Index ▪ 다른 NOSQL 보다 secondary index 기능이 발달되어 있음 샤드키 지정 ▪ _id : 키 필드 ▪ Shard Key <> _id - 대부분의 NOSQL은 Row Key = Shard Key 임 Document 기반 ▪..
blog.voidmainvoid.net
| mongodb shell에서 서로다른 database의 데이터 비교하기 (0) | 2019.08.22 |
|---|---|
| mongodb shell에서 db 이름 명시하여 데이터 조회하기 (0) | 2019.08.22 |
| NoSQL강의) mongoDB에서 data 모델링하는 방법. 예제포함. (3) | 2019.07.25 |
| NoSQL강의) Document Database 개요 및 설명 (0) | 2019.07.23 |
| NoSQL강의) DynamoDB 개요, 특징 및 설명 (0) | 2019.07.23 |
| NoSQL강의) HBase 개요, 특징, client 설명 + Apache Phoenix (0) | 2019.07.23 |