Primeros pasos con Cassandra Java y Hector

De ChuWiki
Saltar a: navegación, buscar

Una vez instalado Cassandra, vamos a ver aquí como acceder desde Java. Cassandra ofrece dos protocolos de acceso (CQL3 y Thrift) y tenemos multitud de drivers java para acceder por cualquiera de ellos.

En Primeros pasos con Cassandra desde Java vimos algún ejemplo de cómo usar el protocolo CQL3 que es posiblemente más sencillo, sobre todo para los que vienen de trabajar con bases de datos SQL.

Aquí vamos a ver algún ejemplo de uso con el protocolo Thirft, y en conccreto, usando el driver Hector


Conseguir el driver

Para conseguir el driver, nos podemos descargar los fuentes y compilarlos, o bien añadir en nuestro proyecto directamente la dependencia de maven

        <dependency>
            <groupId>org.hectorclient</groupId>
            <artifactId>hector-core</artifactId>
            <version>1.1-4</version>
        </dependency>


Crear un KeySpace y una Column Family

El código para crear un KeySpace con una Column Family es el siguiente

Cluster cluster = HFactory.getOrCreateCluster("un-nombre-cluster", "127.0.0.1:9160");

HFactory es una clase del Driver Hector con un montón de métodos para hacer cosas. En este caso se está usando el método getOrCreateCluster() para para obtener una clase Cluster que represente al Cluster Casssandra. Admite dos parámetros:

  • El nombre de cluster es un nombre propio de nuestro programa, podemos poner lo que queramos y no tiene nada que ver con el nombre real del cluster configurado en Cassandra. A través de HFactory y con una llamada como la que se acaba de hacer, podríamos obtener este cluster en cualquier parte de nuestro código sin necesidad de andar pasándolo de un lado a otro.
  • IP y puerto de conexión al cluster. Si no se pone puerto, se usará el de defecto que es precisamente el 9160.

Suponiendo que el KeySpace y la Column Family no estén ya creadas en Cassandra y queramos crearlas, debemos dar los siguientes pasos:

  • Definir cómo es la Column Family
  • Definir cómo es el KeySpace, añadiéndole la Column Family
  • Crear el esquema realmente en Cassandra.

Las tres líneas de código necesario para esto son

ColumnFamilyDefinition columnFamilyDefinition = HFactory
        .createColumnFamilyDefinition("MyKeyspace", "ColumnFamilyName",
                ComparatorType.BYTESTYPE);

KeyspaceDefinition keySpaceDefinition = HFactory
        .createKeyspaceDefinition("MyKeyspace",
                ThriftKsDef.DEF_STRATEGY_CLASS, 1,
                Arrays.asList(columnFamilyDefinition));

        
cluster.addKeyspace(keySpaceDefinition);

En la primera línea, y usando HFactory.createColumnFamilyDefinition() indicamos cómo es la Column Family. Indicamos

  • el nombre del KeySpace al que queremos que pertenezca "MyKeyspace",
  • indicamos el nombre para esta Column Family "ColumnFamilyName" y
  • opcionalmente, indicamos el Validador/Comparador por defecto para los columnas de esta Column Family. Este Validador/Comparador indica de alguna forma cual es el tipo de columna por defecto (bytes, string, boolean, etc). Si no se indica nada, por defecto es bytes, que es justo el tipo de parámetro que estamos pasando ComparatorType.BYTESTYPE

En la segunda línea y usando HFactory.createKeysapceDefinition(), indicamos cómo es el KeySpace. Se pasa:

  • el nombre que queremos para el KeySpace "MyKeyspace",
  • el tipo de estrategía para distribuir los datos en los distintos Cluster Cassandra (ThriftKsDef.DEF_STRATEGY_CLASS para una estrategia simple en la que sólo hay un cluster Cassandra y todos los datos van ahí, usaríamos ThriftKsDef.NETWORK_TOPOLOGY_STRATEGY si tuviéramos varios Cluster Cassandra).
  • Número de réplicas que queremos del dato en los distintos cluster. Debe ser un número menor o igual que el número de cluster disponibles. En ese ejemplo para un solo cluster, ponemos que el dato sólo se guarde una vez.
  • Lista de Column Families, en la que sólo hemos incluido la que acabamos de crear.

