Вопрос об агрегировании карты / сокращения массива MongoDB

У меня есть коллекция MongoDB, в документации которой используется несколько уровней вложенности, из которой я хотел бы извлечь многомерный массив, скомпилированный из подмножества их полей. У меня есть решение, которое работает для меня прямо сейчас, но я хочу лучше понять эту концепцию «идемпотентности» и ее последствия, связанные с функцией сокращения.

{
  "host_name" : "gateway",
  "service_description" : "PING",
  "last_update" : 1305777787,
  "performance_object" : [
    [ "rta", 0.105, "ms", 100, 500, 0 ],
    [ "pl", 0, "%", 20, 60, 0 ]
  ]
}

А вот функции отображения / сокращения.

var M = function() {
  var hn = this.host_name, 
      sv = this.service_description, 
      ts = this.last_update;
  this.performance_object.forEach(function(P){
    emit( { 
      host: hn, 
      service: sv, 
      metric: P[0] 
    }, { 
      time: ts, 
      value: P[1] 
    } );
  });
}
var R = function(key,values) {
  var result = { 
    time: [], 
    value: [] 
  };
  values.forEach(function(V){
    result.time.push(V.time);
    result.value.push(V.value);
  });
  return result;
}
db.runCommand({
  mapreduce: <colname>,
  out: <col2name>,
  map: M,
  reduce: R
});

Данные возвращаются в полезная структура, которую я переформатирую / сортирую с помощью finalize для построения графиков.

{
  "_id" : {
    "host" : "localhost",
    "service" : "Disk Space",
    "metric" : "/var/bck"
  },
  "value" : {
    "time" : [
      [ 1306719302, 1306719601, 1306719903, ... ],
      [ 1306736404, 1306736703, 1306737002, ... ],
      [ 1306766401, 1306766701, 1306767001, ... ]
    ],
    "value" : [
      [ 122, 23423, 25654, ... ],
      [ 336114, 342511, 349067, ... ],
      [ 551196, 551196, 551196, ... ]
    ]
  }
}

Наконец ...

 [ [1306719302,122], [1306719601,23423], [1306719903,25654], ... ]

TL; DR: Каково ожидаемое поведение с наблюдаемым "фрагментированием" результатов массива?

Я понимаю, что функция сокращения может быть вызвана несколько раз для массива (ов) излучаемых значений, поэтому существует несколько «фрагментов» полных массивов, а не один массив. Чанки массива обычно состоят из 25-50 элементов, и это достаточно легко очистить с помощью finalize (). Я объединяю () массивы, чередую их как [время, значение] и сортирую. Но я действительно хочу знать, может ли это стать более сложным:

1) Наблюдается ли разбиение на фрагменты из-за моего кода, реализации MongoDB или самого алгоритма Map / Reduce?

2) Будет ли когда-нибудь более глубокое ( рекурсивное) вложение блоков массива в сегментированные конфигурации или просто из-за моей поспешной реализации? Это нарушит работу метода concat ().

3) Есть ли просто лучшая стратегия для получения результатов массива, как показано выше?

РЕДАКТИРОВАТЬ: Изменено для выдачи массивов:

Я принял совет Томаса и переписал это испускать массивы. Совершенно бессмысленно разделять значения.

var M = function() {
  var hn = this.host_name, 
      sv = this.service_description, 
      ts = this.last_update;
  this.performance_object.forEach(function(P){
    emit( { 
      host: hn, 
      service: sv, 
      metric: P[0] 
    }, { 
      value: [ ts, P[1] ] 
    } );
  });
}
var R = function(key,values) {
  var result = {
    value: [] 
  };
  values.forEach(function(V){
    result.value.push(V.value);
  });
  return result;
}
db.runCommand({
  mapreduce: <colname>,
  out: <col2name>,
  map: M,
  reduce: R
});

Теперь результат похож на этот:

{
  "_id" : {
    "host" : "localhost",
    "service" : "Disk Space",
    "metric" : "/var/bck"
  },
  "value" : {
    "value" : [
      [ [1306736404,336114],[1306736703,342511],[1306737002,349067], ... ],
      [ [1306766401,551196],[1306766701,551196],[1306767001,551196], ... ],
      [ [1306719302,122],[1306719601,122],[1306719903,122], ... ]
    ]
  }
}

И я использовал эту функцию finalize, чтобы объединить куски массива и отсортировать их.

...
var F = function(key,values) {
  return (Array.concat.apply([],values.value)).sort(function(a,b){ 
    if (a[0] < b[0]) return -1;
    if (a[0] > b[0]) return 1;
    return 0;
  });
}
db.runCommand({
  mapreduce: <colname>,
  out: <col2name>,
  map: M,
  reduce: R,
  finalize: F
});

Что отлично работает:

{
  "_id" : {
    "host" : "localhost",
    "service" : "Disk Space",
    "metric" : "/mnt/bck"
  },
  "value" : [ [1306719302,122],[1306719601,122],[1306719903,122],, ... ]
}

Думаю, единственный вопрос, который меня гложет, это то, можно ли доверять этому Array.concat.apply ([], values.value), чтобы постоянно очищать выходные данные reduce.

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ: Многое проще ...

Я изменил структуру документа по сравнению с исходным примером, приведенным выше, но это только меняет пример, делая функцию карты действительно простой.

Я все еще пытаюсь понять, почему именно Array. prototype.push.apply (result, V.data) работает совсем не так, как result.push (V.data) ... но он работает.

var M = function() {
  emit( { 
    host: this.host, 
    service: this.service, 
    metric: this.metric
  } , { 
    data: [ [ this.timestamp, this.data ] ] 
  } );
}
var R = function(key,values) {
  var result = [];
  values.forEach(function(V){
    Array.prototype.push.apply(result, V.data);
  });
  return { data: result };
}
var F = function(key,values) {
  return values.data.sort(function(a,b){
    return (a[0]<b[0]) ? -1 : (a[0]>b[0]) ? 1 : 0;
  });
}

Он имеет тот же результат, что и показан над заголовком LAST EDIT.

Спасибо, Томас!

8
задан Community 22 September 2017 в 18:01
поделиться