2017-05-16 1 views
1

J'ai une base de données CouchDB qui a des documents au format suivant:CouchDB View - clés de filtre avant le regroupement

{ createdBy: 'userId', at: 123456, type: 'action_type' } 

Je veux écrire une vue qui me donnera combien d'actions de chaque type ont été créés par qui utilisateur. J'ai pu le faire que la création d'une vue qui fait cela:

emit([doc.createdBy, doc.type, doc.at], 1); 

Avec la diminution de la fonction « somme » et la consommation de la vue de cette façon:

/_design/userActionsDoc/_view/userActions group_level = 2

ce retourne un résultat avec des lignes juste la façon dont je veux:

"rows":[ {"key":["userId","ACTION_1"],"value":20}, ... 

le problème est que maintenant je veux filtrer les résultats pour une période de temps donnée. Donc, je veux avoir exactement la même information, mais seulement en considérant les actions qui se sont déroulées dans un délai donné.

Je peux filtrer les documents par "at" si j'émets les champs dans un ordre différent.

? Group_level = 3 & startkey = [149328316160] & endkey = [1493283161647, {}, {}]

emit([doc.at, doc.type, doc.createdBy], 1); 

mais je ne vais pas obtenir les résultats regroupés par userId et actionType. Y a-t-il un moyen d'avoir les deux? Peut-être écrire ma propre fonction de réduction?

Répondre

1

Je ressens votre douleur. J'ai déjà fait deux choses différentes dans le passé pour tenter de résoudre des problèmes similaires.

Le premier motif est une douleur et peut fonctionner très bien ou ne pas fonctionner du tout. J'ai vécu les deux. Votre fonction de carte ressemble à ceci:

function(doc) { 
    var obj = {}; 
    obj[doc.createdBy] = {}; 
    obj[doc.createdBy][doc.type] = 1; 

    emit(doc.at, obj); 
    // Ignore this for now 
    // emit(doc.at, JSON.stringify(obj)); 
} 

Ensuite, votre fonction reduce ressemble à ceci:

function(key, values, rereduce) { 
    var output = {}; 
    values.forEach(function(v) { 
    // Ignore this for now 
    // v = JSON.parse(v); 
    for (var user in v) { 
     for (var action in v[user]) { 
     output[user][action] = (output[user][action] || 0) + v[user][action]; 
     } 
    } 
    }); 
    return output; 
    // Ignore this for now 
    // return JSON.stringify(output); 
} 

Avec de grands ensembles de données, il en résulte généralement une erreur de canapé indiquant que votre réduire la fonction ne diminue pas rapidement assez. Dans ce cas, vous pouvez être en mesure de stringifier/analyser les objets comme indiqué dans les commentaires "ignorer" dans le code.

Le raisonnement derrière cela est que couchdb veut finalement que vous produisiez un objet simple comme une chaîne ou un entier dans une fonction de réduction. Dans mon expérience, il ne semble pas important que la chaîne s'allonge, tant qu'elle reste une chaîne. Si vous produisez un objet, à un moment donné les erreurs de fonction, car vous avez ajouté trop d'accessoires à cet objet. Le deuxième modèle est potentiellement meilleur, mais nécessite que vos périodes soient "définies" à l'avance. Si vos exigences de période peuvent être verrouillées à une année spécifique, un mois, un jour, un trimestre spécifique, etc. Vous venez d'émettre plusieurs fois dans votre fonction de carte. Ci-dessous, je suppose que la propriété at est en millisecondes, ou du moins quelque chose que le constructeur de date peut analyser avec précision.

function(doc) { 
    var time_key; 
    var my_date = new Date(doc.at); 

    //// Used for filtering results in a given year 
    //// e.g. startkey=["2017"]&endkey=["2017",{}] 
    time_key = my_date.toISOString().substr(0,4); 
    emit([time_key, doc.createdBy, doc.type], 1); 

    //// Used for filtering results in a given month 
    //// e.g. startkey=["2017-01"]&endkey=["2017-01",{}] 
    time_key = my_date.toISOString().substr(0,7); 
    emit([time_key, doc.createdBy, doc.type], 1); 

    //// Used for filtering results in a given quarter 
    //// e.g. startkey=["2017Q1"]&endkey=["2017Q1",{}] 
    time_key = my_date.toISOString().substr(0,4) + 'Q' + Math.floor(my_date.getMonth()/3).toString(); 
    emit([time_key, doc.createdBy, doc.type], 1); 
} 

Ensuite, votre fonction de réduction est la même que dans votre original.Essentiellement, vous essayez simplement de définir une valeur constante pour le premier élément de votre clé qui correspond à une période définie. Fonctionne bien pour les rapports d'affaires, mais pas tellement pour permettre des périodes de temps flexibles.

+0

merci! Je vais essayer ça –