Post List

레이블이 MongoDB인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MongoDB인 게시물을 표시합니다. 모든 게시물 표시

2015년 9월 18일 금요일

MongoDB Study #27 MongoDB C# 연동하기 part 3 비정형 document 다루기 (insert, update, remove, find)

* 비정형 document 란 ? (unstructured document)

RDBMS에서는 한 table 내의 모든 record 들의 column은 동일합니다.
MongoDB에서는 document 들의 field가 미리 정의되어 있지 않을 뿐더러
동일 collection 내의 document 들의 field들이 서로 다를 수 있습니다.
이런 것을 비정형 data라고 합니다.

앞의 posting에서 정형 data에 대해서는 미리 field 정의와 동일한 형태의 class를 생성 한 뒤 document를 해당 class의 객체로 선언하여 사용하였습니다.

* BsonDocument

비정형 document에서는 정형 document같이 class를 미리 선언 할 수가 없습니다.
그래서 BsonDocument 객체를 이용해서 data를 입출력합니다.
BsonDocument 객체는 key : value 의 pair 들을 포함하는 collection 이라고 생각하면 이해가 쉽게 될것입니다.
value 로는 string, int 등의 값 뿐만 아니라 배열, BsonDocument 도 가능합니다.

BsonDocument에 대한 member 및 method 들의 대한 자세한 정보는 API reference를 참조하시면 됩니다.

http://api.mongodb.org/csharp/1.0/html/3a31e174-4df4-91f3-6760-02078b53ddb1.htm

* 접속, 예제 Collection 및 FindAll

접속 및 collection 선언은 앞서 다룬 내용이라 생략하겠습니다.
그리고, 이번 posting에서는 타입 선언을 명시적으로 하지않고 그냥 var로 선언하겠습니다.
비정형 document에 대한 결과를 .ToList() 로 가져오면 List<BsonDocument> 형태로 저장됩니다. BsonDocument를 string 형태로 출력하려면 .ToJson()을 이용하면 됩니다.

var database = new MongoClient("mongodb://localhost").GetServer().GetDatabase("test");

// emp Collection
var emp = database.GetCollection("emp");
var cEmp = emp.FindAll();
var lstEmp = cEmp.ToList();
foreach (var e in lstEmp)
{
    Console.WriteLine(string.Format("{0}", e.ToJson()));
}

미리 만들어놓은 emp collection에는 다음의 정보가 들어 있었습니다.

* Insert

BsonDocument를 하나 생성하여 .Insert() 메서드를 이용하면 됩니다.

var luna = new BsonDocument { { "name""Luna" }, { "age"19 } };
emp.Insert(luna);

* FindOne , query 생성

luna 를 BsonDocument로 선언했을 때는 name 과 age 에 대한 field만 생성하였지만,
insert 작업 후에는 _id 라는 field로 생성되었습니다.
BsonDocument 내의 특정 field 값을 가져오는 메서드는 .GetValue(필드명) 입니다.
.FindOne(query 객체) 를 사용하면 해당 조건에 맞는 document 1개만을 가져옵니다.

var queryLuna = Query.EQ("_id", luna.GetValue("_id"));
var luna2 = emp.FindOne(queryLuna);

Console.WriteLine("Luna : {0}", emp.FindOne(queryLuna).ToJson());

* Update

.Update() 메서드에 query 객체와 UpdateBuilder 객체를 전달하여 수행하면 됩니다.

emp.Update(queryLuna, Update.Set("age"27));

Console.WriteLine("Luna : {0}", emp.FindOne(queryLuna).ToJson());

* Remove

설명이 필요 없을듯 합니다. 예제를 보시면 이해가 가능하실 겁니다.

var queryLuna2 = Query.EQ("name""Luna");
emp.Remove(queryLuna2);

var rl = emp.FindOne(queryLuna2);
if (rl == null)
    Console.WriteLine("Luna isn't exist");
else
    Console.WriteLine("Luna : {0}", rl.ToJson());

* Find , query 조건 여러개

