[Groonga-commit] groonga/groonga [master] Add snippet() function

Back to archive index

Kouhei Sutou null+****@clear*****
Wed Nov 7 14:53:23 JST 2012


Kouhei Sutou	2012-11-07 14:53:23 +0900 (Wed, 07 Nov 2012)

  New Revision: cb87daef9c8b4bbc283dc0fa29916f79b01ddbb8
  https://github.com/groonga/groonga/commit/cb87daef9c8b4bbc283dc0fa29916f79b01ddbb8

  Log:
    Add snippet() function
    
    It is experimental!

  Added files:
    test/command/suite/select/function/snippet/more.expected
    test/command/suite/select/function/snippet/more.test
    test/command/suite/select/function/snippet/one.expected
    test/command/suite/select/function/snippet/one.test
  Modified files:
    lib/proc.c

  Modified: lib/proc.c (+97 -6)
===================================================================
--- lib/proc.c    2012-11-07 14:49:09 +0900 (5b147dd)
+++ lib/proc.c    2012-11-07 14:53:23 +0900 (e5df63e)
@@ -48,6 +48,9 @@ const char *grn_document_root = NULL;
 
 #define VAR GRN_PROC_GET_VAR_BY_OFFSET
 
+#define GRN_SELECT_INTERNAL_VAR_CONDITION     "$condition"
+#define GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS "$match_columns"
+
 /* bulk must be initialized grn_bulk or grn_msg */
 static int
 grn_bulk_put_from_file(grn_ctx *ctx, grn_obj *bulk, const char *path)
@@ -450,7 +453,8 @@ grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
            const char *cache, unsigned int cache_len,
            const char *match_escalation_threshold, unsigned int match_escalation_threshold_len,
            const char *query_expansion, unsigned int query_expansion_len,
-           const char *query_flags, unsigned int query_flags_len)
+           const char *query_flags, unsigned int query_flags_len,
+           grn_obj *condition_ptr, grn_obj *match_columns_ptr)
 {
   uint32_t nkeys, nhits;
   uint16_t cacheable = 1, taintable = 0;
@@ -525,9 +529,11 @@ grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
       grn_obj *v;
       GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, cond, v);
       if (cond) {
+        GRN_PTR_SET(ctx, condition_ptr, cond);
         if (match_columns_len) {
           GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, match_columns_, v);
           if (match_columns_) {
+            GRN_PTR_SET(ctx, match_columns_ptr, match_columns_);
             grn_expr_parse(ctx, match_columns_, match_columns, match_columns_len,
                            NULL, GRN_OP_MATCH, GRN_OP_AND,
                            GRN_EXPR_SYNTAX_SCRIPT);
@@ -545,7 +551,6 @@ grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
           } else {
             flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
             if (ctx->rc) {
-              grn_obj_unlink(ctx, cond);
               goto exit;
             }
           }
@@ -556,7 +561,6 @@ grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
               query = GRN_TEXT_VALUE(&query_expansion_buf);
               query_len = GRN_TEXT_LEN(&query_expansion_buf);
             } else {
-              grn_obj_unlink(ctx, cond);
               GRN_OBJ_FIN(ctx, &query_expansion_buf);
               goto exit;
             }
@@ -586,8 +590,6 @@ grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
         GRN_OBJ_FIN(ctx, &strbuf);
         */
         if (!ctx->rc) { res = grn_table_select(ctx, table_, cond, NULL, GRN_OP_OR); }
-        if (match_columns_) { grn_obj_unlink(ctx, match_columns_); }
-        grn_obj_unlink(ctx, cond);
       } else {
         /* todo */
         ERRCLR(ctx);
@@ -785,6 +787,7 @@ proc_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
   int drilldown_limit = GRN_TEXT_LEN(VAR(13))
     ? grn_atoi(GRN_TEXT_VALUE(VAR(13)), GRN_BULK_CURR(VAR(13)), NULL)
     : DEFAULT_DRILLDOWN_LIMIT;
+  grn_obj *condition_ptr, *match_columns_ptr;
   if (!output_columns_len) {
     output_columns = DEFAULT_OUTPUT_COLUMNS;
     output_columns_len = strlen(DEFAULT_OUTPUT_COLUMNS);
@@ -793,6 +796,10 @@ proc_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
     drilldown_output_columns = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS;
     drilldown_output_columns_len = strlen(DEFAULT_DRILLDOWN_OUTPUT_COLUMNS);
   }
+  condition_ptr = GRN_PROC_GET_OR_ADD_VAR(GRN_SELECT_INTERNAL_VAR_CONDITION);
+  GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT);
+  match_columns_ptr = GRN_PROC_GET_OR_ADD_VAR(GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS);
+  GRN_PTR_INIT(match_columns_ptr, 0, GRN_DB_OBJECT);
   if (grn_select(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
                  GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
                  GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)),
@@ -808,7 +815,16 @@ proc_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
                  GRN_TEXT_VALUE(VAR(14)), GRN_TEXT_LEN(VAR(14)),
                  GRN_TEXT_VALUE(VAR(15)), GRN_TEXT_LEN(VAR(15)),
                  GRN_TEXT_VALUE(VAR(16)), GRN_TEXT_LEN(VAR(16)),
-                 GRN_TEXT_VALUE(VAR(17)), GRN_TEXT_LEN(VAR(17)))) {
+                 GRN_TEXT_VALUE(VAR(17)), GRN_TEXT_LEN(VAR(17)),
+                 condition_ptr, match_columns_ptr)) {
+  }
+  if (GRN_BULK_VSIZE(match_columns_ptr) > 0) {
+    grn_obj *match_columns = GRN_PTR_VALUE(match_columns_ptr);
+    grn_obj_unlink(ctx, match_columns);
+  }
+  if (GRN_BULK_VSIZE(condition_ptr) > 0) {
+    grn_obj *condition = GRN_PTR_VALUE(condition_ptr);
+    grn_obj_unlink(ctx, condition);
   }
   return NULL;
 }