Finalmente, en la tercera línea, la llamada a cluster.addKeyspace() hace que se cree realmente este KeySpace y Column Family en el nodo Cassandra. Esta llamada dará una excepción si el KeySpace existe, así que se puede hacer la comprobación antes de hacer la llamada

KeyspaceDefinition keyspaceDef = cluster.describeKeyspace("MyKeyspace");

if (keyspaceDef == null) {
   ColumnFamilyDefinition columnFamilyDefinition = HFactory
        .createColumnFamilyDefinition("MyKeyspace", "ColumnFamilyName",
                ComparatorType.BYTESTYPE);

   KeyspaceDefinition keySpaceDefinition = HFactory
        .createKeyspaceDefinition("MyKeyspace",
                ThriftKsDef.DEF_STRATEGY_CLASS, 1,
                Arrays.asList(columnFamilyDefinition));

   cluster.addKeyspace(keySpaceDefinition);
}

con cluster.describeKeyspace("MyKeyspace") intentamos obtener el KeySpace del nodo Cassandra. Si devuelve null, es que no existe, así que podemos proceder a crearlo.


Modificar datos

Para modificar datos necesitamos tener una instancia de Keyspace, que representa el Keyspace sobre el que vamos a trabajar. Se puede obtener esta instancia de la siguiente forma

// El cluster lo teníamos de antes, por lo que podemos volver a obtenerlo así
Cluster cluster = HFactory.getCluster("un-nombre-cluster");

// El Keyspace
Keyspace ksp = HFactory.createKeyspace("MyKeyspace", cluster);

El cluster lo teníamos desde el principio, pero se puede obtener en cualquier sitio sin más que dar el nombre que hayamos decidido darle la primera vez. Si ya estaba creado con un nombre, bastaría obtenerlo con HFactory.getCluster("un-nombre-cluster"), sin necesidad de pasar otra vez la IP y puerto de conexión, como se acaba de hacer en el código de ejemplo.

El Keyspace se obtiene con HFactory.createKeyspace("MyKeyspace", cluster) pasando el nombre del Keyspace y el cluster que acabamos de obtener. Esta instancia de Keysapace podemos guardarla en algún sitio de forma que no haya que crearla cada vez que queramos tocar la base de datos.

A partir de este momento, tenemos dos posibles caminos a seguir para modificar datos en la base de datos. Uno es usar una instancia de Mutator que nos permite modificar los datos contenidos en el KeySpace. La otra es usar una instancia de ColumnFamilyTemplate, que representa una Column Family y con la que podemos modificar datos de esa Column Family.

Mutator es más general, por lo que tendremos que pasar como parámetros contínuamente el nombre de la Column Family que queremos modificar, mientras que ColumnFamilyTemplate nos facilita las cosas, pero siempre limitándonos a una Column Family concreta.


ColumnFamilyTemplate

Para modificar datos con ColumnFamilyTemplate, necesitamos una instancia de ColumnFamilyTemplate, que representa la Column Family sobre la que queremos trabajar.

La ColumnFamilyTemplate se obtiene haciendo un new de ThriftColumnFamilyTemplate<K,N> pasando una serie de parámetros.

// La ColumnFamilyTemplate
ColumnFamilyTemplate<String, String> template = new ThriftColumnFamilyTemplate<String, String>(
          ksp, "ColumnFamilyName", StringSerializer.get(), StringSerializer.get());

  • En la plantilla, K representa el tipo de la clave principal de la Column Family. Si la clave de cada fila es un String, aquí se pone String, si es un array de bytes, entonces aquí se pone byte[], etc. Para este ejemplo simple, usaremos String, pero la clave principal de una Column Family puede ser cualquier cosa, como un timeUUID, un byte[] o cualquiera de los otros tipos que admite apache Cassandra.
  • En la plantilla, N representa el tipo del nombre de las Columna. Para este ejemplo simple, usaremos String, aunque nada impide en Cassandra que los nombres de las columnas sean de otros tipos como como byte[], timeUUID o cualquier otro.

