追記:
以下の文章では、結局BytesTypeではうまく取得出来ないから、LongTypeを使いましょうということが書いてありますが、LongTypeでもうまく取得出来ないようです。Cassandraemonのバグの可能性もあるので、詳しく調べ中です。
追追記:
試しにJassandraで、.NETからLongTypeに登録した値を取ってみたら、なぜか「72057594037927936」みたいな値が取れました。これを16進数に変換してみると、なんと「100000000000000」。もしかして、1で登録したやつが逆になってる・・・( ̄□  ̄ || とてつもなくイヤな予感がします。
追追追記:
.NETでの登録時に、Reverse関数でバイト配列を逆順にしてやると、なんか想定通りの動きをしました・・・。あれ、ほんとにJavaと.NETって、数値から作ったバイト配列が逆順になるってことでしょうか( ̄□  ̄ || 詳しい方カモン!
追追追追記:
調べてみました。→ 次のエントリへ
Cassandraでは、カラム名の比較方法として、以下の6つが選べます。(本家サイトより引用)
-
BytesType: Simple sort by byte value. No validation is performed.
-
AsciiType: Like BytesType, but validates that the input can be parsed as US-ASCII.
-
UTF8Type: A string encoded as UTF8
-
LongType: A 64bit long
-
LexicalUUIDType: A 128bit UUID, compared lexically (by byte value)
-
TimeUUIDType: a 128bit version 1 UUID, compared by timestamp
カラム名に数値を入れたいときは、普通はLongTypeを使うのですが、今日、LongTypeのカラムに4バイトのint型を突っ込んだら、8バイトじゃないとダメよってエラーがでました( ̄□  ̄ || まあ、普通に対処するなら、バイト変換する前にLong型にキャストしてやればいいんですが、数値を格納する際に毎回キャストするのも面倒なので、比較タイプをBytesTypeにすることでキャストしなくてもいい具合に動いてくれないかどうか調べてみました。
BytesTypeの動作は、「org.apache.cassandra.db.marshal.BytesTypeクラス」で規定されてるようです。
public class BytesType extends AbstractType
{
public int compare(byte[] o1, byte[] o2)
{
return FBUtilities.compareByteArrays(o1, o2);
}
public String getString(byte[] bytes)
{
return FBUtilities.bytesToHex(bytes);
}
}
FBUtilitiesクラスっていうのを使ってるみたいですね。「org.apache.cassandra.utils.FBUtilitiesクラス」のようです。
public static int compareByteArrays(byte[] bytes1, byte[] bytes2){
if(null == bytes1){
if(null == bytes2) return 0;
else return -1;
}
if(null == bytes2) return 1;
int minLength = Math.min(bytes1.length, bytes2.length);
for(int i = 0; i < minLength; i++)
{
if(bytes1[i] == bytes2[i])
continue;
// compare non-equal bytes as unsigned
return (bytes1[i] & 0xFF) < (bytes2[i] & 0xFF) ? -1 : 1;
}
if(bytes1.length == bytes2.length) return 0;
else return (bytes1.length < bytes2.length)? -1 : 1;
}
むう、先頭から順番に、どちらかの配列の終わりに達するまで比較して、それでも同じだったら、配列長の長い方が勝ちとなるようです。
次に、.NETで、BitConverterを使って、バイト配列に変換したとき、どんな感じに格納されるのか知らなかったので、実験してみました。
static void Main(string[] args)
{
uint u1 = 10;
uint u2 = 257;
long l1 = 10;
long l2 = 257;
var bu1 = BitConverter.GetBytes(u1);
var bu2 = BitConverter.GetBytes(u2);
var bl1 = BitConverter.GetBytes(l1);
var bl2 = BitConverter.GetBytes(l2);
Console.WriteLine(bu1[0] + " " + bu1[1] + " " + bu1[2] + " " + bu1[3]);
Console.WriteLine(bu2[0] + " " + bu2[1] + " " + bu2[2] + " " + bu2[3]);
Console.WriteLine(bl1[0] + " " + bl1[1] + " " + bl1[2] + " " + bl1[3] + " " + bl1[4] + " " + bl1[5] + " " + bl1[6] + " " + bl1[7]);
Console.WriteLine(bl2[0] + " " + bl2[1] + " " + bl2[2] + " " + bl2[3] + " " + bl2[4] + " " + bl2[5] + " " + bl2[6] + " " + bl2[7]);
}
/*
結果
10 0 0 0
1 1 0 0
10 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0
*/
どうやら、バイト配列の先頭から格納されていくようです。しかし、256単位で繰り上がるので、10を変換した場合と、257を変換した場合では、バイト配列の1つめは、前者の方がおおきくなっていますね。これだと、多分、BytesTypeの比較では上手くいかない気がします。
ためしに実験。設定ファイルを以下のように定義。
<ColumnFamily Name="BytesType" CompareWith="BytesType" />
以下がテストコード。Cassandraemonを使っています。分かりにくいかもしれないですが、通常のThriftインターフェースを使うと、とんでもなく長くなりそうだったので・・・。
public static void Main (string[] args)
{
using(var context = new CassandraContext("localhost", 9160, "Test"))
{
// 1, 10, 257 の、3カラムを生成
var list = new List<Column>();
list.Add(1, "")
.Add(10, "")
.Add(257, "");
// 登録用エンティティ作成
var entity = new CassandraEntity<List<Column>>()
.SetColumnFamily("BytesType")
.SetKey("hoge")
.SetData(list);
// 登録
context.ColumnList.InsertOnSubmit(entity);
context.SubmitChanges();
// 1以上のデータを取得
var q1 = from x in context.ColumnList
where x.ColumnFamily == "BytesType" &&
x.Key == "hoge" &&
x.Column.GreaterThanOrEqual(1)
select x;
Console.Write("条件 1以上 ");
foreach(var e in q1)
{
foreach(var column in e.Data)
{
Console.Write(column.Name.ToInt32() + " ");
}
Console.WriteLine();
}
// 9以上のデータを取得
var q2 = from x in context.ColumnList
where x.ColumnFamily == "BytesType" &&
x.Key == "hoge" &&
x.Column.GreaterThanOrEqual(9)
select x;
Console.Write("条件 9以上 ");
foreach(var e in q2)
{
foreach(var column in e.Data)
{
Console.Write(column.Name.ToInt32() + " ");
}
Console.WriteLine();
}
}
}
/*
条件 1以上 1 257 10
条件 9以上 10
*/
9以上の値を取得しようとしてるのに、257が取れてこないですね。検証は正しかったようです。
というわけで、数値タイプをカラム名にするときは、素直にLongTypeを使いましょうということですね( ̄∇  ̄ ) で、格納するときは毎回ちゃんとlongにキャストするか、そもそもModelクラスのフィールドには基本的に、long型を使うようにするのがいいと思います(あくまで.NETの場合)。