원문은 2019년 1월 18일 ObjectRocket.com/blog에 게시되었습니다.
스키마 검증, 필드 오타 디버깅, 또는 설정하면 안 되는 필드를 찾으려면 MongoDB® 컬렉션에 포함된 모든 키(key)를 파악하고 있어야 합니다.
ObjectRocket을 비롯한 많은 MongoDB-as-a-Service 업체들은 사용자 인터페이스(UI)에서 이 작업을 손쉽게 수행할 수 있는 기능을 제공합니다. 숙련된 MongoDB 사용자라면 보통 JavaScript®용 Mongoose나 Python®용 Mongoengine 같은 ODM(Object-Document Mapper)부터 도입하는 경우가 많습니다. ODM을 활용하면 애플리케이션에 일관된 스키마를 적용해 오타를 줄일 수 있고, 타입 검증도 자동으로 수행해 주기 때문에 정수형 필드에 실수로 문자열을 넣어 연산 오류가 발생하는 상황도 예방할 수 있습니다.
만약 별도의 서비스나 ODM이 없다면 다른 여러 가지 방법으로 키에 접근할 수 있습니다. 상황별로 MongoDB 컬렉션의 모든 키를 가져오는 유용한 방법들을 소개합니다.
MongoDB 키 찾기
다음과 같은 예제 데이터가 있다고 가정해 보겠습니다.
db.activities.insert( { type : ['indoor', 'outdoor', 'mixed'] } );
db.activities.insert( { activity : 'cycling' } );
db.activities.insert( { activity : 'skiing', location: 'Alpes' } );
db.activities.insert( { equipment : ['paddles','sunglasses'] } );
여기서 추출해야 할 고유 키는 type, activity, equipment, location입니다.
MapReduce 활용
MapReduce를 사용하면 다음 연산으로 키를 얻을 수 있습니다.
mr = db.runCommand({ "mapreduce" : "activities", "map" : function() { for (var key in this) { emit(key, null); } }, "reduce" : function(key, stuff) { return null; }, "out": "activities" + "_keys" })
연산이 완료되면 결과 컬렉션에 아래 distinct 명령을 실행해 모든 키를 확인합니다.
db.activities_keys.distinct("_id")
서브도큐먼트(subdocument) 내부의 고유 키 목록이 필요하다면 다음 줄을 수정해서 사용하세요.
for (var key in this.first_level.second_level.nth_level) { emit(key, null); }
애그리게이션(Aggregation) 활용
애그리게이션 프레임워크를 지원하는 모든 드라이버에서 이 방법을 사용할 수 있습니다. 오버헤드를 줄이려면 애그리게이션에 $sample 또는 $limit을 함께 사용하는 것이 좋습니다.
참고: primary 읽기 선호도(read preference)로 실행하면 성능에 영향을 줄 수 있으므로, secondary 읽기 선호도로 실행하는 것을 고려해 보세요.
버전 3.4.4 이상에서 제공되는 $objectToArray와 함께 애그리게이션을 사용하면 최상위 키-값 쌍 전체를 도큐먼트 배열로 변환할 수 있습니다. 이후 $unwind와 $addToSet이 포함된 $group을 적용하면 컬렉션 전체에서 고유한 키를 추출할 수 있습니다.
아래 예제에서 $$ROOT는 최상위 도큐먼트를 참조합니다.
db.activities.aggregate([ {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}}, {"$unwind":"$arrayofkeyvalue"}, {"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}} ])
또한 애그리게이션으로 단일 도큐먼트의 키만 가져올 수도 있습니다.
db.activities.aggregate([ {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}}, {"$project":{"keys":"$arrayofkeyvalue.k"}} ])
Mongo 셸(Mongo Shell) 활용
Mongo 셸 클라이언트에서는 다음 명령으로 키를 확인할 수 있습니다.
var allKeys = {}; db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})}); allKeys;
단일 도큐먼트의 필드 출력
컬렉션 내 각 도큐먼트는 서로 완전히 다른 키를 가질 수 있으므로, 특정 도큐먼트 하나의 필드만 출력해야 하는 경우가 있습니다. 이럴 때는 다음 명령을 사용하세요.
doc=db.thinks.findOne(); for (key in doc) print(key);
Python(pymongo) 활용
Python으로도 MongoDB 키를 가져올 수 있습니다. 다음 예제는 컬렉션의 모든 최상위 키 집합을 반환합니다.
# pymongo 사용, 'db'라는 이름의 연결 객체 기준
reduce(
lambda all_keys, rec_keys: all_keys | set(rec_keys),
map(lambda d: d.keys(), db.things.find()),
set()
)
JavaScript(Node.js) 활용
JavaScript로도 다음과 같이 키를 추출할 수 있습니다.
db.collection('collectionName').mapReduce(
function() { for (var key in this) { emit(key, null); } },
function(key, stuff) { return null; },
{ "out": "allFieldNames" },
function(err, results) {
var fields = db.collection('allFieldNames').distinct('_id');
fields
.then(function(data) {
var finalData = { "status": "success", "fields": data };
res.send(finalData);
deleteCollection(db, 'allFieldNames');
})
.catch(function(err) {
res.send(err);
deleteCollection(db, 'allFieldNames');
});
});
새로 생성된 컬렉션 allFieldNames를 읽은 후에는 삭제하는 것이 좋습니다.
db.collection("allFieldNames").remove({}, function (err, result) {
db.close();
return;
});
다양한 선택지 중 상황에 맞는 방법 고르기
지금까지 살펴본 것처럼 MongoDB 컬렉션의 키를 조회하는 방법은 매우 다양합니다. MapReduce, 애그리게이션, 셸 명령, Python, JavaScript 등 환경과 목적에 맞는 방식을 선택하면 됩니다. 대량의 데이터를 다룰 때는 성능 영향을 고려해 secondary 읽기 선호도 사용이나 $sample/$limit 적용을 권장합니다.
MongoDB 관리가 부담스러워 코드 개발에만 집중하고 싶다면 Rackspace DBA Services에 대해 자세히 알아보세요.
피드백 탭을 통해 의견을 남기거나 질문할 수 있으며, 언제든 저희와 대화를 시작할 수 있습니다.