Y los parámetros...

  • ksp es el Keyspace al que pertenece la Column Family.
  • "ColumnFamilyName" es el nombre de la Column Family.
  • El primer StringSerializer.get() nos devuelve la clase capaz de convertir un String a byte[] y viceversa para la clave primaria de la Column Family. El motivo es que Cassandra internamente usa siempre byte[] y como hemos dicho que la clave primaria de nuestra Column Family es String, necesitamos una clase capaz de convertir del uno al otro. Si hubieramos dicho que la clave primaria es por ejemplo un timeUUID, usaríamos TimeUUIDSerializer.get(). Estos serializer son los de defecto, pero nada impediría que creáramos nuestro propio Serializer.
  • El segundo StringSerializar.get() es lo mismo, para para los nombres de columna, que también hemos dicho que son String.

Al igual que Keypsace, este ColumnFamilyTemplate puede guardarse en algún sitio accesible para ser reutilizado siempre que haga falta y no tener que crearlo cada vez que se quieran modificar o consultar datos en esta Column Family.

Ahora ya podemos empezar a modificar datos. En Cassandra no hay ninguna diferencia entre update e insert. Con el protocolo Thrift y el driver Hector, símplemente se hace update y el valor se cambiará en base de dato si existe, o se creará nuevo si no existe.

El código para insertar/modificar un dato se parece al siguiente:

        ColumnFamilyUpdater<String, String> updater = template.createUpdater();
        for (int i = 0; i < 10; i++) {
            updater.addKey("clave "+i);
            updater.setString("Una Columna", "texto " + i);
        }
        template.update(updater);

Lo primero es obtener un ColumnFamilyUpdater<K,N>, donde K y N son, como vimos antes, el tipo de la clave principal de la Column Family y el tipo de los nombres de cada columna. Ambos String en nuestro ejemplo.

Vamos a insertar 10 datos, así que hacemos un bucle de 10. La clave será "clave 0", "clave 1", ..., así que vamos llamando, dentro del bucle a update.addKey(), pasando en cada momento la clave que nos interesa. Dentro del bucle también y después de haber fijado la clave, llamamos a alguno de los métodos set() que permiten indicar el nombre de la columna y el dato a introducir. En nuestro ejemplo, usaremos setString(), siendo "Una Columna" el nombre de la columna y "texto 0", "texto 1", los diferentes valores que iremos metiendo en cada fila.

Finalmente y ya fuera del bucle, hacemos un template.update(updater) para guardar todo en base de datos.

Si ahora, desde un cliente Thrift de Cassandra listamos el contenido de nuestra columna, obtenemos

-- Necesario para ver como texto plano los campos.
[default@MyKeyspace] assume ColumnFamilyName keys as ascii;
[default@MyKeyspace] assume ColumnFamilyName comparator as ascii;
[default@MyKeyspace] assume ColumnFamilyName validator as ascii;
...
-- Listado de los datos recien insertados
[default@MyKeyspace] list ColumnFamilyName;
Using default limit of 100
Using default cell limit of 100
-------------------
RowKey: clave 0
=> (name=Una Columna, value=texto 0, timestamp=1373875786856000)
-------------------
RowKey: clave 5
=> (name=Una Columna, value=texto 5, timestamp=1373875786856000)
-------------------
RowKey: clave 7
=> (name=Una Columna, value=texto 7, timestamp=1373875786856000)
-------------------
RowKey: clave 6
=> (name=Una Columna, value=texto 6, timestamp=1373875786856000)
-------------------
RowKey: clave 1
=> (name=Una Columna, value=texto 1, timestamp=1373875786856000)
-------------------
RowKey: clave 9
=> (name=Una Columna, value=texto 9, timestamp=1373875786856000)
-------------------
RowKey: clave 2
=> (name=Una Columna, value=texto 2, timestamp=1373875786856000)
-------------------
RowKey: clave 4
=> (name=Una Columna, value=texto 4, timestamp=1373875786856000)
-------------------
RowKey: clave 8
=> (name=Una Columna, value=texto 8, timestamp=1373875786856000)
-------------------
RowKey: clave 3
=> (name=Una Columna, value=texto 3, timestamp=1373875786856000)

