Java から Cassandra を使ってみる その3
Windows で Cassandra を動かしてみる
Java から Cassandra を使ってみる その1
Java から Cassandra を使ってみる その2
の続きです。
複数の Column の値を取得してみる
今回は org.apache.cassandra.thrift.Cassandra.Client#get_slice() メソッドを使用し、複数の Column の値を取得してみます。
Cassandra では通常の KVS と異なり、一つの key の下に複数の Column を設定する事ができます。
一つの key に関連付けされた複数の Column の値を一括で取得したい場合には、以下のように get_slice() が利用できます。
package example.cassandra; import java.util.Date; import java.util.List; import org.apache.cassandra.thrift.Cassandra; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ColumnOrSuperColumn; import org.apache.cassandra.thrift.ColumnParent; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.SlicePredicate; import org.apache.cassandra.thrift.SliceRange; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; public class CassandraSliceExample { public static void main(String[] args) { try { // Thrift を使用して Cassandra に接続 TTransport port = new TSocket("localhost", 9160); TProtocol protocol = new TBinaryProtocol(port); Cassandra.Client client = new Cassandra.Client(protocol); port.open(); // デフォルトで用意されている Keyspace を使用する String keySpace = "Keyspace1"; // README.txt で使用されているサンプルの key を使用する String key = "jsmith"; // ColumnParent には ColumnFamily 名または ColumnFamily/SuperColumn 名を指定 ColumnParent columnParent = new ColumnParent("Standard1"); SliceRange sliceRange = new SliceRange(); // 取得カラムの範囲を指定。全部取得する場合は空の byte 配列を指定 sliceRange.setStart(new byte[0]); sliceRange.setFinish(new byte[0]); SlicePredicate slicePredicate = new SlicePredicate(); slicePredicate.setSlice_range(sliceRange); // get_slice を呼ぶ List<ColumnOrSuperColumn> results = client.get_slice(keySpace, key, columnParent, slicePredicate, ConsistencyLevel.ONE); for (int i = 0; i < results.size(); i++) { ColumnOrSuperColumn result = results.get(i); Column col = result.column; System.out.printf("[%d] カラム名:[%s] 値:[%s] タイムスタンプ:[%s]\n", i + 1, new String(col.name, "UTF8"), new String(col.value, "UTF8"), new Date(col.timestamp)); } port.close(); } catch(Exception e){ e.printStackTrace(); } } }
上記を実行すると、以下のように値が出力されました。
[1] カラム名:[age] 値:[42] タイムスタンプ:[Sun May 02 02:37:33 JST 2010] [2] カラム名:[first] 値:[John] タイムスタンプ:[Sat May 01 00:43:44 JST 2010] [3] カラム名:[last] 値:[Smith] タイムスタンプ:[Sun May 02 02:37:05 JST 2010]
指定した key である「jsmith」に関連付けられた全ての Column の値が取得できている事が分かります。
get_slice() では、引数の SliceRange クラスや SlicePredicate クラスの設定により、取得する Column の範囲を指定する事が可能です。
上記サンプルコードのうち、SliceRange#setStart() を呼び出している辺りを以下のように変更してみます。
// "age" から "first" までを取得 sliceRange.setStart("age".getBytes("UTF8")); sliceRange.setFinish("first".getBytes("UTF8"));
実行すると、今度は以下のように "age" カラムと "first" カラムの値のみ取得され、"last" カラムの値は取得されないようになりました。
[1] カラム名:[age] 値:[42] タイムスタンプ:[Sun May 02 02:37:33 JST 2010] [2] カラム名:[first] 値:[John] タイムスタンプ:[Sat May 01 00:43:44 JST 2010]
次に、先ほど変更した setStart()、setFinish() 呼び出しの下の辺りに以下のコードを追加してみます。
// "age" から "first" までを取得 sliceRange.setStart("age".getBytes("UTF8")); sliceRange.setFinish("first".getBytes("UTF8")); // count を設定すると指定した数のカラムのみ取得する // <= 追加 sliceRange.setCount(1); // <= 追加
今度は、以下のように "age" のみが取得できました。
[1] カラム名:[age] 値:[42] タイムスタンプ:[Sun May 02 02:37:33 JST 2010]
setCount() を指定すると、setStart()、setFinish で指定した範囲のうち、指定した数の Column の値のみ取得するようになるようです。
最後に、SliceRange クラスを使わず、指定したカラム名の値のみ取得してみます。
その為には、SlicePredicate#setColumn_names() を使用します。
package example.cassandra; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.cassandra.thrift.Cassandra; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ColumnOrSuperColumn; import org.apache.cassandra.thrift.ColumnParent; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.SlicePredicate; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; public class CassandraSliceExample { public static void main(String[] args) { try { // Thrift を使用して Cassandra に接続 TTransport port = new TSocket("localhost", 9160); TProtocol protocol = new TBinaryProtocol(port); Cassandra.Client client = new Cassandra.Client(protocol); port.open(); // デフォルトで用意されている Keyspace を使用する String keySpace = "Keyspace1"; // README.txt で使用されているサンプルの key を使用する String key = "jsmith"; // ColumnParent には ColumnFamily 名または ColumnFamily/SuperColumn 名を指定 ColumnParent columnParent = new ColumnParent("Standard1"); SlicePredicate slicePredicate = new SlicePredicate(); // 指定したカラムのみ取得する場合は setColumn_names にカラム名の byte 配列の List を設定。 // 但し、カラム名の List と SliceRange は両方が設定されているとエラーになる。 slicePredicate.setColumn_names(new ArrayList<byte[]>() {{ add("first".getBytes("UTF8")); }}); // get_slice を呼ぶ List<ColumnOrSuperColumn> results = client.get_slice(keySpace, key, columnParent, slicePredicate, ConsistencyLevel.ONE); for (int i = 0; i < results.size(); i++) { ColumnOrSuperColumn result = results.get(i); Column col = result.column; System.out.printf("[%d] カラム名:[%s] 値:[%s] タイムスタンプ:[%s]\n", i + 1, new String(col.name, "UTF8"), new String(col.value, "UTF8"), new Date(col.timestamp)); } port.close(); } catch(Exception e){ e.printStackTrace(); } } }
実行結果は以下のようになります。
[1] カラム名:[first] 値:[John] タイムスタンプ:[Sat May 01 00:43:44 JST 2010]
"first" カラムの値のみが取得できた事がわかります。
コード中のコメントにも書きましたが、setColumn_names() を使用する際は、SliceRange は指定できない事に注意して下さい。
SlicePredicate に両方を設定してしまっていると、 get_slice() 呼び出し時に以下のようなエラーが発生してしまいます。
InvalidRequestException(why:predicate column_names and slice_range may not both be present) at org.apache.cassandra.thrift.Cassandra$get_slice_result.read(Cassandra.java:4873) at org.apache.cassandra.thrift.Cassandra$Client.recv_get_slice(Cassandra.java:393) at org.apache.cassandra.thrift.Cassandra$Client.get_slice(Cassandra.java:367) at example.cassandra.CassandraSliceExample.main(CassandraSliceExample.java:59)
次回は multiget_slice() について調べてみたいと思います。