2016-10-31 7 views
0

Каким образом можно указывать поля в определенном пользователем типе при использовании функций, определенных пользователем Java. Я нашел примеры для map, set и tuple, но не для пользовательских типов с несколькими полями.Cassandra: как ссылаться на поле в пользовательском типе в пользовательской функции (Java)

Я следующий тип определен:

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

Следующий код:

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

возвращает ошибку:

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

Я думаю, что getMap() из класса UDTValue является решением. Он компилируется. Я отправлю ответ, когда у меня будет полный рабочий пример. – rwfbc

ответ

1

В Java переменная UDT представлена ​​классом com.datastax.driver.core. UDTValue. Этот класс имеет методы get и set. Существуют методы, использующие индекс (0 ...) для идентификации полей (в порядке их определения в UDT) и методов, которые используют имя поля.

См. API Doc.

Вот некоторые примеры, используя тип, определенный в вопросе:

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

Первая строка получает Accum поля от состояния функции. Вместо имени можно использовать индекс 0 (ноль, это первое поле).

Вторая строка получает первый элемент кортежа. Можно использовать только индексную версию, так как элементы кортежа не называются.

Третья строка получает поле avg_map.

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

Приведенный выше пример устанавливает третий элемент кортежа, а затем помещает кортеж обратно в переменной состояния функции. Обратите внимание, что вы должны вернуть кортеж в переменную состояния. Следующая работа не работает.

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

Ниже приведен полный пример 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; 

    $$ 
;