10 Rows Returned.
Elapsed time: 42 msec(s).

Mutator

Si preferimos usar Mutator para modificar datos, el código puede ser como este

// Cluster y Keyspace, que ya tenemos de antes.
Cluster cluster = HFactory.getCluster("un-nombre-cluster");
Keyspace ksp = HFactory.createKeyspace("MyKeyspace", cluster);

Mutator<String> mutator = HFactory.createMutator(ksp, StringSerializer.get());
for (int i=0;i<10;i++){
   mutator.addInsertion("clave "+i, "ColumnFamilyName", HFactory.createColumn("Una Columna", "texto "+i));
}
mutator.execute();

El Cluster y Keyspace igual que antes, ya los tenemos o los obtenemos de nuevo.

El Mutator se crea con el método HFactory.createMutator(), pasando como parámetros el Keyspace y un serializador adecuado para la clave primaria de las Column Family que queramos modificar, en este caso, un String.

Podemos ahora usar Mutator de dos formas:

  • Llamando a sus métodos insert y delete para modificar y borrar datos en la base de datos de forma inmediata. Una llamada a insert hace un insert o update en base de datos y una llamada a delete hace un delete en base de datos.
  • Llamando a sus métodos addInsertion y addDeletion para ir añadiendo inserts y deletes que no se harán inmediatamente, sino que quedarán encolados hasta que llamemos al método execute() de Mutator, de forma que se hagan todas esas operaciones encoladas de golpe.

En el ejemplo estamos usando la segunda forma, llamando 10 veces a addInsertion() para luego llamar una vez a execute(). En addInsertion() pasamos los siguientes parámetros:

  • "clave "+i es la clave primaria de la Column Family donde queremos insertar/modificar datos.
  • "ColumnFamilyName" es el nombre de la Column Family donde queremos insertar/modificar datos.
  • El tercer parámetro es una HColumn, que básicamente contiene el nombre de la columna y su valor. Podemos obtener este HColumn con el método HFactory.createColumn() pasando nombre y valor como parámetros.

Al llamar a mutator.execute(), se realizarán en base de datos los 10 insert/update de golpe.


Consultar datos

Al igual que con los insert/update, tenemos dos formas de hacer consultas. Una usando la clase ColumnQuery que nos permite consultar cualquier columna de cualquier Column Family, y la otra usar la clase ColumnFamilyResult, específica para una Column Family concreta y que nos ahorra escribir el nombre de esta Column Family en cada consulta. Veamos ejemplos de ambos.


ColumnQuery

Damos por supuesto, como en los ejemplos anteriores, que ya tenemos el Cluster y el Keysapce instanciados. El código para consultar en una Column Family, con una clave primaria concreta y un nombre de columna es el siguiente

        // ksp es el keyspace, obtenido como en los ejemplos anteriores.

        ColumnQuery<String, String, String> columnQuery = HFactory
                .createStringColumnQuery(ksp);

        columnQuery.setColumnFamily("ColumnFamilyName").setKey("clave 1")
                .setName("Una Columna");

        QueryResult<HColumn<String, String>> result = columnQuery.execute();

        System.out.println(result.get().getName() + " = "
                + result.get().getValue());

Usando HFactory.createStringColumnQuery() obtenemos una ColumnQuery para consulta de columnas interpretadas como String.

A este columnQuery le vamos pasando

  • La Column Family con setColumnFamily()
  • La Clave primaria de la fila que queremos consultar, con setKey().
  • El nombre de la columna que queremos consultar, con setName().

Como cada uno de estos métodos set() devuelve la misma instancia de ColumnQuery, es posible encadenar los set() en una sola línea como hemos hecho en el ejemplo.

