MongoDB의 집계에 대해 많은 질문이 있지만 아직 완전한 해결책을 찾지 못했습니다. 내 목표는 각 과일에 대한 각 색상의 인기를 결정하는 것입니다, 지금지도를 통한 MongoDB 고유 값 집계
{
"fruits" : {
"apple" : "red",
"orange" : "orange",
"plum" : "purple"
}
}
{
"fruits" : {
"apple" : "green",
"plum" : "purple"
}
}
{
"fruits" : {
"apple" : "red",
"orange" : "yellow",
"plum" : "purple"
}
}
때문에,이 같은 출력 수집 될 것이다 : 여기
내 데이터의 예{
"_id" : "apple"
"values" : {
"red" : 2,
"green" : 1
}
}
{
"_id" : "orange"
"values" : {
"orange" : 1,
"yellow" : 1
}
}
{
"_id" : "plum"
"values" : {
"purple" : 3
}
}
저는 다양한 M/R 기능을 시도했으며, 결국에는 작동하지 않거나 기하 급수적으로 오래 걸립니다. 예 (과일)의 맥락에서 나는 약 1,000,000 가지의 문서 전체에 대해 약 1,000 가지의 과일과 100,000 가지의 색을 가지고 있습니다. 나의 현재 작업 M은/R은 이것이다 :
이제map = function() {
if (!this.fruits) return;
for (var fruit in this.fruits) {
emit(fruit, {
val_array: [
{value: this.fruits[fruit], count: 1}
]
});
}
};
reduce = function(key, values) {
var collection = {
val_array: []
};
var found = false;
values.forEach(function(map_obj) {
map_obj.val_array.forEach(function(value_obj) {
found = false;
// if exists in collection, inc, else add
collection.val_array.forEach(function(coll_obj) {
if (coll_obj.value == value_obj.value) {
// the collection already has this object, increment it
coll_obj.count += value_obj.count;
found = true;
return;
}
});
if (!found) {
// the collection doesn't have this obj yet, push it
collection.val_array.push(value_obj);
}
});
});
return collection;
};
,이 작업을 수행하고, 100 개의 기록을 위해, 그냥 초 정도 걸리지 만, 시간이 너무 100M 기록이 걸릴 것이다, 비 선형 적으로 증가 매우 오랜 시간. 문제는 내가 collection
배열을 사용하여 reduce 함수에서 열등한 하위 집합을 수행하고 있으므로 collection
과 my map 함수의 값을 모두 반복해야한다는 것입니다. 이제는 (여러 번의 축소가 필요한 경우에도)이를 효율적으로 수행하는 방법을 알아야합니다. 어떤 제안이라도 환영합니다!
게시 할 수있는 더 나은 장소의 부족 편집, 여기 내 솔루션입니다. 점진적 한 번에 (!
date
으로 분류 색인이 있어야합니다) 100,000 문서를 감소시키고 감소 /지도, 내 전체 컬렉션을 통해
map = function() {
if (!this.fruits) return;
var skip_fruits = {
'Watermelon':1,
'Grapefruit':1,
'Tomato':1 // yes, a tomato is a fruit
}
for (var fruit in this.fruits) {
if (skip_fruits[fruit]) continue;
var obj = {};
obj[this.fruits[fruit]] = 1;
emit(fruit, obj);
}
};
reduce = function(key, values) {
var out_values = {};
values.forEach(function(v) {
for(var k in v) { // iterate values
if (!out_values[k]) {
out_values[k] = v[k]; // init missing counter
} else {
out_values[k] += v[k];
}
}
});
return out_values;
};
var in_coll = "fruit_repo";
var out_coll = "fruit_agg_so";
var total_docs = db[in_coll].count();
var page_size = 100000;
var pages = Math.floor(total_docs/page_size);
print('Starting incremental MR job with '+pages+' pages');
db[out_coll].drop();
for (var i=0; i<pages; i++) {
var skip = page_size * i;
print("Calculating page limits for "+skip+" - "+(skip+page_size-1)+"...");
var start_date = db[in_coll].find({},{date:1}).sort({date:1}).skip(skip).limit(1)[0].date;
var end_date = db[in_coll].find({},{date:1}).sort({date:1}).skip(skip+page_size-1).limit(1)[0].date;
var mr_command = {
mapreduce: in_coll,
map: map,
reduce: reduce,
out: {reduce: out_coll},
sort: {date: 1},
query: {
date: {
$gte: start_date,
$lt: end_date
}
},
limit: (page_size - 1)
};
print("Running mapreduce for "+skip+" - "+(skip+page_size-1));
db[in_coll].runCommand(mr_command);
}
그 파일 반복 :
첫째, 나는
mr.js
라는 파일을 생성 단일 출력 컬렉션으로 변환합니다. 다음과 같이 사용됩니다 :
mongo db_name mr.js
.
그런 다음 몇 시간이 지난 후에 모든 정보가 포함 된 컬렉션이 있습니다. 과일이 가장 색상이있는 파악하기 위해, 나는 상위 25에서 인쇄 할 몽고 쉘에서 이것을 사용 :
// Show number of number of possible values per key
var keys = [];
for (var c = db.fruit_agg_so.find(); c.hasNext();) {
var obj = c.next();
if (!obj.value) break;
var len=0;for(var l in obj.value){len++;}
keys.push({key: obj['_id'], value: len});
}
keys.sort(function(a, b){
if (a.value == b.value) return 0;
return (a.value > b.value)? -1: 1;
});
for (var i=0; i<20; i++) {
print(keys[i].key+':'+keys[i].value);
}
이 방법에 대한 정말 멋진 점은 증분 이후, I 출력 작업을 할 수 있다는 것입니다 데이터가 mapreduce가 실행되는 동안.
와우, 나는 정말로 그 중 하나가 내가 생각하지 못했습니다! 이것은 실제로 내가 원했던 것과 똑같이합니다. 필자는 100, 1,000 및 100,000 개의 레코드로 테스트했으며 각 세트에 대해 약 20k/sec를 실행 중입니다 (이 크기에서는 분명히 선형 임). 나는 이제 10M 레코드 전체를 실행하고 있는데, 맵핑 된 데이터의 배치가 커짐에 따라,''colors'' 오브젝트가 커질 것입니다.''secs_running ": 488, msg ":"m/r : (1/3) phase 383999/10752083 3 %를 방출합니다. – SteveK
Btw, 키가 동적으로 생성되었으므로'emit (fruit, {this.fruits [fruit] : 1}); '을 사용할 수 없으므로 대신이 JS 해킹을 사용했습니다 :'var obj = {}; obj [this.fruits [fruit]] = 1; emit (과일, obj);'. – SteveK
부분적인 작업을 시도해 볼 것을 제안합니다. 즉, 100k (또는 그 이상)의 배치로 문서를 처리 한 다음 최종 작업에서이를 줄입니다. 구현하기 까다로울 수 있으므로 일회용이라면 신경 쓰지 않아도됩니다. :) –