2017-07-07 7 views
6

Moja aplikacja zapisuje wiele typów dokumentów w tym samym segmencie. Wiem, że to nie jest dobra praktyka, ale mam limit ilości wiaderek, które mogę utworzyć na moim serwerze, i nie ma na to czasu. Dokumenty są poprzedzone ich typami, więc kiedy dostaję dokument, muszę po prostu połączyć prefiks i id, aby uzyskać klucz i mogę wykonać kluczowe wyszukiwanie.Jak scalić dane w Couchbase z redukcją mapy?

Potrzebuję utworzyć raport zawierający informacje z więcej niż jednego typu dokumentu.

Moja mapa wygląda następująco:

function(doc, meta) { 

    var getStep = function(stepName, exit, mapper) { 
    if (meta.id.indexOf(stepName) !== -1) { 
     var hotelId = parseInt(meta.id.replace(stepName + '_', '')); 
     if (hotelId > 0) { 
     var result = { 
      hotelId: hotelId, 
      exit: exit 
     }; 
     if (mapper !== undefined) { 
      mapper(result); 
     } 
     return result; 
     } 
    } 
    return null; 
    }; 

    var photos = getStep('PHOTOS', 7); 
    if (photos != null) { 
    emit(photos.hotelId, photos); 
    } 
    var pricing = getStep('PICR', 5); 
    if (pricing != null) { 
    emit(pricing.hotelId, pricing); 
    } 
    var owner = getStep('OWNER', 1); 
    if (owner != null) { 
    emit(owner.hotelId, owner); 
    } 
    var amenity = getStep('AM', 4); 
    if (amenity != null) { 
    emit(amenity.hotelId, amenity); 
    } 
    var description = getStep('HDESC', 3, function(result) { 
    result.description = doc.description; 
    result.hotelRoomTypeId = doc.hotelRoomTypeId; 
    result.starRating = doc.starRating; 
    }); 
    if (description != null) { 
    emit(description.hotelId, description); 
    } 
    var contact = getStep('DC', 3, function(result) { 
    result.email = doc.emailAddress; 
    result.contact = doc.mainContactName; 
    }); 
    if (contact != null) { 
    emit(contact.hotelId, contact); 
    } 
    var location = getStep('LOC', 2, function(result) { 
    result.city = doc.cityName; 
    result.zip = doc.postalCode; 
    result.country = doc.countryName; 
    result.street = doc.stateName + ', ' + doc.streetName; 
    }); 
    if (location != null) { 
    emit(location.hotelId, location); 
    } 
    var property = getStep('PRP', 1, function(result) { 
    result.paymentMethodId = doc.paymentMethodId 
    }); 
    if (property != null) { 
    emit(property.hotelId, property); 
    } 
} 

Generuje to wyjście:

"total_rows":...,"rows":[ 
{"id":"DC_1","key":1,"value":{"hotelId":1,"exit":3,"email":"[email protected]","contact":"Jeno"}}, 
{"id":"HDESC_1","key":1,"value":{"hotelId":1,"exit":3,"description":".","hotelRoomTypeId":0,"starRating":5}}, 
{"id":"LOC_1","key":1,"value":{"hotelId":1,"exit":2,"city":"Barcelona","zip":"1222","country":"Spain","street":"Catalonia, someplacenice"}}, 
{"id":"PRP_1","key":1,"value":{"hotelId":1,"exit":1}}, 
{"id":"PRP_2","key":2,"value":{"hotelId":2,"exit":1}}, 
{"id":"AM_3","key":3,"value":{"hotelId":3,"exit":4}}, 
{"id":"AM_4","key":4,"value":{"hotelId":4,"exit":4}}, 
{"id":"PHOTOS_4","key":4,"value":{"hotelId":4,"exit":7}}, 
{"id":"PRP_4","key":4,"value":{"hotelId":4"exit":1}}, 
{"id":"AM_4","key":4,"value":{"hotelId":4,"exit":4}}, 
{"id":"PRP_4","key":4,"value":{"hotelId":4,"exit":1}}, 
{"id":"PHOTOS_5","key":5,"value":{"hotelId":5,"exit":7}} 
... 

] 

Próbuję grupy datę, hotelId który jest nowy klucz i scalania pól do jednego dokumentu z niestandardowym reduktorem. Dostaję różne błędy w zależności od typu błędu, ale wszystkie błędy wydają się wskazywać, że istnieje limit daty zwrotu reduktora. Jeśli zmienię typ zwracany z obiektu na tablicę asocjacyjną, która działa w podobny sposób, otrzymuję lepszy błąd.

function(key, values, rereduce) { 
    if (rereduce) { 
    return values; 
    } else { 
    var results = {}; // Object! 
    for (var i = 0; i < values.length; i++) { 
     var row = values[i]; 
     if (!results[row.hotelId]) { 
     results[row.hotelId] = { 
      phone: '', 
      exit: 1 
     }; 
     } 
     var result = results[row.hotelId]; 
     for (var name in row) { 
     result[name] = row[name]; 
     } 
     if (row.exit > row.exit) { 
     result.exit = row.exit; 
     } 
    }; 

    return results; 
    } 
} 

Daje mi RangeError: Maximum call stack size exceeded