@@ -3223,6 +3239,77 @@ selector_all_records(grn_ctx *ctx, grn_obj *table, grn_obj *index,
   return ctx->rc;
 }
 
+static grn_obj *
+func_snippet(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+  grn_obj *snippets;
+
+  snippets = GRN_PROC_ALLOC(GRN_DB_SHORT_TEXT, GRN_OBJ_VECTOR);
+  if (!snippets) {
+    return NULL;
+  }
+
+  /* TODO: support parameters */
+  if (nargs == 1) {
+    grn_obj *text = args[0];
+    grn_obj *command = ctx->impl->curr_expr;
+    grn_obj *condition_ptr = NULL;
+    grn_obj *condition = NULL;
+    grn_snip *snip;
+    int flags = GRN_SNIP_NORMALIZE | GRN_SNIP_SKIP_LEADING_SPACES;
+    unsigned int width = 200;
+    unsigned int max_n_results = 3;
+    unsigned int n_tags = 1;
+    const char *open_tags[] = {"<span class=\"keyword\">"};
+    unsigned int open_tag_lengths[1];
+    const char *close_tags[] = {"</span>"};
+    unsigned int close_tag_lengths[1];
+    grn_snip_mapping *mapping = GRN_SNIP_MAPPING_HTML_ESCAPE;
+
+    open_tag_lengths[0] = strlen(open_tags[0]);
+    close_tag_lengths[0] = strlen(close_tags[0]);
+
+    condition_ptr = grn_expr_get_var(ctx, command,
+                                     GRN_SELECT_INTERNAL_VAR_CONDITION,
+                                     strlen(GRN_SELECT_INTERNAL_VAR_CONDITION));
+    if (condition_ptr) {
+      condition = GRN_PTR_VALUE(condition_ptr);
+    }
+
+    snip = grn_expr_snip(ctx, condition, flags,
+                         width, max_n_results, n_tags,
+                         open_tags, open_tag_lengths,
+                         close_tags, close_tag_lengths,
+                         mapping);
+    if (snip) {
+      grn_rc rc;
+      unsigned int i, n_results, max_tagged_length;
+      grn_obj snippet_buffer;
+
+      rc = grn_snip_exec(ctx, snip,
+                         GRN_TEXT_VALUE(text), GRN_TEXT_LEN(text),
+                         &n_results, &max_tagged_length);
+      GRN_TEXT_INIT(&snippet_buffer, 0);
+      grn_bulk_space(ctx, &snippet_buffer, max_tagged_length);
+      for (i = 0; i < n_results; i++) {
+        unsigned int snippet_length;
+
+        GRN_BULK_REWIND(&snippet_buffer);
+        rc = grn_snip_get_result(ctx, snip, i,
+                                 GRN_TEXT_VALUE(&snippet_buffer),
+                                 &snippet_length);
+        grn_vector_add_element(ctx, snippets,
+                               GRN_TEXT_VALUE(&snippet_buffer), snippet_length,
+                               0, GRN_DB_SHORT_TEXT);
+      }
+      GRN_OBJ_FIN(ctx, &snippet_buffer);
+      grn_snip_close(ctx, snip);
+    }
+  }
+
+  return snippets;
+}
+
 
 #define DEF_VAR(v,name_str) do {\
   (v).name = (name_str);\
@@ -3400,4 +3487,8 @@ grn_db_init_builtin_query(grn_ctx *ctx)
                                     func_all_records, NULL, NULL, 0, NULL);
     grn_proc_set_selector(ctx, selector_proc, selector_all_records);
   }
