2012-01-23 2 views
2

Mongo db에 기록중인 전자 메일 활동을 그래프로 표시하려고합니다. 이메일을 보낼 때마다 레코드를 만들고, 이메일에 활동이있을 때 (열기, 클릭, 스팸으로 표시) 문서를 업데이트하여 기록을 업데이트합니다.MapReduce 하위 문서

{ 
    "_id" : new BinData(3, "wbbS0lRI0ESx5DyStKq9pA=="), 
    "MemberId" : null, 
    "NewsletterId" : 4, 
    "NewsletterTypeId" : null, 
    "Contents" : "[message goes here]", 
    "History" : [{ 
     "EmailActionType" : "spam", 
     "DateAdded" : new Date("Sat, 10 Dec 2011 04:17:26 GMT -08:00") 
    }, { 
     "EmailActionType" : "processed", 
     "DateAdded" : new Date("Sun, 11 Dec 2011 04:17:26 GMT -08:00") 
    }, { 
     "EmailActionType" : "deffered", 
     "DateAdded" : new Date("Mon, 12 Dec 2011 04:17:26 GMT -08:00") 
    }], 
    "DateAdded" : new Date("Mon, 01 Jan 0001 00:00:00 GMT -08:00") 
} 

내가 뭘하고 싶은 것은 특정 역사 기간의 데이터베이스를 조회 할 수 있습니다 : 여기

은 샘플 문서입니다. 활동과 각 활동 유형에 대한 총이 경우 최종 결과는 매일에 대한 항목을 목록이어야합니다 : 여기

date: "20111210", spam: 1, processed: 0, deffered: 0 
date: "20111211", spam: 0, processed: 1, deffered: 0 
date: "20111212", spam: 0, processed: 0, deffered: 1 

것은 내가 현재 가지고있는 것입니다 :

db.runCommand({ mapreduce: Email, 
map : function Map() { 
    var key = this.NewsletterId; 
    emit(
      key, 
      { "history" : this.History } 
     ); 
} 
reduce : function Reduce(key, history) { 
    var from = new Date (2011, 1, 1, 0, 0, 0, 0); 
    var to = new Date (2013, 05, 15, 23, 59, 59, 0); 

    // \/ determine # days in the date range \/ 
    var ONE_DAY = 1000 * 60 * 60 * 24; // The number of milliseconds in one day 
    var from_ms = from.getTime(); // Convert both date1 to milliseconds 
    var to_ms = to.getTime(); // Convert both date1 to milliseconds 

    var difference_ms = Math.abs(from_ms - to_ms); // Calculate the difference in milliseconds 
    var numDays = Math.round(difference_ms/ONE_DAY); // Convert back to days and return 
    // /\ determine # days between the two days /\ 

    var results = new Array(numDays); //array where we will store the results. We will have an entry for each day in the date range. 

    //initialize array that will contain our results for each type of emailActivity 
    for(var i=0; i < numDays; i++){ 
     results[i] = { 
      numSpam: 0, 
      numProcessed: 0, 
      numDeffered: 0 
     } 
    } 

    //traverse the history records and count each type of event 
    for (var i = 0; i < history.length; i++){ 
     var to_ms2 = history[i].DateAdded.getTime(); // Convert both date1 to milliseconds 

     var difference_ms2 = Math.abs(from_ms - to_ms2); // Calculate the difference in milliseconds 
     var resultsIndex = Math.round(difference_ms2/ONE_DAY); //determine which row in the results array this date corresponds to 

     switch(history[i].EmailActionType) 
     { 
      case 'spam': 
       results[resultsIndex].numSpam = ++results[resultsIndex].numSpam; 
       break; 
      case 'processed': 
       results[resultsIndex].numProcessed = ++results[resultsIndex].numProcessed; 
       break; 
      case 'deffered': 
       results[resultsIndex].numDeffered = ++results[resultsIndex].numDeffered; 
       break; 
     } 
    } 
    return results; 
} 
finalize : function Finalize(key, reduced) { 
    return { 
     "numSpam": reduced.numSpam, 
     "numProcessed": reduced.numProcessed, 
     "numDeffered": reduced.numDeffered, 
    }; 
} 
out : { inline : 1 } 
}); 

내가 실행하는 경우 그것, 나는 아무것도 얻지 않는다, 그러나 나는 또한 어떤 오류도 얻지 않고있다. 그래서 어디에 정말로 봐야하는지 명확히하지 않는다.

답변

3

지도/축소 기능에 문제가 있습니다. emit과 예상 출력 사이에 연결이 끊어졌습니다.

당신의 예상 출력 :

date: "20111210", spam: 1, processed: 0, deffered: 0 

지도/항상 감소는 keyvalue의 측면에서 출력합니다. 결과는 다음과 같습니다.

_id: "20111220", value: { spam: 1, processed: 0, deferred: 0 } 

다음은 기본 전제입니다. emit은 올바른 형식의 데이터를 출력해야합니다. 당신 emit(key, value), 그럼 당신이하는 경우는 그래서 : 귀하의 경우

var key='20111220' 
var value={spam:1, processed:0, deferred:0} 

을, 당신은 History을 통해 당신에게로 루프를 문서에 여러 번 방출된다. 이것은 정상입니다.

reduce 기능은 동일한 키에 대해 여러 값이있는 경우에만 실행됩니다. 그래서 당신이있는 경우 :

_id: "20111220", value: { spam: 1, processed: 0, deferred: 0 } 
_id: "20111220", value: { spam: 1, processed: 2, deferred: 0 } 

다음 reduce 함께 사람들을 끌어 당신에게 줄 것이다이 :

map = function() { 
    for(var i in this.History) { 
    var key = get_date(this.History[i].DateAdded); 
    var value = {spam: 0, processed: 0, deffered: 0}; 

    if(this.History[i].EmailActionType == "Spam") { value.spam++; } 
    else if(....) 
    ... 

    emit(key, value); 
    } 
} 

reduce = function(key, values) { 
    // values is an array of these things {spam: 0, processed: 0, deffered: 0} 
    var returnValue = { spam: 1, processed: 0, deffered: 0 }; 
    for(var i in values) { 
    returnValue.spam += values[i].spam; 
    returnValue.processed += values[i].processed; 
    returnValue.deffered += values[i].deffered; 
    } 
    return returnValue; 
} 

그냥 기억 : 여기

_id: "20111220", value: { spam: **2**, processed: **2**, deferred: 0 } 

대답에 빠른 찌르기입니다 emit의 구조는 최종 값의 구조와 일치해야합니다.