Finalmente, columnQuery.execute() ejecuta la consulta, devolviéndonos una instancia de QueryResult con los resultados. El método get() de ese QueryResult nos devuelve una HColumn, que contiene un getName() para el nombre de la columna y getValue() para el valor de la misma.


Consultar varias columnas

Si queremos consultar varias columnas simultáneamente de la misma fila, en vez de una ColumnQuery, necesitamos una instancia de SliceQuery. El código puede ser como este

SliceQuery<String, String, String> sliceQuery = 
   HFactory.createSliceQuery(ksp, StringSerializer.get(), StringSerializer.get(), StringSerializer.get());

sliceQuery.setColumnFamily("ColumnFamilyName").setKey("clave 3").setColumnNames("columna 1","columna 2","columna 3");

QueryResult<ColumnSlice<String, String>> resultados = sliceQuery.execute();

List<HColumn<String, String>> columnas = resultados.get().getColumns();

for (HColumn<String, String> columna : columnas){
   System.out.println(columna.getName()+" "+columna.getValue());
}

Se obtiene la SliceQuery por medio de HFactory.createSliceQuery(). Los parámetros a pasar son:

  • Keyspace, obtenido de la forma habitual.
  • Serializador para la clave principal de la Column Family. En nuestro ejemplo, un String.
  • Serializador para el nombre de la columna, en nuestro ejemplo, un String.
  • Serializador para el valor de la columna, en nuestro ejemplo, un String.

Luego rellenamos sliceQuery con los datos específicos de nuestra consulta. Concretamente, necesitamos rellenar

  • setColumnFamily() para indicar el nombre de la Column Family que queremos consultar.
  • setKey() para indicar la clave principal de la fila que queremos consultar.
  • setColumnNames() para indicar todos los nombres de columnas que queremos consultar.

Una vez hecho esto, llamamos al método execute() para realizar la consulta, nos devolverá una instancia de QueryResult<ColumnSlice>. De esa instancia queryResult, llamando a su método get() nos devuelve el ColumnSlice y el método getColumns() de ColumnSlice nos devuelve una lista List<HColumn>.

Ahora sólo nos queda recorrer la List<HColumn> para obtener los distintos valores de las columnas resultado de la consulta. Vamos llamando a columna.getName() para obtener el nombre de la columna y columna.getValue() para obtener el valor de dicha columna.


Consultar varias filas y varias columnas

Si queremos consultar simultáneamente varias filas y columnas, tenemos dos posibles clases: MultigetSliceQuery y RangeSlicesQuery. En la primera debemos indicar todas las claves primarias de las filas que nos interesan. En la segunda, un rango de claves primarias. Por lo demás, el código es prácticamente igual en ambas. Vamos con la primera, MultiSliceQuery. El código puede ser como este

MultigetSliceQuery

MultigetSliceQuery<String, String, String> multigetSliceQuery = HFactory
        .createMultigetSliceQuery(ksp, StringSerializer.get(),
                StringSerializer.get(), StringSerializer.get());

multigetSliceQuery.setColumnFamily("ColumnFamilyName");
multigetSliceQuery.setKeys("clave 1", "clave 2", "clave 3");
multigetSliceQuery.setRange("columna 2", "columna 8", false, 3);

QueryResult<Rows<String, String, String>> result = multigetSliceQuery.execute();

Iterator<Row<String, String, String>> iterador = result.get().iterator();

while (iterador.hasNext()) {
     Row<String, String, String> fila = iterador.next();
     List<HColumn<String, String>> columnas = fila.getColumnSlice().getColumns();

     System.out.print(fila.getKey() + " ");
     for (HColumn<String, String> columna : columnas) {
        System.out.print(columna.getName() + " " + columna.getValue());
     }
     System.out.println();
}

Primero obtenemos la instancia de MultigetSliceQuery usando el método HFactory.createMultigetSliceQuery(). Pasamos como parámetros los mismos que en el apartado anterior de SliceQuery: El keyspace y los serializadores para clave primaria, nombre de columna y valor.

