2016-10-31 3 views
0

Comment faire référence à des champs dans un type défini par l'utilisateur lors de l'utilisation de fonctions définies par l'utilisateur Java. J'ai trouvé des exemples pour map, set et tuple, mais pas pour les types définis par l'utilisateur avec plusieurs champs.Cassandra: Comment référencer un champ dans un type défini par l'utilisateur dans une fonction définie par l'utilisateur (Java)

J'ai le type suivant défini:

create type avg_type_1 (
    accum tuple<text,int,double>, // source, count, sum 
    avg_map map<text,double>  // source, average 
); 

Le code suivant:

CREATE FUNCTION average_by_source_1(state avg_type_1, source text, value double) 
    CALLED ON NULL INPUT 
    RETURNS avg_type_1 
    LANGUAGE java 
    AS $$ 

     // when no source yet, save the first source, set the count to 1, and set the value 
     if (state.accum.getString(0) == null) { 
      state.accum.setString(0, source); 
      state.accum.setInt(1, 1); 
      state.accum.setDouble(2, value); 
     } 
     ... 

renvoie l'erreur:

InvalidRequest: Error from server: code=2200 [Invalid query] message="Java source compilation failed: 
Line 4: accum cannot be resolved or is not a field 
+0

Je pense que getMap() de la classe UDTValue est la solution. Il compile. Je vais poster une réponse quand j'ai un exemple de travail complet. – rwfbc

Répondre

1

En Java la variable UDT est représenté par la classe com.datastax.driver.core. UDTValue. Cette classe a des méthodes get et set. Il existe des méthodes utilisant un index (0 ...) pour identifier les champs (dans l'ordre dans lequel ils sont définis dans l'UDT) et les méthodes qui utilisent le nom du champ.

Voir API Doc.

Voici quelques exemples, en utilisant le type défini dans la question:

TupleValue accumState = state.getTupleValue("accum"); 
String prevSource = accumState.getString(0); 
Map<String,Double> avgMap = state.getMap("avg_map", String.class, Double.class); 

La première ligne obtient le accum champ de l'état de la fonction. Au lieu du nom, l'index 0 (zéro, c'est le premier champ) pourrait être utilisé.

La deuxième ligne obtient le premier élément du tuple. Seule la version de l'index peut être utilisée, car les éléments d'un tuple ne sont pas nommés.

La troisième ligne obtient le champ avg_map.

accumState.setDouble(2, value); 
state.setTupleValue("accum", accumState); 

L'exemple ci-dessus définit le troisième élément du tuple, et met alors le tuple retour dans la variable d'état de la fonction. Notez que vous devez replacer le tuple dans la variable d'état. Ce qui suit ne fonctionne pas.

// does not work 
state.getTupleValue("accum").setDouble(2, value); 

Ci-dessous l'exemple complet UDF.

// sums up until the source changes, then adds the avg to the map 
// IMPORTANT: table must be ordered by source 
CREATE OR REPLACE FUNCTION average_by_source_1(state avg_type_1, source text, value double) 
    CALLED ON NULL INPUT 
    RETURNS avg_type_1 
    LANGUAGE java 
    AS $$ 

     TupleValue accumState = state.getTupleValue("accum"); 
     String prevSource = accumState.getString(0); 

     // when no source yet, save the first source, set the count to 1, and set the value 
     if (prevSource == null) { 
      accumState.setString(0, source); 
      accumState.setInt(1, 1); 
      accumState.setDouble(2, value); 
      state.setTupleValue("accum", accumState); 
     } 

     // when same source, increment the count and add the value 
     else if (prevSource.equals(source)) { 
      accumState.setInt(1, accumState.getInt(1) + 1); 
      accumState.setDouble(2, accumState.getDouble(2) + value); 
      state.setTupleValue("accum", accumState); 
     } 

     // when different source, calc average and copy to map, then re-init accumulation 
     else if (accumState.getInt(1) > 0) { 
      double avgVal = accumState.getDouble(2)/accumState.getInt(1); 
      Map<String,Double> mapState = state.getMap("avg_map", String.class, Double.class); 
      mapState.put(prevSource, avgVal); 
      state.setMap("avg_map", mapState, String.class, Double.class); 
      accumState.setString(0, source); 
      accumState.setInt(1, 1); 
      accumState.setDouble(2, value); 
      state.setTupleValue("accum", accumState); 
     } 

     // should not happen - prev case uses "if" to avoid division by zero 
     else { 
      Map<String,Double> mapState = state.getMap("avg_map", String.class, Double.class); 
      mapState.put("ERROR: div by zero", null); 
      accumState.setString(0, source); 
      accumState.setInt(1, 1); 
      accumState.setDouble(2, value); 
      state.setTupleValue("accum", accumState); 
     } 

     // IMPROTANT: final function must calculate the average for the last source and 
     //   add it to the map. 

     return state; 

    $$ 
;