.Find() 메서드의 사용도 앞서 배운 정형 document와 별로 다르지 않습니다.
query 조건이 1개 이상인 경우에는 Query.AND() 또는 Query.OR() 메서드에 query 객체들을 전달하는 식으로 구현이 가능합니다.

Console.WriteLine("");
Console.WriteLine("sal > 2000");
Console.WriteLine("");

foreach (var e in emp.Find(Query.GT("sal"2000)).ToList())
{
    Console.WriteLine(string.Format("{0}", e.ToJson()));
}

Console.WriteLine("");
Console.WriteLine("sal > 2000 and deptno = 20");
Console.WriteLine("");

var multiQuery = Query.And(Query.GTE("sal"2000), Query.EQ("deptno"20));
foreach(var e in emp.Find(multiQuery).ToList())
{
    Console.WriteLine(string.Format("{0}", e.ToJson()));
}

* 예제 코드


* 실행 결과


MongoDB Study #26 MongoDB C# 연동하기 part 2 정형화된 document 다루기 (insert, update, remove, find)

* 정형화된 Document 란 ? (structured document)

RDBMS에서 Data를 입력하기 위해서 table을 정의합니다.
table 안에는 column 정보가 있죠.
예를 들어서 회원정보라면 id, name, password, addr, tel 등의 정보가 있습니다.
이러한 column 정보들을 미리 정의해 놓아야만 table 생성이 가능합니다.
이렇듯 해당 data에 대해서 어떠어떠한 값이 필요하다라고 정의되어 있는 것을 정형 데이터라고 합니다.
MongoDB에서는 각각의 record를 document 라는 용어로 부르는 관계로 미리 정의해 놓은 field 값들을 class로 만든 뒤 해당 class를 이용해서 MongoDB의 collection을 제어하는 것을 정형화된 document라고 부르기로 합시다.
(합시다 ? 네. 정식용어는 아닙니다. 그냥 제가 그렇게 부르기로 한다는 것이구요.)

* 정형화 document 정의하기

_id와 name 값을 가지는 정형 document를 class로 정의하겠습니다.

public class Member
{
    public ObjectId Id { getset; }
    public string Name { getset; }
}

* collection 객체 선언

MongoCollection<class타입> 을 이용하여 collection을 선언합니다.
(일단 예제라서 명시적으로 타입을 선언했지 대부분의 경우 그냥 var로 선언합니다.)
MongoDB에 접속하는 방법은 이전 posting에서 다뤘으니 설명은 생략하겠습니다.

string connectionString = "mongodb://localhost";
MongoClient client = new MongoClient(connectionString);
MongoServer server = client.GetServer();
MongoDatabase database = server.GetDatabase("test");

// Member Collection
MongoCollection<Member> collection = database.GetCollection<Member>("member");

* Insert

collection.Insert(class 객체) 메서드를 이용하면 됩니다.

// Insert
Member e1 = new Member { Name = "Luna" };
collection.Insert(e1);
ObjectId id = e1.Id;

collection.Save(class 객체) 메서드로도 가능합니다.

// Save
Member e3 = new Member { Name = "Star" };
collection.Save(e3);

.Insert() .Save() 의 차이는 앞서 MongoDB 관련 posting에서 다뤘지만 짧게 다시 설명드리자면,
insert 작업시는 별 차이가 없으며,
save 는 document 단위의 insert, update 작업을 수행하며,
update 는 field 단위의 update 작업을 수행합니다.
사용하는 경우에 따라 성능의 차이가 있습니다.
document 전체값을 수정하는 경우에는 save가 효율적이고,
특정 field들만 수정하는 경우에는 update가 효율적 입니다.

* Find , FindOne, FindAll

Find() FindOne() 은 매개변수로 IMongoQuery 객체를 전달하여 특정 조건에 맞는 document를 찾을 수 있습니다.
query 객체를 만드는 방법은 예제를 참고하세요.
.EQ() , .GT() 등의 메서드로 구현되어 있으며, 조건이 여러개인 경우에는 .AND() , .OR() 메서드에 매개변수로 query 객체 2개를 전달하는 식으로 구현되어 있습니다.
2개 이상인 조건은 다음 posting 인 비정형 document 편에서 소개해드리겠습니다.
FindAll() 은 모든 document를 다 가져옵니다.