Los parámetros de consulta son similares a los de SliceQuery, pero con algunas variantes.

  • setColumnFamily() es igual, se pasa el nombre de la Column Family de interés.
  • setKeys() varía en que podemos pasar muchas claves primarias, en vez de sólo una.
  • setRange() también está disponible en SliceQuery y es otra alternativa para indicar las columnas. Con setColumnNames(), indicamos los nombres de columnas una a una. Con setRange() podemos indicar un "rango" de columnas. El rango de columnas se obtiene indicando la primera y la última que queremos ordenadas por orden alfabético. Por ejemplo, si tenemos las columnas "a", "b", "c", ... "z" e indicamos de rango desde "d" hasta "f", obtendremos las columnas "d", "e" y "f". Veamos los parámtros de setRange() con calma:
    • Columna inicial del rango que queremos. Poniendo "" o null indicamos que queremos desde la primera que haya por orden alfabético.
    • Columna final del rango que queremos. Poniendo "" o null indicamos que queremos hasta la última que haya por orden alfabético.
    • El orden alfabético de las columnas. false es el orden alfabético normal, true es orden alfabético inverso. Poniendo false, la columna inicial debe estar alfabéticamente antes que la columna final que hemos pasado como primer y segundo parámetro. Poniendo true, debemos poner las columnas de inicio y fin al revés, es decir, como primer parámetro la que alfabéticamente va detrás.
    • Número máximo de columnas que queremos. En el ejemplo hemos puesto 3, así que obtendremos como máximo 3 columnas.

Una vez rellenos todos los parámetros, no queda más que llamar a multigetSliceQuery.execute() para obtener los resultados en forma de QueryResult<Rows<String, String, String>>.

El método get() de los resultados nos devuelve el Rows<String,String,String>. El método iterator() de ese Rows nos devuelve un iterador para las filas de forma que iremos obteniendo instancias de Row<String, String, String> por cada fila. Llamando al método getColumnSlice() de cada Row obtendremos un ColumnSlice<String,String> y llamando al método getColumns() de este obtenemos, por fin, una lista List<HColumn<String, String>> de las columnas de esa Row. Adicionalmente, el método getKay() de la Row nos da la clave primaria de esa fila. En fin, un poco rebuscado, pero puedes ver en el código todo el proceso de como obtener los valores consultados.

RangeSlicesQuery

RangeSlicesQuery hace básicamente lo mismo que MultigetSliceQuery, pero tiene dos diferencias importantes.

La primera es que en vez de indicar las claves primarias una a una, podemos indicar un rango de claves. Hay un tema importante a tener en cuenta aquí y es el orden que Cassandra pone a las claves primarias. En la configuración de Cassandra, fichero cassandra.yaml, se indica por defecto

partitioner: org.apache.cassandra.dht.Murmur3Partitioner

que corresponde a que los datos deben distribuirse aleatoriamente por los nodos Cassandra disponibles. Puesto que el reparto en nodos se hace en función de la clave primaria, esto hace que el orden de las claves primarias sea aleatorio. Si ponemos un rango de busqueda así rangeSliceQuery.setRange("clave 1", "clave 3"), no necesariamente obtendremos "clave 1", "clave 2" y "clave 3", sino que obtendremos todas las claves que estén entre "clave 1" y "clave 3" en el orden aleatorio que le haya dado Cassandra a las claves, por ejemplo, podemos obtener "clave 1","clave 9","clave 2","clave 4","clave 8","clave 3". Si queremos poder hacer búsquedas ordenadas pro claves, debemos bien cambiar partitioner en la configuración para que tenga org.apache.cassandra.dht.ByteOrderedPartitioner. Este partitioner tiene la ventaja de que conserva el orden natural de las claves y podemos hacer consultas por rango de claves y obtenerlas ordenadas, pero tiene la pega de que el reparto de datos en nodos puede no ser uniforme, por ejemplo, si insertamos muchas claves similares, tenderán a ir todas al mismo nodo.

La segunda diferencia entre RangeSlicesQuery y MultigetSliceQuery es que RangeSlicesQuery tiene métodos estilo addEqualsExpression() que nos permiten filtrar por otras columnas que no sean la clave principal. Veamos un código completo de consulta como ejemplo

