2015-08-05 2 views
4

J'ai besoin d'aide. J'ai une requête qui obtient le top 5 des enregistrements groupe par date (pas la date + l'heure) et la somme du montant.Comment obtenir le top 5 des records en cassandra 2.2

j'écrit ce qui suit, mais il renvoie tous les enregistrements non seulement top 5 records

CREATE OR REPLACE FUNCTION state_groupbyandsum(state map<text, double>, datetime text, amount text) 
CALLED ON NULL INPUT 
RETURNS map<text, double> 
LANGUAGE java 
AS 'String date = datetime.substring(0,10); Double count = (Double) state.get(date); if (count == null) count = Double.parseDouble(amount); else count = count + Double.parseDouble(amount); state.put(date, count); return state;' ; 


CREATE OR REPLACE AGGREGATE groupbyandsum(text, text) 
SFUNC state_groupbyandsum 
STYPE map<text, double> 
INITCOND {}; 

select groupbyandsum(datetime, amout) from warehouse; 

Pourriez-vous s'il vous plaît aider à obtenir seulement 5 dossiers.

Répondre

3

Voici une façon de le faire. Votre groupe en fonction de l'état pourrait ressembler à ceci:

CREATE FUNCTION state_group_and_total(state map<text, double>, type text, amount double) 
CALLED ON NULL INPUT 
RETURNS map<text, double> 
LANGUAGE java AS ' 
    Double count = (Double) state.get(type); 
    if (count == null) 
     count = amount; 
    else 
     count = count + amount; 
    state.put(type, count); 
    return state; 
'; 

Cela va construire une carte de toutes les lignes de quantité sélectionnée par votre requête clause WHERE. Maintenant, la partie difficile est de savoir comment garder juste le top N. Une façon de le faire est d'utiliser un FINALFUNC qui est exécuté après que toutes les lignes ont été mises dans la carte. Donc, voici une fonction pour faire cela en utilisant une boucle pour trouver la valeur maximale dans la carte et le déplacer vers une carte de résultats. Donc, pour trouver le N supérieur, il parcourrait la carte N fois (il y a des algorithmes plus efficaces que cela, mais c'est juste un exemple rapide et sale).

Voici donc un exemple pour trouver les deux premiers:

CREATE FUNCTION topFinal (state map<text, double>) 
CALLED ON NULL INPUT 
RETURNS map<text, double> 
LANGUAGE java AS ' 
    java.util.Map<String, Double> inMap = new java.util.HashMap<String, Double>(), 
            outMap = new java.util.HashMap<String, Double>(); 

    inMap.putAll(state); 

    int topN = 2; 
    for (int i = 1; i <= topN; i++) { 
     double maxVal = -1; 
     String moveKey = null; 
     for (java.util.Map.Entry<String, Double> entry : inMap.entrySet()) { 

      if (entry.getValue() > maxVal) { 
       maxVal = entry.getValue(); 
       moveKey = entry.getKey(); 
      } 
     } 
     if (moveKey != null) { 
      outMap.put(moveKey, maxVal); 
      inMap.remove(moveKey); 
     } 
    } 

    return outMap; 
'; 

Vous devez ensuite enfin de définir l'ensemble d'appeler les deux fonctions que vous avez définies

CREATE OR REPLACE AGGREGATE group_and_total(text, double) 
    SFUNC state_group_and_total 
    STYPE map<text, double> 
    FINALFUNC topFinal 
    INITCOND {}; 

Voyons voir si cela fonctionne .

CREATE table test (partition int, clustering text, amount double, PRIMARY KEY (partition, clustering)); 
INSERT INTO test (partition , clustering, amount) VALUES (1, '2015', 99.1); 
INSERT INTO test (partition , clustering, amount) VALUES (1, '2016', 18.12); 
INSERT INTO test (partition , clustering, amount) VALUES (1, '2017', 44.889); 
SELECT * from test; 

partition | clustering | amount 
-----------+------------+-------- 
     1 |  2015 | 99.1 
     1 |  2016 | 18.12 
     1 |  2017 | 44.889 

Maintenant, roulement de tambour ...

SELECT group_and_total(clustering, amount) from test where partition=1; 

agg.group_and_total(clustering, amount) 
------------------------------------------- 
      {'2015': 99.1, '2017': 44.889} 

Donc, vous voyez, il a gardé les 2 premières lignes en fonction du montant. Notez que les clés ne seront pas triées car c'est une carte, et je ne pense pas que nous puissions contrôler l'ordre des clés dans la carte, donc trier dans FINALFUNC serait une perte de ressources. Si vous avez besoin de la carte triée, vous pouvez le faire dans le client.

Je pense que vous pourriez faire plus de travail dans la fonction state_group_and_total pour supprimer des éléments de la carte au fur et à mesure. Cela pourrait être mieux pour éviter que la carte ne devienne trop grande.

+0

Excellente réponse! Je vais devoir donner un coup de feu. – Aaron

+0

J'ai essayé celui-ci mais il montre l'erreur "cqlsh: chris> sélectionnez group_and_total (timestamp, value) de l'entrepôt; InvalidRequest: code = 2200 [Invalid query] message =" Erreur de type: la valeur ne peut pas être p assed comme argument 1 de la fonction chris.group_and_total de type double " –

+0

Peut-être que vous avez copié quelque chose de mal Le premier argument de la fonction group_and_total dans mon exemple est de type texte, pas double Faire un describe décrire et décrire AGGREGATE sur les fonctions et s'assurer qu'ils sont corrects . –