// FindOne
IMongoQuery query = Query<Member>.EQ(e => e.Id, id);
Member e2 = collection.FindOne(query);

// FindAll
MongoCursor<Member> cursor = collection.FindAll();
List<Member> L = cursor.ToList<Member>();
foreach (Member e in L)
{
    Console.WriteLine(string.Format("{0} : {1}", e.Id, e.Name));
}

기존에 RDBMS를 다룬 분들이 보기에는 좀 불편해 보일 수 있습니다.
왜 DataSet 이나 DataTable로 가져오지 않고 List로 가져와서 하나하나 제어해야하는지요.
저도 그게 불편해서 그냥 DataTable로 제어 가능하도록 라이브러리를 만들어볼까 생각을 했었지만, MongoDB는 비정형 data를 저장 할 수 있는 구조이므로 별 의미가 없겠다는 생각이 들었습니다.

* Update, Remove

앞서 본 FindOne() 의 경우와 같이 query 개체를 사용해서 특정 조건의 document 들만 수정 및 삭제가 가능합니다.

// Update
UpdateBuilder<Member> update = Update<Member>.Set(e => e.Name, "Silver");
collection.Update(query, update);

// Remove
collection.Remove(query);

* 전체 예제

2015년 9월 17일 목요일

MongoDB Study #25 MongoDB C# 연동하기 part 1 설치 및 접속

* MongoDB C# Driver 종류

MongoDB 의 C# Driver 는 1 버전과 2 버전이 있습니다.
1. 으로 시작하는 것은 예전과 같은 동기방식 (synchronous) 입니다.
2. 으로 시작하는 것은 비동기방식 (asynchronous) 입니다.

MongoDB 공식 홈페이지인 https://www.mongodb.org 에는 강려크 하게 2.0 버전에 대한 Driver 소개와 사용법을 소개하면서 그렇게 하도록 사용자를 푸시하고 있는데, 말리시면 안됩니다. 거기에 말리면 저 같이 시간 낭비만 죽도록 하고, 아~ 이건 잘 안되는구나. 란 것만 몸소 체험 할 수 있습니다. 물론 제 내공이 비동기 방식을 연마하기에 부족한 것일 수도 있겠죠.

비동기 방식으로 DML 쪽 작업 (insert, update, remove)은 잘되던데, query 수행은 제대로 값을 못가져 오더라구요.

1.0 대의 Driver로는 모든것이 쾌적하게 잘됩니다. 아주 잘됩니다.
아무래도 MongoDB 자체가 Big Data를 다루는게 주 목적이다 보니 async 로 작업하는게 좋겠지만, 아직까지 해당 Driver를 도입하기에는 제대로 구현되지 않았다고 봐야 겠네요.

그래서 결국은 1.0 버전대의 Driver를 다운받아서 자료를 찾아가면서 정리하였습니다.

* C# Driver 1.10.1 설치

다운로드 : https://github.com/mongodb/mongo-csharp-driver/releases

에서 1.10.1 을 다운로드 받으시면 됩니다.



압축을 푸셔서 MongoDB.Bson.dll , MongoDB.Driver.dll 2개의 파일을 사용할 project에서 references 에 추가하시면 됩니다.

* 접속

긴 설명 필요없이 그냥 아래와 같이 하시면 됩니다.

string connectionString = "mongodb://localhost";  // connection string : "mongodb://sys:mongo@localhost:27017/test"
MongoClient client = new MongoClient(connectionString);
MongoServer server = client.GetServer();
MongoDatabase database = server.GetDatabase("test");

예제라서 앞에 타입형식을 다 적어주었지만, 그냥 var 로 선언해서 사용하는 경우가 많습니다.
뒤에 포스팅에서 다루겠지만, 정형화된 document를 다루는 경우는 타입을 다 적어주는 것이 가능하지만, 비정형화된 document를 다룰때 하나하나 타입을 다 외워서 적어주는건 힘듭니다.
꼭 적어야 겠다면 일단 var 로 적어서 코드를 작성 후에 제대로 동작하는 것을 확인 한 후에 해당 객체에 마우스를 올리면 Visual Studio가 진철하게 타입을 알려줍니다. 그것을 확인 한 다음에 앞에 var을 지우시고 해당 타입으로 적어주시면 됩니다.

