Подсчет уникальных ключей в представлениях Couchbase

#map #unique #couchbase #reduce #scalar

#Карта #уникальный #couchbase #уменьшить #скалярный

Вопрос:

Я пытаюсь найти способ подсчитать количество уникальных значений в корзине, которая не является первичным ключом. Couchbase 2.5 предоставляет для этого метод n1ql. Если мы используем их образец пива, вы можете выполнить следующую команду:

выберите количество (отдельный стиль) из beer-sample

который возвращает скалярное значение 68.

Я использую couchbase 2.2.0, в которой технически нет n1ql. И я хочу использовать функциональность map / reduce / rereduce, если это возможно. Причина в том, что у меня 100 миллионов записей, и выполнение запроса adhoc, вероятно, займет несколько дней. Есть ли такой способ сделать это?

Для функции map у меня есть следующее:

 function (doc, meta) {
     if ( doc.type == "beer")
       emit(doc.style, doc.style);
}
  

для уменьшения у меня есть следующее:

 function(key, values, rereduce) {
    var u = {}, a = [];
    var results = {};

    if (rereduce) {

      for (var i = 0; i < values.length; i    ) {
        for ( var j = 0; j < values[i].length; j    ) {
          if (u.hasOwnProperty(values[i][j])) {
            continue;
          }

          a.push(values[i][j]);
          u[values[i][j]] = 1;
        }
      }
      return (a); 
    } else {  
      for(var i = 0; i < values.length; i  ) {
        if (u.hasOwnProperty(values[i])) {
          continue;
        }

        a.push(values[i]);
        u[values[i]] = 1;
      } 
      return(a);  
  }
}
  

Это возвращает массив с уникальными значениями, но не скалярное число. В любом случае я могу просто получить скалярное количество уникальных стилей пива? Спасибо.

Ответ №1:

Решение этой проблемы относительно простое (по крайней мере, для представления).

Во-первых, нет необходимости выводить стиль doc / beer как ключ, так и значение, поэтому ваша функция map будет лучше, если:

 function (doc, meta) {
    if (doc.type == "beer") {
        emit(doc.style, null)
    }
}
  

Далее просто используйте встроенную _count функцию уменьшения.

По умолчанию это просто выведет количество всех документов в корзине, которые вы подсчитываете, однако путем вызова функции map с параметрами фильтра group , установленными true равными и group level равными 1 (точный метод зависит от вашего клиентского SDK). При этом будет возвращен массив объектов, подобных следующему:

 {"rows":[
{"key":null,"value":1111},
{"key":"American Rye Ale or Lager","value":11},
{"key":"American-Style Amber/Red Ale","value":219},
{"key":"American-Style Barley Wine Ale","value":32},
{"key":"American-Style Brown Ale","value":187},
{"key":"American-Style Cream Ale or Lager","value":12},
{"key":"American-Style Dark Lager","value":1},
{"key":"American-Style Imperial Stout","value":55},
{"key":"American-Style India Black Ale","value":1},
{"key":"American-Style India Pale Ale","value":230},
{"key":"American-Style Lager","value":370},
{"key":"American-Style Light Lager","value":39},
{"key":"American-Style Pale Ale","value":393},
{"key":"American-Style Stout","value":241},
{"key":"American-Style Strong Pale Ale","value":8}
…
…
]
}
  

Этот массив можно уменьшить с key помощью параметра filter (в данном случае ключом является определенный стиль (или что бы вы ни хотели посчитать)) или, аналогично, вы можете выбрать с этой клиентской стороны.

Комментарии:

1. value равным количеством документов этого значения)

2. Кроме того, _count reduce сильно оптимизирован, поэтому даже для миллионов документов он не будет использовать чрезмерные ресурсы. Запросы попадают в индекс, поэтому единственной проблемой может быть количество операций чтения / запросов этого индекса. Обновление индекса является инкрементным, поэтому даже при добавлении / обновлении документов, если только миллионы не были обновлены одновременно, опять же, это не должно приводить к большим накладным расходам после первоначальной сборки индекса (см. Здесь ).

3. Чтобы дополнительно уточнить, что «чтение / запросы» могут быть потенциальной проблемой, это означает, что единственной проблемой здесь будут обычные узкие места для чтения базы данных (дисковый ввод-вывод и пространство, пропускная способность и оперативная память, не используемые Couchbase).

4. @Mwuk … спасибо за ввод, но это не отвечает на первоначальный вопрос. Мне нужно скалярное значение для всех уникальных стилей пива, а не список с количеством каждого стиля. Фильтрация по ключу не помогает и не может быть выполнена в моей ситуации. Есть еще мысли? Спасибо.

5. @user3775720 после некоторой переделки я думаю, что нужно было бы выполнить сгруппированный подсчет, как я описал выше, а затем подсчитать строки, которые возвращаются на стороне клиента в вашем SDK (какой SDK вы бы использовали — я могу попробовать и уточнить лучше, если вы скажете мне, на каком конкретном языке вы (намереваясь быть) использующим.

Ответ №2:

Если количество отдельных групп не будет слишком большим, попробуйте передать ассоциативный массив в функцию reduce.

В ведре с образцами пива:

 /**
 * Map function
 */
function (doc, meta) {
  if (doc.type == "beer" amp;amp; doc.style)
    emit(doc.style, null);
}

/**
 * Reduce function
 */
function (keys, values, rereduce) {
  count_by_key = {};
  if (rereduce) {
    for (i in values) {
      _count_by_key = values[i];
      for (key in _count_by_key) {
        count_by_key[key] = _count_by_key[key]   (count_by_key[key] || 0);
      }
    }
  } else {
    if (keys)
      for (i in keys) {
        key = keys[i];
        count_by_key[key] = 1   (count_by_key[key] || 0);
      }
  }
  return count_by_key; 
}
  

Количество ключей в результирующем значении будет скалярным количеством уникальных сортов пива. Он также работает с фильтрами ключей.