+
+  /* experimental */
+  grn_proc_create(ctx, "snippet", 7, GRN_PROC_FUNCTION,
+                  func_snippet, NULL, NULL, 0, NULL);
 }

  Added: test/command/suite/select/function/snippet/more.expected (+108 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/function/snippet/more.expected    2012-11-07 14:53:23 +0900 (df59854)
@@ -0,0 +1,108 @@
+table_create Documents TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+column_create Documents content COLUMN_SCALAR Text
+[[0,0.0,0.0],true]
+table_create Terms TABLE_PAT_KEY|KEY_NORMALIZE ShortText --default_tokenizer TokenBigram
+[[0,0.0,0.0],true]
+column_create Terms document_index COLUMN_INDEX|WITH_POSITION Documents content
+[[0,0.0,0.0],true]
+load --table Documents
+[
+["_key", "content"],
+["groonga の概要", "groonga は転置索引を用いた高速・高精度な全文検索エンジンであり、登録された文書をすぐに検索結果に反映できます。また、参照をブロックせずに更新できることから、即時更新の必要なアプリケーションにおいても高い性能を発揮します。\n\n全文検索エンジンとして開発された groonga ですが、独自のカラムストアを持つ列指向のデータベースとしての側面も持っています。そのため、MySQL ã‚„ PostgreSQL など、既存の代表的なデータベースが苦手とする集計クエリを高速に処理できるという特徴があり、組み合わせによって弱点を補うような使い方もできます。\n\ngroonga の基本機能は C ライブラリとして提供されていますが、MySQL ã‚„ PostgreSQL と連携させたり、Ruby から呼び出したりすることもできます。そのため、任æ
 „ã®ã‚¢ãƒ—リケーションに組み込むことが可能であり、多様な使い方が考えられます。 興味のある方は 利用例 をご覧ください。"],
+["全文検索と即時更新", "一般的なデータベースにおいては、追加・削除などの操作がすぐに反映されます。一方、全文検索においては、転置索引が逐次更新の難しいデータ構造であることから、文書の追加・削除に対応しないエンジンが少なくありません。\n\nこれに対し、転置索引を用いた全文検索エンジンでありながら、groonga は文書を短時間で追加・削除することができます。その上、更新しながらでも検索できるという優れた特徴を持っているため、全文検索エンジンとしてはとても柔軟性があります。また、複数の転置索引を統合するような重い処理を必要としないので、安定して高い性能を発揮することが期待できます。"],
+["カラムストアと集計クエリ", "現代は、インターネットを情報源とすれば、いくらでも情報を収集できる時代です。しかし、膨大な情報から有益な情報を引き出すのは困難であり、多面的な分析による試行錯誤が必要となります。たとえば、日付や時間帯により絞り込んでみたり、地域により絞り込んでみたり、性別や年齢により絞り込んでみたりすることでしょう。そして、そのようなときに便利な存在が集計クエリです。\n\n集計クエリとは、指定したカラムの値によってレコードをグループ化し、各グループに含まれるレコードの数を求めるクエリです。たとえば、地域の ID を格納しているカラムを指定すれば、地域毎のレコード数が求まります。日付のカラムを指定したときの出力をグラフ化すれば、レコード数の時間変化を
 視覚化することができます。さらに、地域による絞り込みと日付に対する集計クエリを組み合わせれば、特定の地域におけるレコード数の時間変化を視覚化ことも可能です。このように、尺度を自由に選択して絞り込み・集計できることは、膨大な情報を扱う上でとても重要になります。\n\ngroonga が集計クエリを高速に処理できる理由は、データベースの論理構造にカラムストアを採用しているからです。集計クエリが参照するのは指定されたカラムのみであるため、カラム単位でデータを格納する列指向のデータベースでは、必要なカラムのみを無駄なく読み出せることが利点となります。一方、レコード単位でデータを格納する行指向のデータベースでは、隣接するカラムをまとめて読み出してしまうことが欠点となります。"],
+["転置索引とトークナイザ", "転置索引は大規模な全文検索に用いられる伝統的なデータ構造です。転置索引を用いた全文検索エンジンでは、文書を追加するときに索引語を記録しておき、検索するときはクエリを索引語に分割して出現文書を求めます。そのため、文書やクエリから索引語を抜き出す方法が重要になります。\n\nトークナイザは、文字列から索引語を抜き出すモジュールです。日本語を対象とする全文検索においては、形態素を索引語として抜き出す方式と文字 N-gram を抜き出す方式のいずれか、あるいは両方を用いるのが一般的です。形態素方式は検索時間や索引サイズの面で優れているほか、検索結果に不要な文書が含まれにくいという利点を持っています。一方、N-gram 方式には検索漏れが発生しにくいという利点ã
 Œã‚り、状況によって適した方式を選択することが望ましいとされています。\n\ngroonga は形態素方式と N-gram 方式の両方に対応しています。初期状態で利用できるトークナイザは空白を区切り文字として用いる方式と N-gram 方式のみですが、形態素解析器 MeCab を組み込んだときは MeCab による分かち書きの結果を用いる形態素方式が有効になります。トークナイザはプラグインとして追加できるため、特徴的なキーワードのみを索引語として採用するなど、独自のトークナイザを開発することが可能です。"],
+["共有可能なストレージと参照ロックフリー", "CPU のマルチコア化が進んでいるため、同時に複数のクエリを実行したり、一つのクエリを複数のスレッドで実行したりすることの重要性はますます高まっています。\n\ngroonga のストレージは、複数のスレッド・プロセスで共有することができます。また、参照ロックフリーなデータ構造を採用しているため、更新クエリを実行している状況でも参照クエリを実行することができます。参照クエリを実行できる状態を維持しながら更新クエリを実行できるので、リアルタイムなシステムに適しています。さらには、MySQL を介して更新クエリを実行している最中に groonga の HTTP サーバを介して参照クエリを実行するなど、多彩な運用が可能となっています。"],
+["位置情報(緯度・経度)検索", "GPS に代表される測位システムを搭載した高機能な携帯端末の普及などによって、位置情報を扱うサービスはますます便利になっています。たとえば、近くにあるレストランを探しているときは、現在地からの距離を基準として検索をおこない、検索結果を地図上に表示してくれるようなサービスが便利です。そのため、位置情報検索を高速に実現できることが重要になっています。\n\ngroonga では転置索引を応用して高速な位置情報検索を実現しています。矩形・円による範囲検索に対応しているほか、基準点の近くを優先的に探索させることができます。また、距離計算をサポートしているので、位置情報検索の結果を基準点からの距離によって整列することも可能です。"],
+["groonga ライブラリ", "Groonga の基本機能は C ライブラリとして提供されているので、任意のアプリケーションに組み込んで利用することができます。C/C++ 以外については、Ruby から groonga を利用するライブラリなどが関連プロジェクトにおいて提供されています。詳しくは 関連プロジェクト を参照してください。"],
+["groonga サーバ", "groonga にはサーバ機能があるため、レンタルサーバなどの新しいライブラリをインストールできない環境においても利用できます。対応しているのは HTTP, memcached binary プロトコル、およびに groonga の独自プロトコルである gqtp です。サーバとして利用するときはクエリのキャッシュ機能が有効になるため、同じクエリを受け取ったときは応答時間が短くなるという特徴があります。"],
+["groonga ストレージエンジン", "groonga は独自のカラムストアを持つ列指向のデータベースとしての側面を持っていますが、既存の RDBMS のストレージエンジンとして利用することもできます。たとえば、groonga をベースとする MySQL のストレージエンジンとして mroonga が開発されています。mroonga は MySQL のプラグインとして動的にロードすることが可能であり、groonga のカラムストアをストレージとして利用したり、全文検索エンジンとして groonga を MyISAM や InnoDB と連携させたりすることができます。groonga 単体での利用、およびに MyISAM, InnoDB との連携には一長一短があるので、用途に応じて適切な組み合わせを選ぶことが大切です。詳しくは 関連プロジェクト を参照してください。"]
+]
+[[0,0.0,0.0],9]
+select Documents   --match_columns content --query 'groonga'   --output_columns '_key, snippet(content)'   --command_version 2
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        9
+      ],
+      [
+        [
+          "_key",
+          "ShortText"
+        ],
+        [
+          "snippet",
+          "null"
+        ]
+      ],
+      [
+        "groonga の概要",
+        [
+          "<span class=\"keyword\">groonga</span> は転置索引を用いた高速・高精度な全文検索エンジンであり、登録された文書をすぐに検索結果に反映できます。また、参照をブロックせずに",
+          "おいても高い性能を発揮します。\n\n全文検索エンジンとして開発された <span class=\"keyword\">groonga</span> ですが、独自のカラムストアを持つ列指向のデータベースとしての側面",
+          "徴があり、組み合わせによって弱点を補うような使い方もできます。\n\n<span class=\"keyword\">groonga</span> の基本機能は C ライブラリとして提供されていますが、MySQL や PostgreSQL "
+        ]
+      ],
+      [
+        "全文検索と即時更新",
+        [
+          "ん。\n\nこれに対し、転置索引を用いた全文検索エンジンでありながら、<span class=\"keyword\">groonga</span> は文書を短時間で追加・削除することができます。その上、更新しなが"
+        ]
+      ],
+      [
+        "カラムストアと集計クエリ",
+        [
+          "・集計できることは、膨大な情報を扱う上でとても重要になります。\n\n<span class=\"keyword\">groonga</span> が集計クエリを高速に処理できる理由は、データベースの論理構造にカ"
+        ]
+      ],
+      [
+        "転置索引とトークナイザ",
+        [
+          "状況によって適した方式を選択することが望ましいとされています。\n\n<span class=\"keyword\">groonga</span> は形態素方式と N-gram 方式の両方に対応しています。初期状態で利用で"
+        ]
+      ],
+      [
+        "共有可能なストレージと参照ロックフリー",
+        [
+          "スレッドで実行したりすることの重要性はますます高まっています。\n\n<span class=\"keyword\">groonga</span> のストレージは、複数のスレッド・プロセスで共有することができます",
+          "しています。さらには、MySQL を介して更新クエリを実行している最中に <span class=\"keyword\">groonga</span> の HTTP サーバを介して参照クエリを実行するなど、多彩な運用が可能"
+        ]
+      ],
+      [
+        "位置情報(緯度・経度)検索",
+        [
+          "ため、位置情報検索を高速に実現できることが重要になっています。\n\n<span class=\"keyword\">groonga</span> では転置索引を応用して高速な位置情報検索を実現しています。矩形・"
+        ]
+      ],
+      [
+        "groonga ライブラリ",
+        [
+          "<span class=\"keyword\">Groonga</span> の基本機能は C ライブラリとして提供されているので、任意のアプリケーションに組み込んで利用することができます。C/C++ 以外については、",
+          "Ruby から <span class=\"keyword\">groonga</span> を利用するライブラリなどが関連プロジェクトにおいて提供されています。詳しくは 関連プロジェクト を参照してください。"
+        ]
+      ],
+      [
+        "groonga サーバ",
+        [
+          "<span class=\"keyword\">groonga</span> にはサーバ機能があるため、レンタルサーバなどの新しいライブラリをインストールできない環境においても利用できます。対応しているのは",
+          " HTTP, memcached binary プロトコル、およびに <span class=\"keyword\">groonga</span> の独自プロトコルである gqtp です。サーバとして利用するときはクエリのキャッシュ機能が有効にな"
+        ]
+      ],
+      [
+        "groonga ストレージエンジン",
+        [
+          "<span class=\"keyword\">groonga</span> は独自のカラムストアを持つ列指向のデータベースとしての側面を持っていますが、既存の RDBMS のストレージエンジンとして利用することも",
+          "できます。たとえば、<span class=\"keyword\">groonga</span> をベースとする MySQL のストレージエンジンとして mroonga が開発されています。mroonga は MySQL のプラグインとして動的に",
+          "<span class=\"keyword\">groonga</span> のカラムストアをストレージとして利用したり、全文検索エンジンとして <span class=\"keyword\">groonga</span> を MyISAM や InnoDB と連携させたりすることができます。<span class=\"keyword\">groonga</span> 単"
+        ]
+      ]
+    ]
+  ]
+]

  Added: test/command/suite/select/function/snippet/more.test (+24 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/function/snippet/more.test    2012-11-07 14:53:23 +0900 (e986183)
@@ -0,0 +1,24 @@
+table_create Documents TABLE_HASH_KEY ShortText
+column_create Documents content COLUMN_SCALAR Text
+
+table_create Terms TABLE_PAT_KEY|KEY_NORMALIZE ShortText --default_tokenizer TokenBigram
+column_create Terms document_index COLUMN_INDEX|WITH_POSITION Documents content
+
+load --table Documents
+[
+["_key", "content"],
+["groonga の概要", "groonga は転置索引を用いた高速・高精度な全文検索エンジンであり、登録された文書をすぐに検索結果に反映できます。また、参照をブロックせずに更新できることから、即時更新の必要なアプリケーションにおいても高い性能を発揮します。\n\n全文検索エンジンとして開発された groonga ですが、独自のカラムストアを持つ列指向のデータベースとしての側面も持っています。そのため、MySQL ã‚„ PostgreSQL など、既存の代表的なデータベースが苦手とする集計クエリを高速に処理できるという特徴があり、組み合わせによって弱点を補うような使い方もできます。\n\ngroonga の基本機能は C ライブラリとして提供されていますが、MySQL ã‚„ PostgreSQL と連携させたり、Ruby から呼び出したりすることもできます。そのため、任æ
 „ã®ã‚¢ãƒ—リケーションに組み込むことが可能であり、多様な使い方が考えられます。 興味のある方は 利用例 をご覧ください。"],
+["全文検索と即時更新", "一般的なデータベースにおいては、追加・削除などの操作がすぐに反映されます。一方、全文検索においては、転置索引が逐次更新の難しいデータ構造であることから、文書の追加・削除に対応しないエンジンが少なくありません。\n\nこれに対し、転置索引を用いた全文検索エンジンでありながら、groonga は文書を短時間で追加・削除することができます。その上、更新しながらでも検索できるという優れた特徴を持っているため、全文検索エンジンとしてはとても柔軟性があります。また、複数の転置索引を統合するような重い処理を必要としないので、安定して高い性能を発揮することが期待できます。"],
+["カラムストアと集計クエリ", "現代は、インターネットを情報源とすれば、いくらでも情報を収集できる時代です。しかし、膨大な情報から有益な情報を引き出すのは困難であり、多面的な分析による試行錯誤が必要となります。たとえば、日付や時間帯により絞り込んでみたり、地域により絞り込んでみたり、性別や年齢により絞り込んでみたりすることでしょう。そして、そのようなときに便利な存在が集計クエリです。\n\n集計クエリとは、指定したカラムの値によってレコードをグループ化し、各グループに含まれるレコードの数を求めるクエリです。たとえば、地域の ID を格納しているカラムを指定すれば、地域毎のレコード数が求まります。日付のカラムを指定したときの出力をグラフ化すれば、レコード数の時間変化を
 視覚化することができます。さらに、地域による絞り込みと日付に対する集計クエリを組み合わせれば、特定の地域におけるレコード数の時間変化を視覚化ことも可能です。このように、尺度を自由に選択して絞り込み・集計できることは、膨大な情報を扱う上でとても重要になります。\n\ngroonga が集計クエリを高速に処理できる理由は、データベースの論理構造にカラムストアを採用しているからです。集計クエリが参照するのは指定されたカラムのみであるため、カラム単位でデータを格納する列指向のデータベースでは、必要なカラムのみを無駄なく読み出せることが利点となります。一方、レコード単位でデータを格納する行指向のデータベースでは、隣接するカラムをまとめて読み出してしまうことが欠点となります。"],
+["転置索引とトークナイザ", "転置索引は大規模な全文検索に用いられる伝統的なデータ構造です。転置索引を用いた全文検索エンジンでは、文書を追加するときに索引語を記録しておき、検索するときはクエリを索引語に分割して出現文書を求めます。そのため、文書やクエリから索引語を抜き出す方法が重要になります。\n\nトークナイザは、文字列から索引語を抜き出すモジュールです。日本語を対象とする全文検索においては、形態素を索引語として抜き出す方式と文字 N-gram を抜き出す方式のいずれか、あるいは両方を用いるのが一般的です。形態素方式は検索時間や索引サイズの面で優れているほか、検索結果に不要な文書が含まれにくいという利点を持っています。一方、N-gram 方式には検索漏れが発生しにくいという利点ã
 Œã‚り、状況によって適した方式を選択することが望ましいとされています。\n\ngroonga は形態素方式と N-gram 方式の両方に対応しています。初期状態で利用できるトークナイザは空白を区切り文字として用いる方式と N-gram 方式のみですが、形態素解析器 MeCab を組み込んだときは MeCab による分かち書きの結果を用いる形態素方式が有効になります。トークナイザはプラグインとして追加できるため、特徴的なキーワードのみを索引語として採用するなど、独自のトークナイザを開発することが可能です。"],
+["共有可能なストレージと参照ロックフリー", "CPU のマルチコア化が進んでいるため、同時に複数のクエリを実行したり、一つのクエリを複数のスレッドで実行したりすることの重要性はますます高まっています。\n\ngroonga のストレージは、複数のスレッド・プロセスで共有することができます。また、参照ロックフリーなデータ構造を採用しているため、更新クエリを実行している状況でも参照クエリを実行することができます。参照クエリを実行できる状態を維持しながら更新クエリを実行できるので、リアルタイムなシステムに適しています。さらには、MySQL を介して更新クエリを実行している最中に groonga の HTTP サーバを介して参照クエリを実行するなど、多彩な運用が可能となっています。"],
+["位置情報(緯度・経度)検索", "GPS に代表される測位システムを搭載した高機能な携帯端末の普及などによって、位置情報を扱うサービスはますます便利になっています。たとえば、近くにあるレストランを探しているときは、現在地からの距離を基準として検索をおこない、検索結果を地図上に表示してくれるようなサービスが便利です。そのため、位置情報検索を高速に実現できることが重要になっています。\n\ngroonga では転置索引を応用して高速な位置情報検索を実現しています。矩形・円による範囲検索に対応しているほか、基準点の近くを優先的に探索させることができます。また、距離計算をサポートしているので、位置情報検索の結果を基準点からの距離によって整列することも可能です。"],
+["groonga ライブラリ", "Groonga の基本機能は C ライブラリとして提供されているので、任意のアプリケーションに組み込んで利用することができます。C/C++ 以外については、Ruby から groonga を利用するライブラリなどが関連プロジェクトにおいて提供されています。詳しくは 関連プロジェクト を参照してください。"],
+["groonga サーバ", "groonga にはサーバ機能があるため、レンタルサーバなどの新しいライブラリをインストールできない環境においても利用できます。対応しているのは HTTP, memcached binary プロトコル、およびに groonga の独自プロトコルである gqtp です。サーバとして利用するときはクエリのキャッシュ機能が有効になるため、同じクエリを受け取ったときは応答時間が短くなるという特徴があります。"],
+["groonga ストレージエンジン", "groonga は独自のカラムストアを持つ列指向のデータベースとしての側面を持っていますが、既存の RDBMS のストレージエンジンとして利用することもできます。たとえば、groonga をベースとする MySQL のストレージエンジンとして mroonga が開発されています。mroonga は MySQL のプラグインとして動的にロードすることが可能であり、groonga のカラムストアをストレージとして利用したり、全文検索エンジンとして groonga を MyISAM や InnoDB と連携させたりすることができます。groonga 単体での利用、およびに MyISAM, InnoDB との連携には一長一短があるので、用途に応じて適切な組み合わせを選ぶことが大切です。詳しくは 関連プロジェクト を参照してください。"]
+]
+
+select Documents \
+  --match_columns content --query 'groonga' \
+  --output_columns '_key, snippet(content)' \
+  --command_version 2

  Added: test/command/suite/select/function/snippet/one.expected (+53 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/function/snippet/one.expected    2012-11-07 14:53:23 +0900 (51068eb)
@@ -0,0 +1,53 @@
+table_create Documents TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+column_create Documents content COLUMN_SCALAR Text
+[[0,0.0,0.0],true]
+table_create Terms TABLE_PAT_KEY|KEY_NORMALIZE ShortText --default_tokenizer TokenBigram
+[[0,0.0,0.0],true]
+column_create Terms document_index COLUMN_INDEX|WITH_POSITION Documents content
+[[0,0.0,0.0],true]
+load --table Documents
+[
+["_key", "content"],
+["groonga の概要", "groonga は転置索引を用いた高速・高精度な全文検索エンジンであり、登録された文書をすぐに検索結果に反映できます。また、参照をブロックせずに更新できることから、即時更新の必要なアプリケーションにおいても高い性能を発揮します。\n\n全文検索エンジンとして開発された groonga ですが、独自のカラムストアを持つ列指向のデータベースとしての側面も持っています。そのため、MySQL ã‚„ PostgreSQL など、既存の代表的なデータベースが苦手とする集計クエリを高速に処理できるという特徴があり、組み合わせによって弱点を補うような使い方もできます。\n\ngroonga の基本機能は C ライブラリとして提供されていますが、MySQL ã‚„ PostgreSQL と連携させたり、Ruby から呼び出したりすることもできます。そのため、任æ
 „ã®ã‚¢ãƒ—リケーションに組み込むことが可能であり、多様な使い方が考えられます。 興味のある方は 利用例 をご覧ください。"],
+["全文検索と即時更新", "一般的なデータベースにおいては、追加・削除などの操作がすぐに反映されます。一方、全文検索においては、転置索引が逐次更新の難しいデータ構造であることから、文書の追加・削除に対応しないエンジンが少なくありません。\n\nこれに対し、転置索引を用いた全文検索エンジンでありながら、groonga は文書を短時間で追加・削除することができます。その上、更新しながらでも検索できるという優れた特徴を持っているため、全文検索エンジンとしてはとても柔軟性があります。また、複数の転置索引を統合するような重い処理を必要としないので、安定して高い性能を発揮することが期待できます。"],
+["カラムストアと集計クエリ", "現代は、インターネットを情報源とすれば、いくらでも情報を収集できる時代です。しかし、膨大な情報から有益な情報を引き出すのは困難であり、多面的な分析による試行錯誤が必要となります。たとえば、日付や時間帯により絞り込んでみたり、地域により絞り込んでみたり、性別や年齢により絞り込んでみたりすることでしょう。そして、そのようなときに便利な存在が集計クエリです。\n\n集計クエリとは、指定したカラムの値によってレコードをグループ化し、各グループに含まれるレコードの数を求めるクエリです。たとえば、地域の ID を格納しているカラムを指定すれば、地域毎のレコード数が求まります。日付のカラムを指定したときの出力をグラフ化すれば、レコード数の時間変化を
 視覚化することができます。さらに、地域による絞り込みと日付に対する集計クエリを組み合わせれば、特定の地域におけるレコード数の時間変化を視覚化ことも可能です。このように、尺度を自由に選択して絞り込み・集計できることは、膨大な情報を扱う上でとても重要になります。\n\ngroonga が集計クエリを高速に処理できる理由は、データベースの論理構造にカラムストアを採用しているからです。集計クエリが参照するのは指定されたカラムのみであるため、カラム単位でデータを格納する列指向のデータベースでは、必要なカラムのみを無駄なく読み出せることが利点となります。一方、レコード単位でデータを格納する行指向のデータベースでは、隣接するカラムをまとめて読み出してしまうことが欠点となります。"],
+["転置索引とトークナイザ", "転置索引は大規模な全文検索に用いられる伝統的なデータ構造です。転置索引を用いた全文検索エンジンでは、文書を追加するときに索引語を記録しておき、検索するときはクエリを索引語に分割して出現文書を求めます。そのため、文書やクエリから索引語を抜き出す方法が重要になります。\n\nトークナイザは、文字列から索引語を抜き出すモジュールです。日本語を対象とする全文検索においては、形態素を索引語として抜き出す方式と文字 N-gram を抜き出す方式のいずれか、あるいは両方を用いるのが一般的です。形態素方式は検索時間や索引サイズの面で優れているほか、検索結果に不要な文書が含まれにくいという利点を持っています。一方、N-gram 方式には検索漏れが発生しにくいという利点ã
 Œã‚り、状況によって適した方式を選択することが望ましいとされています。\n\ngroonga は形態素方式と N-gram 方式の両方に対応しています。初期状態で利用できるトークナイザは空白を区切り文字として用いる方式と N-gram 方式のみですが、形態素解析器 MeCab を組み込んだときは MeCab による分かち書きの結果を用いる形態素方式が有効になります。トークナイザはプラグインとして追加できるため、特徴的なキーワードのみを索引語として採用するなど、独自のトークナイザを開発することが可能です。"],
+["共有可能なストレージと参照ロックフリー", "CPU のマルチコア化が進んでいるため、同時に複数のクエリを実行したり、一つのクエリを複数のスレッドで実行したりすることの重要性はますます高まっています。\n\ngroonga のストレージは、複数のスレッド・プロセスで共有することができます。また、参照ロックフリーなデータ構造を採用しているため、更新クエリを実行している状況でも参照クエリを実行することができます。参照クエリを実行できる状態を維持しながら更新クエリを実行できるので、リアルタイムなシステムに適しています。さらには、MySQL を介して更新クエリを実行している最中に groonga の HTTP サーバを介して参照クエリを実行するなど、多彩な運用が可能となっています。"],
+["位置情報(緯度・経度)検索", "GPS に代表される測位システムを搭載した高機能な携帯端末の普及などによって、位置情報を扱うサービスはますます便利になっています。たとえば、近くにあるレストランを探しているときは、現在地からの距離を基準として検索をおこない、検索結果を地図上に表示してくれるようなサービスが便利です。そのため、位置情報検索を高速に実現できることが重要になっています。\n\ngroonga では転置索引を応用して高速な位置情報検索を実現しています。矩形・円による範囲検索に対応しているほか、基準点の近くを優先的に探索させることができます。また、距離計算をサポートしているので、位置情報検索の結果を基準点からの距離によって整列することも可能です。"],
+["groonga ライブラリ", "Groonga の基本機能は C ライブラリとして提供されているので、任意のアプリケーションに組み込んで利用することができます。C/C++ 以外については、Ruby から groonga を利用するライブラリなどが関連プロジェクトにおいて提供されています。詳しくは 関連プロジェクト を参照してください。"],
+["groonga サーバ", "groonga にはサーバ機能があるため、レンタルサーバなどの新しいライブラリをインストールできない環境においても利用できます。対応しているのは HTTP, memcached binary プロトコル、およびに groonga の独自プロトコルである gqtp です。サーバとして利用するときはクエリのキャッシュ機能が有効になるため、同じクエリを受け取ったときは応答時間が短くなるという特徴があります。"],
+["groonga ストレージエンジン", "groonga は独自のカラムストアを持つ列指向のデータベースとしての側面を持っていますが、既存の RDBMS のストレージエンジンとして利用することもできます。たとえば、groonga をベースとする MySQL のストレージエンジンとして mroonga が開発されています。mroonga は MySQL のプラグインとして動的にロードすることが可能であり、groonga のカラムストアをストレージとして利用したり、全文検索エンジンとして groonga を MyISAM や InnoDB と連携させたりすることができます。groonga 単体での利用、およびに MyISAM, InnoDB との連携には一長一短があるので、用途に応じて適切な組み合わせを選ぶことが大切です。詳しくは 関連プロジェクト を参照してください。"]
+]
+[[0,0.0,0.0],9]
+select Documents   --match_columns content --query 'mroonga MySQL'   --output_columns '_key, snippet(content)'   --command_version 2
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        1
+      ],
+      [
+        [
+          "_key",
+          "ShortText"
+        ],
+        [
+          "snippet",
+          "null"
+        ]
+      ],
+      [
+        "groonga ストレージエンジン",
+        [
+          "。たとえば、groonga をベースとする <span class=\"keyword\">MySQL</span> のストレージエンジンとして <span class=\"keyword\">mroonga</span> が開発されています。<span class=\"keyword\">mroonga</span> は <span class=\"keyword\">MySQL</span> のプラグインとして動的にロードす"
+        ]
+      ]
+    ]
+  ]
+]

  Added: test/command/suite/select/function/snippet/one.test (+24 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/function/snippet/one.test    2012-11-07 14:53:23 +0900 (8bd0a6a)
@@ -0,0 +1,24 @@
+table_create Documents TABLE_HASH_KEY ShortText
+column_create Documents content COLUMN_SCALAR Text
+
+table_create Terms TABLE_PAT_KEY|KEY_NORMALIZE ShortText --default_tokenizer TokenBigram
+column_create Terms document_index COLUMN_INDEX|WITH_POSITION Documents content
+
+load --table Documents
+[
+["_key", "content"],
+["groonga の概要", "groonga は転置索引を用いた高速・高精度な全文検索エンジンであり、登録された文書をすぐに検索結果に反映できます。また、参照をブロックせずに更新できることから、即時更新の必要なアプリケーションにおいても高い性能を発揮します。\n\n全文検索エンジンとして開発された groonga ですが、独自のカラムストアを持つ列指向のデータベースとしての側面も持っています。そのため、MySQL ã‚„ PostgreSQL など、既存の代表的なデータベースが苦手とする集計クエリを高速に処理できるという特徴があり、組み合わせによって弱点を補うような使い方もできます。\n\ngroonga の基本機能は C ライブラリとして提供されていますが、MySQL ã‚„ PostgreSQL と連携させたり、Ruby から呼び出したりすることもできます。そのため、任æ
 „ã®ã‚¢ãƒ—リケーションに組み込むことが可能であり、多様な使い方が考えられます。 興味のある方は 利用例 をご覧ください。"],
+["全文検索と即時更新", "一般的なデータベースにおいては、追加・削除などの操作がすぐに反映されます。一方、全文検索においては、転置索引が逐次更新の難しいデータ構造であることから、文書の追加・削除に対応しないエンジンが少なくありません。\n\nこれに対し、転置索引を用いた全文検索エンジンでありながら、groonga は文書を短時間で追加・削除することができます。その上、更新しながらでも検索できるという優れた特徴を持っているため、全文検索エンジンとしてはとても柔軟性があります。また、複数の転置索引を統合するような重い処理を必要としないので、安定して高い性能を発揮することが期待できます。"],
+["カラムストアと集計クエリ", "現代は、インターネットを情報源とすれば、いくらでも情報を収集できる時代です。しかし、膨大な情報から有益な情報を引き出すのは困難であり、多面的な分析による試行錯誤が必要となります。たとえば、日付や時間帯により絞り込んでみたり、地域により絞り込んでみたり、性別や年齢により絞り込んでみたりすることでしょう。そして、そのようなときに便利な存在が集計クエリです。\n\n集計クエリとは、指定したカラムの値によってレコードをグループ化し、各グループに含まれるレコードの数を求めるクエリです。たとえば、地域の ID を格納しているカラムを指定すれば、地域毎のレコード数が求まります。日付のカラムを指定したときの出力をグラフ化すれば、レコード数の時間変化を
 視覚化することができます。さらに、地域による絞り込みと日付に対する集計クエリを組み合わせれば、特定の地域におけるレコード数の時間変化を視覚化ことも可能です。このように、尺度を自由に選択して絞り込み・集計できることは、膨大な情報を扱う上でとても重要になります。\n\ngroonga が集計クエリを高速に処理できる理由は、データベースの論理構造にカラムストアを採用しているからです。集計クエリが参照するのは指定されたカラムのみであるため、カラム単位でデータを格納する列指向のデータベースでは、必要なカラムのみを無駄なく読み出せることが利点となります。一方、レコード単位でデータを格納する行指向のデータベースでは、隣接するカラムをまとめて読み出してしまうことが欠点となります。"],
+["転置索引とトークナイザ", "転置索引は大規模な全文検索に用いられる伝統的なデータ構造です。転置索引を用いた全文検索エンジンでは、文書を追加するときに索引語を記録しておき、検索するときはクエリを索引語に分割して出現文書を求めます。そのため、文書やクエリから索引語を抜き出す方法が重要になります。\n\nトークナイザは、文字列から索引語を抜き出すモジュールです。日本語を対象とする全文検索においては、形態素を索引語として抜き出す方式と文字 N-gram を抜き出す方式のいずれか、あるいは両方を用いるのが一般的です。形態素方式は検索時間や索引サイズの面で優れているほか、検索結果に不要な文書が含まれにくいという利点を持っています。一方、N-gram 方式には検索漏れが発生しにくいという利点ã
 Œã‚り、状況によって適した方式を選択することが望ましいとされています。\n\ngroonga は形態素方式と N-gram 方式の両方に対応しています。初期状態で利用できるトークナイザは空白を区切り文字として用いる方式と N-gram 方式のみですが、形態素解析器 MeCab を組み込んだときは MeCab による分かち書きの結果を用いる形態素方式が有効になります。トークナイザはプラグインとして追加できるため、特徴的なキーワードのみを索引語として採用するなど、独自のトークナイザを開発することが可能です。"],
+["共有可能なストレージと参照ロックフリー", "CPU のマルチコア化が進んでいるため、同時に複数のクエリを実行したり、一つのクエリを複数のスレッドで実行したりすることの重要性はますます高まっています。\n\ngroonga のストレージは、複数のスレッド・プロセスで共有することができます。また、参照ロックフリーなデータ構造を採用しているため、更新クエリを実行している状況でも参照クエリを実行することができます。参照クエリを実行できる状態を維持しながら更新クエリを実行できるので、リアルタイムなシステムに適しています。さらには、MySQL を介して更新クエリを実行している最中に groonga の HTTP サーバを介して参照クエリを実行するなど、多彩な運用が可能となっています。"],
+["位置情報(緯度・経度)検索", "GPS に代表される測位システムを搭載した高機能な携帯端末の普及などによって、位置情報を扱うサービスはますます便利になっています。たとえば、近くにあるレストランを探しているときは、現在地からの距離を基準として検索をおこない、検索結果を地図上に表示してくれるようなサービスが便利です。そのため、位置情報検索を高速に実現できることが重要になっています。\n\ngroonga では転置索引を応用して高速な位置情報検索を実現しています。矩形・円による範囲検索に対応しているほか、基準点の近くを優先的に探索させることができます。また、距離計算をサポートしているので、位置情報検索の結果を基準点からの距離によって整列することも可能です。"],
+["groonga ライブラリ", "Groonga の基本機能は C ライブラリとして提供されているので、任意のアプリケーションに組み込んで利用することができます。C/C++ 以外については、Ruby から groonga を利用するライブラリなどが関連プロジェクトにおいて提供されています。詳しくは 関連プロジェクト を参照してください。"],
+["groonga サーバ", "groonga にはサーバ機能があるため、レンタルサーバなどの新しいライブラリをインストールできない環境においても利用できます。対応しているのは HTTP, memcached binary プロトコル、およびに groonga の独自プロトコルである gqtp です。サーバとして利用するときはクエリのキャッシュ機能が有効になるため、同じクエリを受け取ったときは応答時間が短くなるという特徴があります。"],
+["groonga ストレージエンジン", "groonga は独自のカラムストアを持つ列指向のデータベースとしての側面を持っていますが、既存の RDBMS のストレージエンジンとして利用することもできます。たとえば、groonga をベースとする MySQL のストレージエンジンとして mroonga が開発されています。mroonga は MySQL のプラグインとして動的にロードすることが可能であり、groonga のカラムストアをストレージとして利用したり、全文検索エンジンとして groonga を MyISAM や InnoDB と連携させたりすることができます。groonga 単体での利用、およびに MyISAM, InnoDB との連携には一長一短があるので、用途に応じて適切な組み合わせを選ぶことが大切です。詳しくは 関連プロジェクト を参照してください。"]
+]
+
+select Documents \
+  --match_columns content --query 'mroonga MySQL' \
+  --output_columns '_key, snippet(content)' \
+  --command_version 2
-------------- next part --------------
HTML����������������������������...
下载 



More information about the Groonga-commit mailing list
Back to archive index