string connectionString = "mongodb://localhost";  // connection string : "mongodb://sys:mongo@localhost:27017/test"
var client = new MongoClient(connectionString);
var server = client.GetServer();
var database = server.GetDatabase("test");

아니면 그냥 한줄로도 가능하구요.

var database = new MongoClient("mongodb://localhost").GetServer().GetDatabase("test");

다음 포스팅에서 정형화된 document에 대해서 select, insert, update, delete 하는 법에 대해서 다루겠습니다.

* 예제코드

2015년 8월 20일 목요일

MongoDB Study #24 MongoDB Python 연동하기 Tutorial

1. pymongo 설치


에서 다운로드 받으시면 됩니다.
Windows 용일 경우 설치된 Python과 같은 버전 , (32/64)bit 제품의 install 파일을 다운받으셔서 실행하면 됩니다.
Registry에 등록된 Python 과 버전 및 bit 정보가 다른 경우에는 설치가 되지 않습니다.

2. MongoDB Server 구동 및 Test Data 입력

아래 Posting을 참조하여 Server 구동 및 Test Data를 입력해 주세요.

MongoDB Server 구동 참조

Test Collection 입력

Test 편의를 위해서 employees 를 emp로 이름을 변경하였습니다.
(mongo 로 접속하여 db.employees.renameCollection("emp") 실행)

3. Python에서 MongoDB 연결
import pymongo
 
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017')
db = client['test']
MongoClient에 MongoDB Connection String을 넣어주면 됩니다.
따로 인자로 서버이름 , 포트 번호를 넣어줘도 됩니다.
db 도 client.test 형식으로 해도 됩니다.
Python은 MongoDB Client(Mongo.exe) 를 실행해서 Shell에서 수행했던 명령어들과 유사하게 동작하므로 쉽습니다.

4. Collection Data 조회 및 출력

이것 또한 client의 shell 명령어와 유사합니다.
cursor = db.emp.find()
 
for document in cursor:
    print(document)
 
cursor = db.emp.find( { "sal" : { "$gt":2000} }, {"_id":0 } ).sort("sal", pymongo.ASCENDING)
 
for document in cursor:
    print(document)
db.collectjon.find() 명령어로 기본적인 조회가 가능하고,
조회 조건 및 출력하는 Field List, sort 등의 명령어가 shell 명령어와 유사합니다.
MongoDB 의 findOne() 명령어 (하나의 Document를 Return) 는 find_one() 으로 실행하면 됩니다.

