2012-05-06 3 views
4

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가 실행되는 동안.

답변

8

실제로는 val_array이 필요하지 않은 것 같습니다. 간단한 해시를 사용하지 않는 이유는 무엇입니까? 이 시도 :

map = function() { 
    if (!this.fruits) return; 
    for (var fruit in this.fruits) { 
     emit(fruit, 
      {this.fruits[fruit]: 1}); 
    } 
}; 

reduce = function(key, values) { 
    var colors = {}; 

    values.forEach(function(v) { 
    for(var k in v) { // iterate colors 
     if(!colors[k]) // init missing counter 
     colors[k] = 0 

     color[k] += v[k]; 
    } 
    }); 

    return colors; 
} 
+0

와우, 나는 정말로 그 중 하나가 내가 생각하지 못했습니다! 이것은 실제로 내가 원했던 것과 똑같이합니다. 필자는 100, 1,000 및 100,000 개의 레코드로 테스트했으며 각 세트에 대해 약 20k/sec를 실행 중입니다 (이 크기에서는 분명히 선형 임). 나는 이제 10M 레코드 전체를 실행하고 있는데, 맵핑 된 데이터의 배치가 커짐에 따라,''colors'' 오브젝트가 커질 것입니다.''secs_running ": 488, msg ":"m/r : (1/3) phase 383999/10752083 3 %를 방출합니다. – SteveK

+0

Btw, 키가 동적으로 생성되었으므로'emit (fruit, {this.fruits [fruit] : 1}); '을 사용할 수 없으므로 대신이 JS 해킹을 사용했습니다 :'var obj = {}; obj [this.fruits [fruit]] = 1; emit (과일, obj);'. – SteveK

+0

부분적인 작업을 시도해 볼 것을 제안합니다. 즉, 100k (또는 그 이상)의 배치로 문서를 처리 한 다음 최종 작업에서이를 줄입니다. 구현하기 까다로울 수 있으므로 일회용이라면 신경 쓰지 않아도됩니다. :) –

0

을 나는 당신이 말할 미안 해요,하지만 MongoDB를 맵리 듀스 프레임 워크는 매우 느리고 아마도 "꽤"에 대한 그래서이 될 것 (I가 개선 될 기대하지 않을 것이다 그들의 로드맵에). http://docs.mongodb.org/manual/reference/aggregation/

또는 상단에 하둡을 실행 :

간단하게, 내 대답은 내가 몽고 - 맵리 듀스와 함께, 대신에 새로운 집계 프레임 워크의 도움으로 그것을 구현에 초점을 그렇게하지 않을 것이라고 것 http://www.slideshare.net/spf13/mongodb-and-hadoop (멋지고 간단한 소개)

구현 된 MapReduce 기능을 사용할 때 MongoDB와 관련하여 느린 문제가 있었으며, 가장 간단한 작업을 수행 할 때조차도 성능 측면에서 위의 두 가지 솔루션. 새로운 집계 프레임 워크를 사용하여 범용 하드웨어에서> 1M 워드 프로세서/초 (또는 그 이상)를 쉽게 처리 할 수 ​​있습니다.