MahoutはいろんなものをlongのIDで扱う。ので、文字列をIDに変換しないといけないシーンにけっこう出くわす。
手軽にIDと文字列を変換できる機能に、IDMigratorがある。ファイルとかDBから文字列とIDの対応表を生成してくれる機能。
変換にはハッシュ値を使用し、衝突したら仕方ないという男らしい仕様を採用している。JavaDocには「最悪、違うユーザのレコメン出しちゃうこともあるかもね」と書かれている。
でも、大丈夫。64bitでぶつかってしまうような運命の2人なら、きっと趣味も一緒のはずだから。
とりあえず手軽に使えるところで、MemoryIDMigratorを使ってみる。Collectionに入った文字列からIDへの変換表を生成してくれる。
List<String> list = Arrays.asList(
"田中", "佐藤", "鈴木", "高橋", "伊藤",
"山本", "渡辺", "中村", "小林", "加藤");
// Migratorの用意
MemoryIDMigrator migrator = new MemoryIDMigrator();
migrator.initialize(list);
// 文字列 → ID変換
long id = migrator.toLongID("田中");
System.out.println(id);
//=> -3962337966239618561
// ID → 文字列変換
String name = migrator.toStr
//=> 田中
ID→文字列の変換(toStringID)に必要な情報はMahoutのFastByIDMapで持っている。FastByIDMapはJavaのHashMapよりも少ない容量で情報が持てる子。
文字列→IDの変換(toLongID)はMapは見に行かずに、ハッシュ値を計算して返している。そのため、登録していない文字列に対してtoLongIDを実行しても普通に結果が返ってくる。
id = migrator.toLongID("藤原")
System.out.println(id);
//=> -4224957980247652293
ハッシュ値が重複した文字列を登録した場合は、後にputした方で上書きされる。
ハッシュ値自体は、java.security.MessageDigestでgetInstance("MD5")したもの使って生成している。
ファイルにIDになる文字列を改行区切りで羅列しておき、それを読み込むタイプのIDMigrator。
とりあえず下記のような感じのファイルを用意する。
田中 佐藤 鈴木 高橋 伊藤 山本 渡辺 中村 小林 加藤
あとはファイル名をコンストラクタで指定するだけ。
FileIDMigrator migrator = new FileIDMigrator(new File("data/ids.txt"));
long id = migrator.toLongID("田中");
System.out.println(id);
//=> -3962337966239618561
String name = migrator.toStringID(id);
System.out.println(name);
//=> 田中
変換の仕組み自体はMemoryIDMigratorと同じ。
名前の通り、MySQLと連携してIDを変換する。
とりあえずDBとテーブルを用意。
mysql> create database mahout; mysql> use mahout; mysql> create table table_name( id_column bigint not null primary key, value_column varchar(255) not null unique );
コードを書いてみる。
// DataSourceの用意
MysqlDataSource ds = new MysqlDataSource();
ds.setServerName("localhost");
ds.setUser("scott");
ds.setPassword("tiger");
ds.setDatabaseName("mahout");
// migratorの用意
MySQLJDBCIDMigrator migrator = new MySQLJDBCIDMigrator(ds,
"table_name", "id_column", "value_column");
// とりあえずsotreしてみる
migrator.storeMapping(migrator.toLongID("田中"), "田中");
migrator.storeMapping(migrator.toLongID("佐藤"), "佐藤");
migrator.storeMapping(migrator.toLongID("鈴木"), "鈴木");
// IDから名前を取ってみる
String name = migrator.toStringID(migrator.toLongID("田中"));
System.out.println(name);
//=> 田中
ちゃんと取れた。
上記コマンドを実行した後のテーブルの中身。
mysql> select * from table_name; +----------------------+--------------+ | id_column | value_column | +----------------------+--------------+ | -3962337966239618561 | 田中 | | -8429178488470665793 | 佐藤 | | -245016868995516317 | 鈴木 | +----------------------+--------------+
ちゃんと入っとるね。