Kouhei Sutou
kou****@clear*****
2014年 7月 31日 (木) 23:28:06 JST
須藤です。 In <CANM+****@mail*****> "[groonga-dev,02549] grn_table_selectの検索結果のハッシュテーブルからのキーの取得・削除について" on Wed, 30 Jul 2014 12:17:08 +0900, Naoya Murakami <visio****@gmail*****> wrote: > ちょっとわけがわからないタイトルで恐縮なのですが、 > 現在、GroongaのC-APIでgrn_table_selectによって > 得られたハッシュテーブルのキーを取得・削除する方法 > で悩んでいます。 あぁ、これ、最初はわかんないですよねぇ。 絵を書くとすぐにわかる話なんですが、文字とコードで説明します ね。 > <質問> > grn_table_selectによって得られたテーブル型のハッシュテーブル > からキーの名前指定でキーを削除またはIDを取得する方法は > ありますか? できるんですけど、やりたいのは、たぶん、 「キーの名前指定」じゃなくて「ID指定」でやりたいんだと思いま す。 > なぜこれがしたいかというと、どの程度パフォーマンスが > でるかわからないのですが、あるテーブルの検索結果と > 他のテーブルの検索結果を積集合(AND)してみたいという > ことです。 > (2つのテーブル間のキーは部分集合の関係にしておく) この場合は「ID指定」です。 > 以下はサンプルプログラムの実行結果です。 > https://github.com/naoa/groonga-table_type_key_delete/blob/master/src/table_key_delete_sample.c > > table dump column: > key=groonga column=groonga world > key=mroonga column=mroonga world > key=rroonga column=rroonga world > filter:'column @ "world"' hits: 3 これ、元のレコードのIDと検索結果を保持するテーブルのレコード IDが同じで混乱するので↓のようにずらすようにしますね。 -- @@ -164,6 +169,7 @@ main(int argc, char **argv) table = table_create(&ctx, "data"); column = column_create(&ctx, table, "column"); + id = record_insert(&ctx, table, column, "garbage", "not matched"); id = record_insert(&ctx, table, column, "groonga", "groonga world"); id = record_insert(&ctx, table, column, "mroonga", "mroonga world"); id = record_insert(&ctx, table, column, "rroonga", "rroonga world"); -- dump_tableではidも出力するようにします。 --- a/src/table_key_delete_sample.c +++ b/src/table_key_delete_sample.c @@ -119,6 +119,7 @@ dump_column(grn_ctx *ctx, grn_obj *table, char *column_name) GRN_CURSOR_BY_ID))) { grn_id id; while ((id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) { + printf("id=%u ", id); key_size = grn_table_get_key(ctx, table, id, key_buf, GRN_TABLE_MAX_KEY_SIZE); key_buf[key_size] = '\0'; printf("key=%s ", key_buf); -- すると、この段階ではこうなります。 -- table dump column: id=1 key=garbage column=not matched id=2 key=groonga column=groonga world id=3 key=mroonga column=mroonga world id=4 key=rroonga column=rroonga world filter:'column @ "world"' hits: 3 -- > 上記のようなキーとカラムのテーブルからスクリプト構文で > 3件全てをヒットさせたresultテーブルを作ります。 > https://github.com/naoa/groonga-table_type_key_delete/blob/master/src/table_key_delete_sample.c#L187-L193 > > result dump _key: > key= column=groonga > key= column=mroonga > key= column=rroonga で、ここなんですけど、resultの_keyにはgrn_idが入っているんで す。元のコードだと key_size = grn_table_get_key(ctx, table, id, key_buf, GRN_TABLE_MAX_KEY_SIZE); key_buf[key_size] = '\0'; printf("key=%s ", key_buf); としているので、ID=2とかID=3という数値を文字列として表示しよ うとしているんですね。で、文字コード0x02とか0x03の文字は表示 できない文字なので > key= column=groonga > key= column=mroonga > key= column=rroonga というようになります。 # ちなみに、 # printf("key=%.*s ", key_size, key_buf); # とすると # key_buf[key_size] = '\0'; # としなくてもよくなります。 ということで、dump_tableを次のように変えて、 resultのとき( 一時テーブルの時 == !(table->header.flags & GRN_OBJ_PERSISTENT)のとき )は文字列ではなくgrn_idとして_keyの値を取得します。 --- a/src/table_key_delete_sample.c +++ b/src/table_key_delete_sample.c @@ -119,9 +119,16 @@ dump_column(grn_ctx *ctx, grn_obj *table, char *column_name) GRN_CURSOR_BY_ID))) { grn_id id; while ((id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) { - key_size = grn_table_get_key(ctx, table, id, key_buf, GRN_TABLE_MAX_KEY_SIZE); - key_buf[key_size] = '\0'; - printf("key=%s ", key_buf); - key_size = grn_table_get_key(ctx, table, id, key_buf, GRN_TABLE_MAX_KEY_SIZE); - key_buf[key_size] = '\0'; - printf("key=%s ", key_buf); + printf("id=%u ", id); + if (table->header.flags & GRN_OBJ_PERSISTENT) { + key_size = grn_table_get_key(ctx, table, id, key_buf, GRN_TABLE_MAX_KEY_SIZE); + key_buf[key_size] = '\0'; + printf("key=%s ", key_buf); + } else { + grn_id sub_record_id; + grn_table_get_key(ctx, table, id, &sub_record_id, sizeof(grn_id)); + printf("key=%u ", sub_record_id); + } 具体的には + grn_id sub_record_id; + grn_table_get_key(ctx, table, id, &sub_record_id, sizeof(grn_id)); + printf("key=%u ", sub_record_id); の部分ですね。 で、こうすると結果はこうなります。 -- result dump _key: id=1 key=2 column=groonga id=2 key=3 column=mroonga id=3 key=4 column=rroonga -- resultのレコードID 1には元のテーブルのレコードID 2のレコード が入っている、といった具合ですね。 > resultテーブルでは、grn_table_get_keyによるキーの取得 > がうまくできません。grn_obj_get_valueでは値が取得され > ています。 > https://github.com/naoa/groonga-table_type_key_delete/blob/master/src/table_key_delete_sample.c#L122 > > grn_table_get(&ctx, result, "groonga", strlen("groonga")); id=0 > grn_table_delete(&ctx, result, "groonga", strlen("groonga")); rc=-22 > result dump _key: > key= column=groonga > key= column=mroonga > key= column=rroonga ここでは、元のテーブルのキー(groongaとかmroongaとか)で resultのレコードを操作しようとしているのが間違いなんです。 ここは、元のテーブルのレコードIDで指定しないとダメなんです。 元のテーブルは -- table dump column: id=1 key=garbage column=not matched id=2 key=groonga column=groonga world id=3 key=mroonga column=mroonga world id=4 key=rroonga column=rroonga world -- だったので、「groongaがキーのレコード」を指定する場合はID 2 です。rroongaなら4です。 ということで、 @@ -196,11 +204,17 @@ main(int argc, char **argv) dump_column(&ctx, result, "_key"); grn_rc rc; - id = grn_table_get(&ctx, result, "groonga", strlen("groonga")); - printf("grn_table_get(&ctx, result, \"groonga\", strlen(\"groonga\")); id=%d\n",id); + { + grn_id query_id = 2; + id = grn_table_get(&ctx, result, &query_id, sizeof(grn_id)); + printf("grn_table_get(&ctx, result, \"groonga\", strlen(\"groonga\")); id=%d\n",id); + } というようにgrn_idでgrn_table_getします。この場合は元のテー ブルのレコードID 2を指定しているので、 -- id=1 key=2 column=groonga -- を取得できるはずです。resultでのレコードIDは1ですが、元のレ コードではID 2です。ここがポイントです!ついてきていますか!? なので、結果は -- grn_table_get(&ctx, result, "groonga", strlen("groonga")); id=1 -- となります。 同様に、grn_table_delete()もgrn_idで指定するのでこうです。 - rc = grn_table_delete(&ctx, result, "groonga", strlen("groonga")); - printf("grn_table_delete(&ctx, result, \"groonga\", strlen(\"groonga\")); rc=%d\n",rc); + { + grn_id id = 2; + rc = grn_table_delete(&ctx, result, &id, sizeof(grn_id)); + printf("grn_table_delete(&ctx, result, \"groonga\", strlen(\"groonga\")); rc=%d\n",rc); + } 元のテーブルのレコードID 2のレコード(resultではレコードID 1 のレコード)が消えます。 -- grn_table_delete(&ctx, result, "groonga", strlen("groonga")); rc=0 result dump _key: id=2 key=3 column=mroonga id=3 key=4 column=rroonga -- > grn_table_getとgrn_table_deleteのキーの名前指定では > resultテーブルからうまくキーを取得、削除できていません。 > https://github.com/naoa/groonga-table_type_key_delete/blob/master/src/table_key_delete_sample.c#L199-L206 > > grn_table_delete_by_id(&ctx, result, 1); rc=0 > result dump _key: > key= column=mroonga > key= column=rroonga > > grn_table_delete_by_idのID指定では、resultテーブルから > ID=1のkey=groongaのレコードが削除されています。 > https://github.com/naoa/groonga-table_type_key_delete/blob/master/src/table_key_delete_sample.c#L208-L212 これは勘違いで、resultでのレコードID 1のレコードが消えている んです。元のテーブルでレコードIDが1だったから消えたんじゃあり ません。 たまたま、元のテーブルのレコードIDとresultのレコードIDが同じ だったので勘違いしてしまったんです。冒頭でgarbageレコードを 追加したようにレコードIDがずれるとよくわかると思います。 という感じなんですが、わかりました? あ、実際に動くコードがあって助かりました! -- 須藤 功平 <kou****@clear*****> 株式会社クリアコード <http://www.clear-code.com/> Groongaベースの全文検索システムを総合サポート: http://groonga.org/ja/support/ パッチ採用 - プログラミングが楽しい人向けの採用プロセス: http://www.clear-code.com/recruitment/ コードリーダー育成支援 - 自然とリーダブルコードを書くチームへ: http://www.clear-code.com/services/code-reader/