위 Code의 실행 결과 입니다.
{'sal': 2975, 'ename': 'JONES', 'job': 'MANAGER', 'empno': 7566, 'deptno': 20, 'hiredate': '02-04-1981', '_id': ObjectId('55d129a8257cf8fbc83b5ffc')}
{'sal': 800, 'ename': 'SMITH', 'job': 'CLERK', 'empno': 7369, 'deptno': 20, 'hiredate': '17-12-1980', '_id': ObjectId('55d129a8257cf8fbc83b5ffa')}
{'sal': 1250, 'ename': 'MARTIN', 'job': 'SALESMAN', 'empno': 7654, 'deptno': 30, 'hiredate': '28-09-1981', '_id': ObjectId('55d129a8257cf8fbc83b5ffb')}
{'sal': 1600, 'ename': 'ALLEN', 'job': 'SALESMAN', 'empno': 7499, 'deptno': 30, 'hiredate': '20-02-1981', '_id': ObjectId('55d129a8257cf8fbc83b5ffd')}
{'sal': 1250, 'ename': 'WARD', 'job': 'SALESMAN', 'empno': 7521, 'deptno': 30, 'hiredate': '22-02-1981', '_id': ObjectId('55d129a8257cf8fbc83b5fff')}
{'sal': 2450, 'ename': 'CLARK', 'job': 'MANAGER', 'empno': 7782, 'deptno': 10, 'hiredate': '09-06-1981', '_id': ObjectId('55d129a8257cf8fbc83b6000')}
{'sal': 2850, 'ename': 'BLAKE', 'job': 'MANAGER', 'empno': 7698, 'deptno': 30, 'hiredate': '01-05-1981', '_id': ObjectId('55d129a8257cf8fbc83b5ffe')}
{'sal': 3000, 'ename': 'SCOTT', 'job': 'ANALYST', 'empno': 7788, 'deptno': 20, 'hiredate': '13-06-1987', '_id': ObjectId('55d129a8257cf8fbc83b6001')}
{'sal': 5000, 'ename': 'PRESIDENT', 'job': 'CEO', 'empno': 7839, 'deptno': 10, 'hiredate': '17-11-1981', '_id': ObjectId('55d129a8257cf8fbc83b6002')}
{'sal': 1500, 'ename': 'TURNER', 'job': 'SALESMAN', 'empno': 7844, 'deptno': 30, 'hiredate': '08-09-1981', '_id': ObjectId('55d129a8257cf8fbc83b6003')}
{'sal': 1100, 'ename': 'ADAMS', 'job': 'CLERK', 'empno': 7876, 'deptno': 20, 'hiredate': '13-06-1987', '_id': ObjectId('55d129a8257cf8fbc83b6004')}
{'sal': 950, 'ename': 'JAMES', 'job': 'CLERK', 'empno': 7900, 'deptno': 30, 'hiredate': '03-12-1981', '_id': ObjectId('55d129a8257cf8fbc83b6005')}
{'sal': 3000, 'ename': 'FORD', 'job': 'ANALYST', 'empno': 7902, 'deptno': 20, 'hiredate': '03-12-1981', '_id': ObjectId('55d129a8257cf8fbc83b6006')}
{'sal': 1300, 'ename': 'CLERK', 'job': 'CLERK', 'empno': 7934, 'deptno': 10, 'hiredate': '23-01-1982', '_id': ObjectId('55d129a8257cf8fbc83b6007')}
{'sal': 2450, 'ename': 'CLARK', 'job': 'MANAGER', 'empno': 7782, 'deptno': 10, 'hiredate': '09-06-1981'}
{'sal': 2850, 'ename': 'BLAKE', 'job': 'MANAGER', 'empno': 7698, 'deptno': 30, 'hiredate': '01-05-1981'}
{'sal': 2975, 'ename': 'JONES', 'job': 'MANAGER', 'empno': 7566, 'deptno': 20, 'hiredate': '02-04-1981'}
{'sal': 3000, 'ename': 'SCOTT', 'job': 'ANALYST', 'empno': 7788, 'deptno': 20, 'hiredate': '13-06-1987'}
{'sal': 3000, 'ename': 'FORD', 'job': 'ANALYST', 'empno': 7902, 'deptno': 20, 'hiredate': '03-12-1981'}
{'sal': 5000, 'ename': 'PRESIDENT', 'job': 'CEO', 'empno': 7839, 'deptno': 10, 'hiredate': '17-11-1981'}

5. Insert

json 형식의 document를 변수로 생성 한 후
db.collection.insert(document) 명령어로 쉽게 입력이 됩니다.
import datetime
 
doc = { "id" : "LunaStar" , "firstname" : "Seokjoon" , "lastname" : "Yun" , "date" : datetime.datetime.utcnow() }
 
try:
    db.users.insert(doc)
except:
    print("insert failed", sys.exec_info[0])
 
docRet = db.users.find_one()
print(docRet)
{'lastname': 'Yun', 'firstname': 'Seokjoon', '_id': ObjectId('55d50f3ff867d619c0819a93'), 'date': datetime.datetime(2015, 8, 19, 23, 20, 31, 939000), 'id': 'LunaStar'}

6. Bulk Insert

json 형식의 document를 배열로 만든후 insert_many() 함수를 이용하면 됩니다.
docs = [ { "id" : "Banana" , "firstname" : "Nana" , "lastname" : "Bar" , "date" : datetime.datetime(2015, 12, 31) },
         { "id" : "Orange" , "firstname" : "Range" , "lastname" : "Oh" , "date" : datetime.datetime(2001, 2, 1) } ]
