[groonga-dev,02562] Re: grn_table_selectの検索結果のハッシュテーブルからのキーの取得・削除について

Back to archive index

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/




groonga-dev メーリングリストの案内
Back to archive index