#couchdb #mapreduce #cloudant #couchbase
#couchdb #mapreduce #cloudant #couchbase
Вопрос:
Я поиграл с сервером Couchbase и теперь просто попытался скопировать свою локальную базу данных в Cloudant, но получаю противоречивые результаты для моей пары функций map / reduce для создания набора уникальных тегов с их связанными проектами…
// map.js
function(doc) {
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], doc._id);
}
}
}
// reduce.js
function(key,values,rereduce) {
if (!rereduce) {
var res=[];
for(var v in values) {
res.push(values[v]);
}
return res;
} else {
return values.length;
}
}
На сервере Cloudbase это возвращает JSON, подобный:
{"rows":[
{"key":"3d","value":["project1","project3","project8","project10"]},
{"key":"agents","value":["project2"]},
{"key":"fabrication","value":["project3","project5"]}
]}
Это именно то, чего я хотел и ожидал. Однако тот же запрос к реплике Cloudant возвращает это:
{"rows":[
{"key":"3d","value":4},
{"key":"agents","value":1},
{"key":"fabrication","value":2}
]}
Таким образом, он каким-то образом возвращает только длину массива значений… Очень запутанный и я благодарен за любую информацию некоторым M amp; R ninjas … 😉
Ответ №1:
Похоже, что это именно то поведение, которое вы ожидаете, учитывая вашу функцию уменьшения. Ключевая часть заключается в следующем:
else {
return values.length;
}
В Cloudant всегда вызывается rereduce (поскольку сокращение должно охватывать несколько сегментов). В этом случае rereduce вызывает значения.длина, которая вернет только длину массива.
Комментарии:
1. Понятно, но как я могу написать этот запрос, чтобы он выдавал одинаковый результат (# 1) на обеих платформах? Прямо сейчас меня не волнует (ну, это так :), что моя функция reduce не идеальна, но я бы предположил последовательное поведение…
2. 1, Алан. Токси, я предлагаю тебе переписать так, чтобы ты мог «почувствовать» повторное уменьшение по сравнению с первоначальным уменьшением, не проверяя параметр, переданный из couch. Я делал это раньше, поэтому я попробую в качестве реального ответа для лучшего форматирования.
3. Вам просто нужно помнить, что Cloudant всегда вызывает rereduce, поэтому, если вы хотите, чтобы они были согласованными, вам нужно, чтобы ваше предложение rereduce соответствовало вашему предложению reduce. В качестве дополнительного примечания: любая нетривиальная функция reduce в CouchDB будет использовать rereduce, поэтому неплохо бы разобраться в механизмах, лежащих в основе. Смотрите, например wiki.apache.org/couchdb /…
4. Я постоянно игнорирую повторное обновление в рабочей среде. Я покажу вам в своем ответе.
Ответ №2:
Я предпочитаю уменьшать / повторно уменьшать неявно, а не в зависимости от rereduce
параметра.
function(doc) { // map
if (doc.tags) {
for(var t in doc.tags) {
emit(doc.tags[t], {id:doc._id, tag:doc.tags[t]});
}
}
}
Затем reduce проверяет, накапливает ли он идентификаторы документов из идентичного тега или просто считает разные теги.
function(keys, vals, rereduce) {
var initial_tag = vals[0].tag;
return vals.reduce(function(state, val) {
if(initial_tag amp;amp; val.tag === initial_tag) {
// Accumulate ids which produced this tag.
var ids = state.ids;
if(!ids)
ids = [ state.id ]; // Build initial list from the state's id.
return { tag: val.tag,
, ids: ids.concat([val.id])
};
} else {
var state_count = state.ids ? state.ids.length : state;
var val_count = val.ids ? val.ids.length : val;
return state_count val_count;
}
})
}
(Я не тестировал этот код, но вы поняли идею. Пока tag
значение одно и то же, не имеет значения, является ли это уменьшением или повторным уменьшением. Как только разные теги начнут сокращаться вместе, он обнаружит это, потому что tag
значение изменится. Итак, на этом этапе просто начните накапливать.
Я уже использовал этот трюк раньше, хотя ИМО это редко того стоит.
Также в вашем конкретном случае это опасная функция уменьшения. Вы создаете широкий список, чтобы увидеть все документы, у которых есть тег. CouchDB любит длинные списки, а не толстые. Если вы хотите просмотреть все документы, у которых есть тег, вы могли бы сопоставить их.
for(var a = 0; a < doc.tags.length; a ) {
emit(doc.tags[a], doc._id);
}
Теперь вы можете запрашивать /db/_design/app/_view/docs_by_tag?key="3d"
, и вы должны получить
{"total_rows":287,"offset":30,"rows":[
{"id":"project1","key":"3d","value":"project1"}
{"id":"project3","key":"3d","value":"project3"}
{"id":"project8","key":"3d","value":"project8"}
{"id":"project10","key":"3d","value":"project10"}
]}
Комментарии:
1. Спасибо вам, люди, за все ваши идеи! (и извините, что мне потребовалось так много времени, чтобы ответить, меня отстранили) Мне удалось обойти этот вопрос, но все еще иногда бывает трудно полностью выполнить часть сокращения без какой-либо функции отладки в couchdb. Возможность видеть промежуточные результаты сотворила бы чудеса, чтобы лучше понять, что происходит…