result = db.users.insert_many(docs)
print(result.inserted_ids)
print(db.users.find_one({ "_id" : result.inserted_ids[0] }, { "_id" : 0 } ) )
print(db.users.find_one({ "_id" : result.inserted_ids[1] }, { "_id" : 0 } ) )
[ObjectId('55d51074f867d619c0819a94'), ObjectId('55d51074f867d619c0819a95')]
{'lastname': 'Bar', 'firstname': 'Nana', 'date': datetime.datetime(2015, 12, 31, 0, 0), 'id': 'Banana'}
{'lastname': 'Oh', 'firstname': 'Range', 'date': datetime.datetime(2001, 2, 1, 0, 0), 'id': 'Orange'}

7. Index

따로 설명 없이 바로 예제를 보겠습니다.
from pymongo import ASCENDING, DESCENDING
 
db.emp.create_index( [ ("empno", ASCENDING) , ("sal" , DESCENDING) ] )
plan = db.emp.find( { "sal" : { "$gt" : 2000 } }, { "_id" : 0 } ).sort("empno").explain()
print(plan)
{'executionStats': {'nReturned': 6, 'executionSuccess': True, 'executionStages': {'needFetch': 0, 'works': 15, 'needTime': 8, 'nReturned': 6, 'stage': 'PROJECTION', 'executionTimeMillisEstimate': 0, 'saveState': 0, 'restoreState': 0, 'invalidates': 0, 'transformBy': {'_id': 0}, 'isEOF': 1, 'inputStage': {'needFetch': 0, 'works': 15, 'needTime': 8, 'nReturned': 6, 'docsExamined': 14, 'stage': 'FETCH', 'executionTimeMillisEstimate': 0, 'saveState': 0, 'restoreState': 0, 'filter': {'sal': {'$gt': 2000}}, 'alreadyHasObj': 0, 'invalidates': 0, 'isEOF': 1, 'inputStage': {'needFetch': 0, 'works': 14, 'needTime': 0, 'nReturned': 14, 'stage': 'IXSCAN', 'matchTested': 0, 'executionTimeMillisEstimate': 0, 'saveState': 0, 'indexBounds': {'empno': ['[MinKey, MaxKey]'], 'sal': ['[MaxKey, MinKey]']}, 'restoreState': 0, 'invalidates': 0, 'dupsTested': 0, 'keyPattern': {'empno': 1, 'sal': -1}, 'isMultiKey': False, 'seenInvalidated': 0, 'indexName': 'empno_1_sal_-1', 'isEOF': 1, 'dupsDropped': 0, 'direction': 'forward', 'advanced': 14, 'keysExamined': 14}, 'advanced': 6}, 'advanced': 6}, 'totalKeysExamined': 14, 'allPlansExecution': [], 'totalDocsExamined': 14, 'executionTimeMillis': 0}, 'queryPlanner': {'rejectedPlans': [], 'indexFilterSet': False, 'plannerVersion': 1, 'namespace': 'test.emp', 'winningPlan': {'inputStage': {'inputStage': {'indexName': 'empno_1_sal_-1', 'keyPattern': {'empno': 1, 'sal': -1}, 'stage': 'IXSCAN', 'direction': 'forward', 'indexBounds': {'empno': ['[MinKey, MaxKey]'], 'sal': ['[MaxKey, MinKey]']}, 'isMultiKey': False}, 'filter': {'sal': {'$gt': 2000}}, 'stage': 'FETCH'}, 'transformBy': {'_id': 0}, 'stage': 'PROJECTION'}, 'parsedQuery': {'sal': {'$gt': 2000}}}, 'serverInfo': {'gitVersion': '8bc4ae20708dbb493cb09338d9e7be6698e4a3a3', 'port': 27017, 'host': 'SJYUN-ATIV', 'version': '3.0.5'}}

8. Count
.count() 함수로 확인이 가능합니다.
print(db.emp.count())
print(db.emp.find({ "sal" : { "$gt" : 2000 } }).count())
14
6

위 예제들의 Full Source 입니다.


참고 :