#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;
}
Количество ключей в результирующем значении будет скалярным количеством уникальных сортов пива. Он также работает с фильтрами ключей.