RangeSlicesQuery<String, String, String> rangeSlicesQuery = HFactory
        .createRangeSlicesQuery(ksp, StringSerializer.get(),
                StringSerializer.get(), StringSerializer.get());

rangeSlicesQuery.setColumnFamily("ColumnFamilyName");
        
// rangeSlicesQuery.setKeys("clave 1", "clave 3");
rangeSlicesQuery.addEqualsExpression("columna 0", "texto 0");

rangeSlicesQuery.setRange("", "", false, 3);
QueryResult<OrderedRows<String, String, String>> result = rangeSlicesQuery.execute();

Iterator<Row<String, String, String>> iterador = result.get().iterator()
while (iterador.hasNext()) {
   Row<String, String, String> fila = iterador.next();
   List<HColumn<String, String>> columnas = fila.getColumnSlice().getColumns();
   System.out.print(fila.getKey() + " ");
   for (HColumn<String, String> columna : columnas) {
      System.out.print(columna.getName() + " " + columna.getValue());
   }
   System.out.println();
}

Se obtiene la instancia de RangeSlicesQuery con HFactory.createRangeSlicesQuery(). Nada especial respecto a MultigetSliceQuery, mismos parámetros. Se pasa con setColumnFamily el nombre "ColumnFamilyName" de la Column Family.

Hemos indicado como poner el rango de claves primarias con setKeys(), pero está comentado ya que no tiene mucho sentido si no se usa un partitioner que conserve el orden natural de las claves. Tampoco es necesario ponerlo si no queremos limitar la búsqueda por clave primaria.

Hemos añadido una condición en que "columna 0" debe valer "texto 0".

Con setRange() indicamos el rango de columnas que queremos como resultado, es decir, todas con un máximo de 3. Se comentó este método en MultigetSliceQuery

Y listo, sólo nos queda llamar a rangeSlicesQuery.execute() para recoger los resultados de la consulta.


Borrar datos

Podemos borrar filas con mutator o con template, al igual que hacíamos a la hora de modificar datos. mutator es más general y permite borrar columnas de cualquier ColumnFamily, mientras que template está asociado a una ColumnFamily y nos ahorra andar metiendo el nombre de la Column Family cada vez. Veamos ejemplos con ambos.

El siguiente código borra una columna usando template

ColumnFamilyTemplate<String, String> template = new ThriftColumnFamilyTemplate<String, String>(
        ksp, "ColumnFamilyName", StringSerializer.get(),
        StringSerializer.get());

ColumnFamilyUpdater<String, String> updater = template.createUpdater();
updater.addKey("clave 1");
updater.deleteColumn("columna 1");
template.update(updater);

Es todo igual que cuando hicimos el update, con la única diferencia de que llamamos a updater.deleteColumn() en vez de a updater.setString() o el método que corresponda al tipo al que queremos hacer update.

Si pasamos null en el método deleteColumn() en vez de pasar un nombre de columna, se borrarán todas las columnas de la fila.

Veamos ahora la otra opción, usando un mutator. El siguiente código borrará todas las columnas de la fila "clave 2" de la Column Family "ColumnFamilyName"

        Mutator<String> mutator = HFactory.createMutator(ksp,
                StringSerializer.get());
        mutator.delete("clave 2", "ColumnFamilyName", null,
                StringSerializer.get());
        mutator.execute();

Los parámetros de delete son la clave de la fila, el nombre de la Column Family, el nombre de la columna que se quiere borrar (null para borrar todas) y el consabido serilizador para el tipo del nombre de la columna, String en este caso.

En caso de tener que hacer varios borrados, podemos hacerlo de forma más eficiente indicado todos esos borrados por medio de addDeletion() para luego ejecutarlos todos de golpe. El siguiente código muestra cómo hacerlo

        mutator.addDeletion("clave 4", "ColumnFamilyName", "columna 4",StringSerializer.get());
        mutator.addDeletion("clave 5", "ColumnFamilyName", "columna 5",StringSerializer.get());
        mutator.execute();

Enlaces externos