3

У меня есть данные в MongoDB. Структура одного объекта, как это:MongoDB MapReduce, возврат только при подсчете> 1

{ 
    "_id" : ObjectId("5395177980a6b1ccf916312c"), 
    "institutionId" : "831", 
    "currentObject" : { 
      "systemIdentifiers" : [ 
      { 
       "value" : "24387", 
       "system" : "ABC" 
      }] 
     } 
} 

Я должен знать, сколько объектов есть же institutionId и systemIdentifiers [0] .value и хотите вернуть только те дублируется таким образом. Для этого я группирую их по этим идентификаторам и подсчетам.

Объект (пара идентификаторов) должен быть возвращен, когда отсчета больше 1.

Это фрагмент кода, который делает группировку с помощью MapReduce.

var map = function() { 
    var key = this.institutionId; 
    var val = this.currentObject.systemIdentifiers[0].value; 
    emit({"institutionId":key,"workId":val}, {count:1});  
}; 
var reduce = function(key, values) { 
    var count = 0; 
    values.forEach(function(v) { 
     count += v['count']; 
    }); 
    return {count: count}; 
} 
db.name.mapReduce(map, reduce, {out: "grouped"}) 
db.grouped.find() 

Чтобы получить только те, которые имеют счета greather, чем 1, я

db.grouped.aggregate([{$match:{"value.count":{$gt: 1}}}]) 

Пример результат затем следующий

{ 
    "_id" : { 
     "institutionId" : "1004", 
     "workId" : "591426" 
    }, 
    "value" : { 
     "count" : 2 
    } 
} 

Но мне любопытно ли, если это возможно, чтобы это было сделано просто сделав MapReduce в качестве одного оператора. Sth, как добавление финализатора или около того.

ответ

1

Гораздо лучше, проще и более эффективный подход будет состоять в том, чтобы использовать структуру агрегации, где вы можете использовать операторы, как $arrayElemAt возвратить первый вложенный документ из массива, а затем с помощью трубопровода с $group агрегировать отсчеты. Затем вы можете разместить трубопровод $match, чтобы отфильтровать результаты на основе данных критериев.

Следующий пример показывает, что это более быстрый подход:

db.name.aggregate([ 
    { 
     "$project": { 
      "key": "$institutionId", 
      "val": { 
       "$arrayElemAt": ["$currentObject.systemIdentifiers", 0] 
      } 
     } 
    }, 
    { 
     "$group": { 
      "_id": { 
       "institutionId": "$key", 
       "workId": "$val.value" 
      }, 
      "count": { "$sum": 1 } 
     } 
    }, 
    { "$match": { "count": { "$gt": 1 } } } 
]) 
+1

спасибо, это работает отлично :) –

2

Если есть один документ havig ключ не будет никогда идти внутрь уменьшить, считается снижается уже, что это поведение MongoDB Map-Reduce:

MongoDB will not call the reduce function for a key that has only a single value.

Использование finalzie также не очень помогает, то есть если в завершении funtion вы делаете if count > 1 then return reducedVal else None, то в результате вы получите None (вместо 1).

Я боюсь, что используя (один) снимок карты, документы, имеющие счет 1, будут alwasy в результате, так как они активированы из карты.

Вы можете использовать 2 операции уменьшения карты в цепочке, на второй карте вы не используете документы, имеющие счет < 2. Но они не считают, что это лучше, чем дополнительный запрос, как в вашем примере.