2017-07-07 1 views
6

Mon application stocke plusieurs types de documents dans le même compartiment. Je sais que ce n'est pas une bonne pratique mais j'ai une limite sur le nombre de seaux que je peux créer sur mon serveur et il n'y en a pas pour le moment. Les documents sont préfixés avec leurs types alors quand je reçois un document j'ai juste besoin de concaténer le préfixe et l'identifiant pour obtenir la clé et je peux faire une recherche de clé.Comment fusionner des données dans Couchbase avec map reduce?

Je dois créer un rapport qui génère des informations à partir de plusieurs types de document.

Ma carte ressemble à ceci:

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); 
    } 
} 

Il génère cette sortie:

"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}} 
... 

] 

Je suis en train de regrouper la date par hotelId qui est la nouvelle clé et une fusion des champs à un seul document avec un réducteur personnalisé. Je reçois des erreurs différentes selon le type d'erreur, mais toutes les erreurs semblent indiquer qu'il y a une limite sur la date à laquelle le réducteur peut retourner. Si je change le type de retour d'un objet en un tableau associatif qui fonctionne à peu près de la même manière, j'ai une meilleure erreur.

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; 
    } 
} 

Ça me donne 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; 
    } 
} 

Donne-moi reduction too large error

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

Donne-moi RangeError: Maximum call stack size exceeded

Si je lance:

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

je reviens:

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

Le moteur JavaScript doit être en mesure de réduire les tableaux avec max 114 taille et les données de sortie devrait être encore plus faible. Apparemment, il ya une limite sur combien de données une réduction peut renvoyer max_kv_size_per_doc qui est de 1 Mo aussi il y a une limite d'exécution de 10 secondes mais dans mon cas c'est autre chose. Existe-t-il un moyen de contourner ces limites en changeant l'algorithme, en retournant un tableau ou des tableaux ou quelque chose? Y a-t-il quelque chose que je peux faire sur la carte ou des trucs que je peux utiliser pour faire des rerets?

+0

a) ayant plusieurs types de documents dans un seau n'est pas une mauvaise pratique; C'est très commun et souvent recommandé. –

+0

b) quelle version de Couchbase utilisez-vous? N1QL n'est-il pas une option pour vous? –

+0

c) "La taille maximale de la pile d'appels a été dépassée" ressemble à une erreur JavaScript; idéalement, vous pouvez déboguer à travers et trouver d'où il vient, mais pas sûr de savoir comment le faire –

Répondre

3

Je l'ai compris. Cela fonctionne si j'utilise des clés composées et un niveau de groupe.

Donc, si je change ma carte pour revenir un tableau comme clé pour id hôtel et je mis group_level = 1 alors les valeurs seront regroupées pour moi comme je m'y attendais au départ:

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 
    } 
} 

Puis-je besoin de définir group_level=1 et reduce=true. Vous pouvez le faire dans l'éditeur de vue ou dans la chaîne de requête.

Le dernier bit est la réduire:

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; 
    } 
} 

Le résultat sera fusionné par hotelId comme prévu :)