function(key, values, rereduce) { 
    if (rereduce) { 
    return values; 
    } else { 
    var results = []; // Array! 
    for (var i = 0; i < values.length; i++) { 
     var row = values[i]; 
     if (!results[row.hotelId]) { 
     results[row.hotelId] = { 
      phone: '', 
      exit: 1 
     }; 
     } 
     var result = results[row.hotelId]; 
     for (var name in row) { 
     result[name] = row[name]; 
     } 
     if (row.exit > row.exit) { 
     result.exit = row.exit; 
     } 
    }; 

    return results; 
    } 
} 

Daje mi reduction too large error

function(key, values, rereduce) { 
    if (rereduce) { 
    return values; 
    } else {  
    return values; 
    } 
} 

Daje mi RangeError: Maximum call stack size exceeded

Jeśli biegnę:

function(key, values, rereduce) { 
    if (rereduce) { 
    return values; 
    } else {   
    return values.length; 
    } 
} 

wrócę:

[ 68, 72, 65, 66, 68, 68, 70, 114 ] 

Silnik JavaScript powinien być w stanie zmniejszyć tablice z max 114 rozmiarów i danych wyjściowych powinny być jeszcze mniejsze. Wygląda na to, że istnieje limit ilości danych, które redukują, mogą zwrócić max_kv_size_per_doc, co oznacza 1 MB, a także istnieje limit 10 sekund wykonania, ale w moim przypadku jest to coś innego. Czy istnieje sposób, aby ominąć te ograniczenia, zmieniając algorytm, zwracając tablicę lub tablice lub coś takiego? Czy jest coś, co mogę zrobić na mapie lub jakieś sztuczki, których mogę użyć w rereduce?

+0

a) posiadania wielu typów dokumentów w wiadrze nie jest złą praktyką; jest bardzo powszechny i ​​często polecany. –

+0

b) z jakiej wersji Couchbase korzystasz? czy N1QL nie jest dla ciebie opcją? –

+0

c) "Przekroczony maksymalny rozmiar stosu połączeń" brzmi jak błąd JavaScript; najlepiej można debugować go i dowiedzieć się, skąd pochodzi, ale nie wiem, jak to zrobić, –

Odpowiedz

3

Wymyśliłem to. Działa, jeśli używam klawiszy złożonych i poziomu group_level.

Więc jeśli mogę zmienić mapę, aby powrócić tablicę jako klucz na zdjęcie id i ustawić group_level = 1 wówczas wartości zostaną zgrupowane dla mnie jak początkowo oczekiwano:

function(doc, meta) { 

    var getStep = function(stepName, exit, mapper) { 
    if (meta.id.indexOf(stepName) !== -1) { 
     var hotelId = parseInt(meta.id.replace(stepName + '_', '')); 
     if (hotelId > 0) { 
     var result = { 
      hotelId: hotelId, 
      exit: exit 
     }; 
     if (mapper !== undefined) { 
      mapper(result); 
     } 
     return result; 
     } 
    } 
    return null; 
    }; 

    var photos = getStep('PHOTOS', 7); 
    if (photos != null) { 
    emit([photos.hotelId], photos); // array as key 
    } 
    var pricing = getStep('PICR', 5); // array as key 
    if (pricing != null) { 
    emit([pricing.hotelId], pricing); 
    } 
    var owner = getStep('OWNER', 1); // array as key 
    if (owner != null) { 
    emit([owner.hotelId], owner); 
    } 
    var amenity = getStep('AM', 4); // array as key 
    if (amenity != null) { 
    emit([amenity.hotelId], amenity); 
    } 
    var description = getStep('HDESC', 3, function(result) { 
    result.description = doc.description; 
    result.hotelRoomTypeId = doc.hotelRoomTypeId; 
    result.starRating = doc.starRating; 
    }); 
    if (description != null) { 
    emit([description.hotelId], description); // array as key 
    } 
    var contact = getStep('DC', 3, function(result) { 
    result.email = doc.emailAddress; 
    result.contact = doc.mainContactName; 
    }); 
    if (contact != null) { 
    emit([contact.hotelId], contact); // array as key 
    } 
    var location = getStep('LOC', 2, function(result) { 
    result.city = doc.cityName; 
    result.zip = doc.postalCode; 
    result.country = doc.countryName; 
    result.street = doc.stateName + ', ' + doc.streetName; 
    }); 
    if (location != null) { 
    emit([location.hotelId], location); // array as key 
    } 
    var property = getStep('PRP', 1, function(result) { 
    result.paymentMethodId = doc.paymentMethodId 
    }); 
    if (property != null) { 
    emit([property.hotelId], property); // array as key 
    } 
} 

Następnie trzeba ustawić group_level=1 i reduce=true. Możesz to zrobić w edytorze widoku lub w ciągu zapytania.

ostatni bit jest zmniejszenie:

function(key, values, rereduce) { 
    if (rereduce) { 
    return values; 
    } else {   
    var result = {}; 
    values.forEach(function(item){ 
     for(var name in item){ 
      result[name] = item[name]; 
     } 
    }); 

    return result; 
    } 
} 

Wynik zostaną połączone przez hotelId zgodnie z oczekiwaniami :)