[Groonga-commit] groonga/groonga [master] Enhanced command_suggest

Back to archive index

null+****@clear***** null+****@clear*****
2010年 8月 27日 (金) 10:47:55 JST


Daijiro MORI	2010-08-27 01:47:55 +0000 (Fri, 27 Aug 2010)

  New Revision: 64918e509ffa3a0718ed9246078ab43f49212b94

  Log:
    Enhanced command_suggest

  Modified files:
    modules/suggest/suggest.c

  Modified: modules/suggest/suggest.c (+329 -289)
===================================================================
--- modules/suggest/suggest.c    2010-08-26 09:14:38 +0000 (6ff7b5d)
+++ modules/suggest/suggest.c    2010-08-27 01:47:55 +0000 (6b8a367)
@@ -20,8 +20,13 @@
 #include "output.h"
 #include <string.h>
 
-#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
 #define VAR GRN_PROC_GET_VAR_BY_OFFSET
+#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
+#define TEXT_VALUE_LEN(x) GRN_TEXT_VALUE(x), GRN_TEXT_LEN(x)
+
+#define COMPLETE 1
+#define CORRECT  2
+#define SUGGEST  4
 
 static int
 grn_parse_suggest_types(const char *nptr, const char *end)
@@ -33,299 +38,25 @@ grn_parse_suggest_types(const char *nptr, const char *end)
       continue;
     }
     if (!memcmp(nptr, CONST_STR_LEN("complete"))) {
-      types |= 1;
+      types |= COMPLETE;
       nptr += sizeof("complete") - 1;
-    }
-    if (!memcmp(nptr, CONST_STR_LEN("correct"))) {
-      types |= 2;
+    } else if (!memcmp(nptr, CONST_STR_LEN("correct"))) {
+      types |= CORRECT;
       nptr += sizeof("correct") - 1;
-    }
-    if (!memcmp(nptr, CONST_STR_LEN("suggest"))) {
-      types |= 4;
+    } else if (!memcmp(nptr, CONST_STR_LEN("suggest"))) {
+      types |= SUGGEST;
       nptr += sizeof("suggest") - 1;
-    }
-  }
-  return types;
-}
-
-static grn_obj *
-command_suggest(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
-{
-  int types;
-  grn_obj *table;
-  types = grn_parse_suggest_types(GRN_TEXT_VALUE(VAR(0)), GRN_BULK_CURR(VAR(0)));
-
-  if ((table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(1)),
-                           GRN_TEXT_LEN(VAR(1))))) {
-    grn_obj *col;
-    if ((col = grn_obj_column(ctx, table, GRN_TEXT_VALUE(VAR(2)),
-                              GRN_TEXT_LEN(VAR(2))))) {
-      grn_obj *res;
-      if ((res = grn_table_create(ctx, NULL, 0, NULL,
-                                   GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL))) {
-        grn_obj *sorted;
-        if ((sorted = grn_table_create(ctx, NULL, 0, NULL,
-                                       GRN_OBJ_TABLE_NO_KEY, NULL, res))) {
-          grn_str *norm;
-          if ((norm = grn_str_open(ctx, GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)),
-                                   GRN_STR_NORMALIZE))) {
-            if (types & 1) {
-              /* RK search + prefix search */
-              grn_obj *index;
-              /* FIXME: support index selection */
-              if (grn_column_index(ctx, col, GRN_OP_PREFIX,
-                                   &index, 1, NULL)) {
-                grn_table_cursor *cur;
-                if ((cur = grn_table_cursor_open(ctx, grn_ctx_at(ctx, index->header.domain),
-                                                 norm->norm, norm->norm_blen,
-                                                 NULL, 0,
-                                                 0, -1,
-                                                 GRN_CURSOR_PREFIX | GRN_CURSOR_RK))) {
-                  grn_id id;
-                  while ((id = grn_table_cursor_next(ctx, cur))) {
-                    grn_ii_cursor *icur;
-                    if ((icur = grn_ii_cursor_open(ctx, (grn_ii *)index, id,
-                                                   GRN_ID_NIL, GRN_ID_MAX, 1, 0))) {
-                      grn_ii_posting *p;
-                      while ((p = grn_ii_cursor_next(ctx, icur))) {
-                        grn_hash_add(ctx, (grn_hash *)res, &p->rid, sizeof(grn_id), NULL, NULL);
-                        /* FIXME: execute _score = score */
-                      }
-                      grn_ii_cursor_close(ctx, icur);
-                    }
-                  }
-                  grn_table_cursor_close(ctx, cur);
-                } else {
-                  ERR(GRN_UNKNOWN_ERROR, "cannot open cursor for pk.");
-                  goto exit;
-                }
-
-                if ((cur = grn_table_cursor_open(ctx, table,
-                                                 GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)),
-                                                 NULL, 0, 0, -1, GRN_CURSOR_PREFIX))) {
-                  grn_id id;
-                  while ((id = grn_table_cursor_next(ctx, cur))) {
-                    grn_hash_add(ctx, (grn_hash *)res, &id, sizeof(grn_id), NULL, NULL);
-                  }
-                  grn_table_cursor_close(ctx, cur);
-                }
-              } else {
-                ERR(GRN_UNKNOWN_ERROR, "cannot find index for prefix search.");
-              }
-              grn_str_close(ctx, norm);
-            }
-          }
-          if (types & 2) {
-            grn_select_optarg optarg;
-            memset(&optarg, 0, sizeof(grn_select_optarg));
-            optarg.mode = GRN_OP_SIMILAR;
-            optarg.similarity_threshold = 1048576;
-
-            grn_ii_select(ctx, (grn_ii *)grn_ctx_get(ctx, CONST_STR_LEN("SuggestBigram.suggest_key")),
-                          GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)),
-                          (grn_hash *)res, GRN_OP_OR, &optarg);
-            {
-              /* exec _score = edit_distance(_key, "query string") for all records */
-              grn_obj *var;
-              grn_obj *expr;
-
-              GRN_EXPR_CREATE_FOR_QUERY(ctx, res, expr, var);
-              if (expr) {
-                grn_table_cursor *tc;
-
-                grn_expr_append_obj(ctx, expr,
-                                    grn_obj_column(ctx, res, CONST_STR_LEN("_score")),
-                                    GRN_OP_GET_VALUE, 1);
-                grn_expr_append_obj(ctx, expr,
-                                    grn_ctx_get(ctx, CONST_STR_LEN("edit_distance")),
-                                    GRN_OP_PUSH, 1);
-                grn_expr_append_obj(ctx, expr,
-                                    grn_obj_column(ctx, res, CONST_STR_LEN("_key")),
-                                    GRN_OP_GET_VALUE, 1);
-                grn_expr_append_const(ctx, expr, VAR(2), GRN_OP_PUSH, 1);
-                grn_expr_append_op(ctx, expr, GRN_OP_CALL, 2);
-                grn_expr_append_op(ctx, expr, GRN_OP_ASSIGN, 2);
-
-                if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) {
-                  while (!grn_table_cursor_next_o(ctx, tc, var)) {
-                    grn_expr_exec(ctx, expr, 0);
-                  }
-                  grn_table_cursor_close(ctx, tc);
-                }
-                grn_expr_close(ctx, expr);
-              } else {
-                ERR(GRN_UNKNOWN_ERROR, "error on building expr. for calicurating edit distance");
-                goto exit;
-              }
-            }
-          }
-          if (types & 4) {
-          }
-          if (ctx->rc) { goto exit; }
-
-          /* sort */
-          {
-            uint32_t nkeys;
-            grn_obj *score_col;
-            grn_table_sort_key *keys;
-            score_col = grn_obj_column(ctx, res, CONST_STR_LEN("_score"));
-            /* FIXME: use grn_table_sort instead */
-            if ((keys = grn_table_sort_key_from_str(ctx, CONST_STR_LEN("-_score"), res, &nkeys))) {
-              grn_table_cursor *scur;
-              /* TODO: support offset limit */
-              grn_table_sort(ctx, res, 0, grn_table_size(ctx, res), sorted, keys, nkeys);
-              GRN_OUTPUT_ARRAY_OPEN("RESULTS", -1);
-              if ((scur = grn_table_cursor_open(ctx, sorted, NULL, 0, NULL, 0, 0, -1, 0))) {
-                grn_id id;
-                while ((id = grn_table_cursor_next(ctx, scur))) {
-                  grn_id res_id;
-                  unsigned int key_len;
-                  char key[GRN_TABLE_MAX_KEY_SIZE];
-                  grn_obj score_val;
-
-                  GRN_OUTPUT_ARRAY_OPEN("RESULT", 2);
-                  grn_table_get_key(ctx, sorted, id, &res_id, sizeof(grn_id));
-                  grn_table_get_key(ctx, res, res_id, &id, sizeof(grn_id));
-                  key_len = grn_table_get_key(ctx, table, id, key, GRN_TABLE_MAX_KEY_SIZE);
-                  GRN_OUTPUT_STR(key, key_len);
-
-                  GRN_INT32_INIT(&score_val, 0);
-                  grn_obj_get_value(ctx, score_col, res_id, &score_val);
-                  GRN_OUTPUT_INT32(GRN_INT32_VALUE(&score_val));
-                  GRN_OUTPUT_ARRAY_CLOSE();
-                }
-                grn_table_cursor_close(ctx, scur);
-              } else {
-                ERR(GRN_UNKNOWN_ERROR, "cannot open sorted cursor.");
-              }
-              GRN_OUTPUT_ARRAY_CLOSE();
-              grn_table_sort_key_close(ctx, keys, nkeys);
-            } else {
-              ERR(GRN_UNKNOWN_ERROR, "cannot sort.");
-            }
-          }
-exit:
-          grn_obj_close(ctx, sorted);
-        } else {
-          ERR(GRN_UNKNOWN_ERROR, "cannot create temporary sort table.");
-        }
-        grn_obj_close(ctx, res);
-      } else {
-        ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
-      }
     } else {
-      ERR(GRN_INVALID_ARGUMENT, "invalid column.");
-    }
-  } else {
-    ERR(GRN_INVALID_ARGUMENT, "invalid table.");
-  }
-  return NULL;
-}
-
-static grn_obj *
-func_suggest_preparer(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
-{
-  int r = 0;
-  grn_obj *obj;
-  if (nargs == 6) {
-    grn_obj v1, pre_events;
-    grn_id post_event = GRN_RECORD_VALUE(args[0]);
-    grn_id post_type = GRN_RECORD_VALUE(args[1]);
-    grn_id post_item = GRN_RECORD_VALUE(args[2]);
-    grn_id seq = GRN_RECORD_VALUE(args[3]);
-    int64_t post_time = GRN_TIME_VALUE(args[4]);
-    grn_obj *pairs = args[5];
-    grn_obj *items = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(args[2]));
-    grn_obj *items_freq = grn_obj_column(ctx, items, CONST_STR_LEN("freq"));
-    grn_obj *items_last = grn_obj_column(ctx, items, CONST_STR_LEN("last"));
-    grn_obj *seqs = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(args[3]));
-    grn_obj *seqs_events = grn_obj_column(ctx, seqs, CONST_STR_LEN("events"));
-    grn_obj *events = grn_ctx_at(ctx, grn_obj_get_range(ctx, seqs_events));
-    grn_obj *events_type = grn_obj_column(ctx, events, CONST_STR_LEN("type"));
-    grn_obj *events_time = grn_obj_column(ctx, events, CONST_STR_LEN("time"));
-    grn_obj *events_item = grn_obj_column(ctx, events, CONST_STR_LEN("item"));
-    grn_obj *pairs_pre = grn_obj_column(ctx, pairs, CONST_STR_LEN("pre"));
-    grn_obj *pairs_post = grn_obj_column(ctx, pairs, CONST_STR_LEN("post"));
-    grn_obj *pairs_freq0 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq0"));
-    grn_obj *pairs_freq1 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq1"));
-    grn_obj *pairs_freq2 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq2"));
-    GRN_UINT32_INIT(&v1, 0);
-    GRN_UINT32_SET(ctx, &v1, 1);
-    GRN_RECORD_INIT(&pre_events, 0, grn_obj_id(ctx, events));
-    grn_obj_set_value(ctx, items_freq, post_item, &v1, GRN_OBJ_INCR);
-    grn_obj_set_value(ctx, items_last, post_item, args[4], GRN_OBJ_SET);
-    if (post_type) {
-      int added;
-      grn_id pid, tid, *ep, *es;
-      grn_obj pre_type, pre_time, pre_item;
-      uint64_t key, key_ = ((uint64_t)post_item) << 32;
-      grn_obj_get_value(ctx, seqs_events, seq, &pre_events);
-      ep = (grn_id *)GRN_BULK_CURR(&pre_events);
-      es = (grn_id *)GRN_BULK_HEAD(&pre_events);
-      GRN_RECORD_INIT(&pre_type, 0, grn_obj_get_range(ctx, events_type));
-      GRN_TIME_INIT(&pre_time, 0);
-      GRN_RECORD_INIT(&pre_item, 0, grn_obj_get_range(ctx, events_item));
-      while (es < ep--) {
-        GRN_BULK_REWIND(&pre_type);
-        GRN_BULK_REWIND(&pre_time);
-        GRN_BULK_REWIND(&pre_item);
-        grn_obj_get_value(ctx, events_type, *ep, &pre_type);
-        grn_obj_get_value(ctx, events_time, *ep, &pre_time);
-        grn_obj_get_value(ctx, events_item, *ep, &pre_item);
-        if (GRN_TIME_VALUE(&pre_time) + 60000000 < post_time) {
-          r = (int)((post_time - GRN_TIME_VALUE(&pre_time))/1000000);
-          break;
-        }
-        key = key_ + GRN_RECORD_VALUE(&pre_item);
-        pid = grn_table_add(ctx, pairs, &key, sizeof(uint64_t), &added);
-        if (added) {
-          grn_obj_set_value(ctx, pairs_pre, pid, &pre_item, GRN_OBJ_SET);
-          grn_obj_set_value(ctx, pairs_post, pid, args[2], GRN_OBJ_SET);
-        }
-        if (GRN_RECORD_VALUE(&pre_type)) {
-          grn_obj_set_value(ctx, pairs_freq1, pid, &v1, GRN_OBJ_INCR);
-          break;
-        } else {
-          grn_obj_set_value(ctx, pairs_freq0, pid, &v1, GRN_OBJ_INCR);
-        }
-      }
-      {
-        char keybuf[GRN_TABLE_MAX_KEY_SIZE];
-        int keylen = grn_table_get_key(ctx, items, post_item,
-                                       keybuf, GRN_TABLE_MAX_KEY_SIZE);
-        grn_token *token = grn_token_open(ctx, items, keybuf, keylen, 1);
-        if (token) {
-          while ((tid = grn_token_next(ctx, token)) && tid != post_item) {
-            key = key_ + tid;
-            pid = grn_table_add(ctx, pairs, &key, sizeof(uint64_t), &added);
-            if (added) {
-              GRN_RECORD_SET(ctx, &pre_item, tid);
-              grn_obj_set_value(ctx, pairs_pre, pid, &pre_item, GRN_OBJ_SET);
-              grn_obj_set_value(ctx, pairs_post, pid, args[2], GRN_OBJ_SET);
-            }
-            grn_obj_set_value(ctx, pairs_freq2, pid, &v1, GRN_OBJ_INCR);
-          }
-          grn_token_close(ctx, token);
-        }
-      }
-      GRN_OBJ_FIN(ctx, &pre_type);
-      GRN_OBJ_FIN(ctx, &pre_time);
-      GRN_OBJ_FIN(ctx, &pre_item);
-      GRN_BULK_REWIND(&pre_events);
+      break;
     }
-    GRN_RECORD_SET(ctx, &pre_events, post_event);
-    grn_obj_set_value(ctx, seqs_events, seq, &pre_events, GRN_OBJ_APPEND);
-    GRN_OBJ_FIN(ctx, &pre_events);
-    GRN_OBJ_FIN(ctx, &v1);
   }
-  if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) { GRN_UINT32_SET(ctx, obj, r); }
-  return obj;
+  return types;
 }
 
 static void
-suggest_sub(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int query_type)
+cooccur_search(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int query_type)
 {
-  grn_id id = grn_table_get(ctx, table, GRN_TEXT_VALUE(query), GRN_TEXT_LEN(query));
+  grn_id id = grn_table_get(ctx, table, TEXT_VALUE_LEN(query));
   if (id) {
     grn_ii_cursor *c;
     grn_obj *co = grn_obj_column(ctx, table, CONST_STR_LEN("co"));
@@ -333,13 +64,13 @@ suggest_sub(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int quer
     grn_obj *items_freq = grn_obj_column(ctx, table, CONST_STR_LEN("freq"));
     grn_obj *pairs_freq, *pairs_post = grn_obj_column(ctx, pairs, CONST_STR_LEN("post"));
     switch (query_type) {
-    case 0 :
+    case COMPLETE :
       pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq0"));
       break;
-    case 1 :
+    case CORRECT :
       pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq1"));
       break;
-    case 2 :
+    case SUGGEST :
       pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq2"));
       break;
     default :
@@ -354,6 +85,7 @@ suggest_sub(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int quer
       GRN_INT32_INIT(&item_freq, 0);
       while ((p = grn_ii_cursor_next(ctx, c))) {
         grn_id post_id;
+        int pfreq, ifreq;
         GRN_BULK_REWIND(&post);
         GRN_BULK_REWIND(&pair_freq);
         GRN_BULK_REWIND(&item_freq);
@@ -361,9 +93,12 @@ suggest_sub(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int quer
         grn_obj_get_value(ctx, pairs_freq, p->rid, &pair_freq);
         post_id = GRN_RECORD_VALUE(&post);
         grn_obj_get_value(ctx, items_freq, post_id, &item_freq);
-        if (GRN_INT32_VALUE(&pair_freq)) {
+        pfreq = GRN_INT32_VALUE(&pair_freq);
+        ifreq = GRN_INT32_VALUE(&item_freq);
+        if (pfreq && ifreq) {
           grn_rset_recinfo *ri;
-          uint32_t score = GRN_INT32_VALUE(&pair_freq) * 1000 / GRN_INT32_VALUE(&item_freq);
+          uint32_t score = 1000/pfreq;
+          /* put any formula if desired */
           if (grn_hash_add(ctx, (grn_hash *)res,
                            &post_id, sizeof(grn_id), (void **)&ri, NULL)) {
             ri->score += score;
@@ -378,6 +113,311 @@ suggest_sub(grn_ctx *ctx, grn_obj *table, grn_obj *query, grn_obj *res, int quer
   }
 }
 
+static void
+output(grn_ctx *ctx, grn_obj *table, grn_obj *res, int limit)
+{
+  grn_obj *sorted;
+  if ((sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, NULL, res))) {
+    uint32_t nkeys;
+    grn_obj *score_col;
+    grn_table_sort_key *keys;
+    score_col = grn_obj_column(ctx, res, CONST_STR_LEN("_score"));
+    /* FIXME: use grn_table_sort instead */
+    if ((keys = grn_table_sort_key_from_str(ctx, CONST_STR_LEN("_score"), res, &nkeys))) {
+      grn_table_cursor *scur;
+      /* TODO: support offset limit */
+      grn_table_sort(ctx, res, 0, limit, sorted, keys, nkeys);
+      GRN_OUTPUT_ARRAY_OPEN("RESULTS", -1);
+      if ((scur = grn_table_cursor_open(ctx, sorted, NULL, 0, NULL, 0, 0, -1, 0))) {
+        grn_id id;
+        while ((id = grn_table_cursor_next(ctx, scur))) {
+          grn_id res_id;
+          unsigned int key_len;
+          char key[GRN_TABLE_MAX_KEY_SIZE];
+          grn_obj score_val;
+
+          GRN_OUTPUT_ARRAY_OPEN("RESULT", 2);
+          grn_table_get_key(ctx, sorted, id, &res_id, sizeof(grn_id));
+          grn_table_get_key(ctx, res, res_id, &id, sizeof(grn_id));
+          key_len = grn_table_get_key(ctx, table, id, key, GRN_TABLE_MAX_KEY_SIZE);
+          GRN_OUTPUT_STR(key, key_len);
+
+          GRN_INT32_INIT(&score_val, 0);
+          grn_obj_get_value(ctx, score_col, res_id, &score_val);
+          GRN_OUTPUT_INT32(GRN_INT32_VALUE(&score_val));
+          GRN_OUTPUT_ARRAY_CLOSE();
+        }
+        grn_table_cursor_close(ctx, scur);
+      } else {
+        ERR(GRN_UNKNOWN_ERROR, "cannot open sorted cursor.");
+      }
+      GRN_OUTPUT_ARRAY_CLOSE();
+      grn_table_sort_key_close(ctx, keys, nkeys);
+    } else {
+      ERR(GRN_UNKNOWN_ERROR, "cannot sort.");
+    }
+    grn_obj_unlink(ctx, score_col);
+    grn_obj_unlink(ctx, sorted);
+  } else {
+    ERR(GRN_UNKNOWN_ERROR, "cannot create temporary sort table.");
+  }
+}
+
+static void
+complete(grn_ctx *ctx, grn_obj *table, grn_obj *col, grn_obj *query)
+{
+  grn_obj *res;
+  if ((res = grn_table_create(ctx, NULL, 0, NULL,
+                              GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL))) {
+    grn_str *norm;
+    grn_table_cursor *cur;
+    if ((norm = grn_str_open(ctx, TEXT_VALUE_LEN(query), GRN_STR_NORMALIZE))) {
+      /* RK search + prefix search */
+      grn_obj *index;
+      /* FIXME: support index selection */
+      if (grn_column_index(ctx, col, GRN_OP_PREFIX, &index, 1, NULL)) {
+        if ((cur = grn_table_cursor_open(ctx, grn_ctx_at(ctx, index->header.domain),
+                                         norm->norm, norm->norm_blen, NULL, 0, 0, -1,
+                                         GRN_CURSOR_PREFIX|GRN_CURSOR_RK))) {
+          grn_id id;
+          while ((id = grn_table_cursor_next(ctx, cur))) {
+            grn_ii_cursor *icur;
+            if ((icur = grn_ii_cursor_open(ctx, (grn_ii *)index, id,
+                                           GRN_ID_NIL, GRN_ID_MAX, 1, 0))) {
+              grn_ii_posting *p;
+              while ((p = grn_ii_cursor_next(ctx, icur))) {
+                grn_hash_add(ctx, (grn_hash *)res, &p->rid, sizeof(grn_id), NULL, NULL);
+                /* FIXME: execute _score = score */
+              }
+              grn_ii_cursor_close(ctx, icur);
+            }
+          }
+          grn_table_cursor_close(ctx, cur);
+        } else {
+          ERR(GRN_UNKNOWN_ERROR, "cannot open cursor for pk.");
+        }
+      } else {
+        ERR(GRN_UNKNOWN_ERROR, "cannot find index for prefix search.");
+      }
+      grn_str_close(ctx, norm);
+    }
+    if ((cur = grn_table_cursor_open(ctx, table, TEXT_VALUE_LEN(query),
+                                     NULL, 0, 0, -1, GRN_CURSOR_PREFIX))) {
+      grn_id id;
+      while ((id = grn_table_cursor_next(ctx, cur))) {
+        grn_hash_add(ctx, (grn_hash *)res, &id, sizeof(grn_id), NULL, NULL);
+      }
+      grn_table_cursor_close(ctx, cur);
+    }
+    cooccur_search(ctx, table, query, res, COMPLETE);
+    output(ctx, table, res, 10);
+    grn_obj_close(ctx, res);
+  } else {
+    ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+  }
+}
+
+static void
+correct(grn_ctx *ctx, grn_obj *table, grn_obj *query)
+{
+  grn_obj *res;
+  if ((res = grn_table_create(ctx, NULL, 0, NULL,
+                              GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL))) {
+    grn_obj *key, *index;
+    if ((key = grn_obj_column(ctx, table, CONST_STR_LEN("_key")))) {
+      if (grn_column_index(ctx, key, GRN_OP_MATCH, &index, 1, NULL)) {
+        grn_select_optarg optarg;
+        memset(&optarg, 0, sizeof(grn_select_optarg));
+        optarg.mode = GRN_OP_SIMILAR;
+        optarg.similarity_threshold = INT32_MAX;
+        grn_ii_select(ctx, (grn_ii *)index, TEXT_VALUE_LEN(query),
+                      (grn_hash *)res, GRN_OP_OR, &optarg);
+        {
+          /* exec _score = edit_distance(_key, "query string") for all records */
+          grn_obj *var;
+          grn_obj *expr;
+
+          GRN_EXPR_CREATE_FOR_QUERY(ctx, res, expr, var);
+          if (expr) {
+            grn_table_cursor *tc;
+            grn_obj *score = grn_obj_column(ctx, res, CONST_STR_LEN("_score"));
+            grn_obj *key = grn_obj_column(ctx, res, CONST_STR_LEN("_key"));
+            grn_expr_append_obj(ctx, expr,
+                                score,
+                                GRN_OP_GET_VALUE, 1);
+            grn_expr_append_obj(ctx, expr,
+                                grn_ctx_get(ctx, CONST_STR_LEN("edit_distance")),
+                                GRN_OP_PUSH, 1);
+            grn_expr_append_obj(ctx, expr,
+                                key,
+                                GRN_OP_GET_VALUE, 1);
+            grn_expr_append_const(ctx, expr, query, GRN_OP_PUSH, 1);
+            grn_expr_append_op(ctx, expr, GRN_OP_CALL, 2);
+            grn_expr_append_op(ctx, expr, GRN_OP_ASSIGN, 2);
+
+            if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) {
+              while (!grn_table_cursor_next_o(ctx, tc, var)) {
+                grn_expr_exec(ctx, expr, 0);
+              }
+              grn_table_cursor_close(ctx, tc);
+            }
+            grn_obj_unlink(ctx, score);
+            grn_obj_unlink(ctx, key);
+            grn_obj_unlink(ctx, expr);
+          } else {
+            ERR(GRN_UNKNOWN_ERROR,
+                "error on building expr. for calicurating edit distance");
+          }
+        }
+        grn_obj_unlink(ctx, index);
+      }
+      grn_obj_unlink(ctx, key);
+    }
+    cooccur_search(ctx, table, query, res, CORRECT);
+    output(ctx, table, res, 10);
+    grn_obj_close(ctx, res);
+  } else {
+    ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+  }
+}
+
+static void
+suggest(grn_ctx *ctx, grn_obj *table, grn_obj *query)
+{
+  grn_obj *res;
+  if ((res = grn_table_create(ctx, NULL, 0, NULL,
+                              GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL))) {
+    cooccur_search(ctx, table, query, res, SUGGEST);
+    output(ctx, table, res, 10);
+    grn_obj_close(ctx, res);
+  } else {
+    ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+  }
+}
+
+static grn_obj *
+command_suggest(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+  grn_obj *table, *col;
+  int types = grn_parse_suggest_types(GRN_TEXT_VALUE(VAR(0)), GRN_BULK_CURR(VAR(0)));
+  if ((table = grn_ctx_get(ctx, TEXT_VALUE_LEN(VAR(1))))) {
+    if (types & 1) {
+      if ((col = grn_obj_column(ctx, table, TEXT_VALUE_LEN(VAR(2))))) {
+        complete(ctx, table, col, VAR(3));
+      } else {
+        ERR(GRN_INVALID_ARGUMENT, "invalid column.");
+      }
+    }
+    if (types & 2) { correct(ctx, table, VAR(3)); }
+    if (types & 4) { suggest(ctx, table, VAR(3)); }
+  } else {
+    ERR(GRN_INVALID_ARGUMENT, "invalid table.");
+  }
+  return NULL;
+}
+
+static grn_obj *
+func_suggest_preparer(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+  int r = 0;
+  grn_obj *obj;
+  if (nargs == 6) {
+    grn_obj v1, pre_events;
+    grn_id post_event = GRN_RECORD_VALUE(args[0]);
+    grn_id post_type = GRN_RECORD_VALUE(args[1]);
+    grn_id post_item = GRN_RECORD_VALUE(args[2]);
+    grn_id seq = GRN_RECORD_VALUE(args[3]);
+    int64_t post_time = GRN_TIME_VALUE(args[4]);
+    grn_obj *pairs = args[5];
+    if (post_event && post_item && seq) {
+      grn_obj *items = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(args[2]));
+      grn_obj *items_freq = grn_obj_column(ctx, items, CONST_STR_LEN("freq"));
+      grn_obj *items_last = grn_obj_column(ctx, items, CONST_STR_LEN("last"));
+      grn_obj *seqs = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(args[3]));
+      grn_obj *seqs_events = grn_obj_column(ctx, seqs, CONST_STR_LEN("events"));
+      grn_obj *events = grn_ctx_at(ctx, grn_obj_get_range(ctx, seqs_events));
+      grn_obj *events_type = grn_obj_column(ctx, events, CONST_STR_LEN("type"));
+      grn_obj *events_time = grn_obj_column(ctx, events, CONST_STR_LEN("time"));
+      grn_obj *events_item = grn_obj_column(ctx, events, CONST_STR_LEN("item"));
+      grn_obj *pairs_pre = grn_obj_column(ctx, pairs, CONST_STR_LEN("pre"));
+      grn_obj *pairs_post = grn_obj_column(ctx, pairs, CONST_STR_LEN("post"));
+      grn_obj *pairs_freq0 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq0"));
+      grn_obj *pairs_freq1 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq1"));
+      grn_obj *pairs_freq2 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq2"));
+      GRN_UINT32_INIT(&v1, 0);
+      GRN_UINT32_SET(ctx, &v1, 1);
+      GRN_RECORD_INIT(&pre_events, 0, grn_obj_id(ctx, events));
+      grn_obj_set_value(ctx, items_freq, post_item, &v1, GRN_OBJ_INCR);
+      grn_obj_set_value(ctx, items_last, post_item, args[4], GRN_OBJ_SET);
+      if (post_type) {
+        int added;
+        grn_id pid, tid, *ep, *es;
+        grn_obj pre_type, pre_time, pre_item;
+        uint64_t key, key_ = ((uint64_t)post_item) << 32;
+        grn_obj_get_value(ctx, seqs_events, seq, &pre_events);
+        ep = (grn_id *)GRN_BULK_CURR(&pre_events);
+        es = (grn_id *)GRN_BULK_HEAD(&pre_events);
+        GRN_RECORD_INIT(&pre_type, 0, grn_obj_get_range(ctx, events_type));
+        GRN_TIME_INIT(&pre_time, 0);
+        GRN_RECORD_INIT(&pre_item, 0, grn_obj_get_range(ctx, events_item));
+        while (es < ep--) {
+          GRN_BULK_REWIND(&pre_type);
+          GRN_BULK_REWIND(&pre_time);
+          GRN_BULK_REWIND(&pre_item);
+          grn_obj_get_value(ctx, events_type, *ep, &pre_type);
+          grn_obj_get_value(ctx, events_time, *ep, &pre_time);
+          grn_obj_get_value(ctx, events_item, *ep, &pre_item);
+          if (GRN_TIME_VALUE(&pre_time) + 60000000 < post_time) {
+            r = (int)((post_time - GRN_TIME_VALUE(&pre_time))/1000000);
+            break;
+          }
+          key = key_ + GRN_RECORD_VALUE(&pre_item);
+          pid = grn_table_add(ctx, pairs, &key, sizeof(uint64_t), &added);
+          if (added) {
+            grn_obj_set_value(ctx, pairs_pre, pid, &pre_item, GRN_OBJ_SET);
+            grn_obj_set_value(ctx, pairs_post, pid, args[2], GRN_OBJ_SET);
+          }
+          if (GRN_RECORD_VALUE(&pre_type)) {
+            grn_obj_set_value(ctx, pairs_freq1, pid, &v1, GRN_OBJ_INCR);
+            break;
+          } else {
+            grn_obj_set_value(ctx, pairs_freq0, pid, &v1, GRN_OBJ_INCR);
+          }
+        }
+        {
+          char keybuf[GRN_TABLE_MAX_KEY_SIZE];
+          int keylen = grn_table_get_key(ctx, items, post_item,
+                                         keybuf, GRN_TABLE_MAX_KEY_SIZE);
+          grn_token *token = grn_token_open(ctx, items, keybuf, keylen, 1);
+          if (token) {
+            while ((tid = grn_token_next(ctx, token)) && tid != post_item) {
+              key = key_ + tid;
+              pid = grn_table_add(ctx, pairs, &key, sizeof(uint64_t), &added);
+              if (added) {
+                GRN_RECORD_SET(ctx, &pre_item, tid);
+                grn_obj_set_value(ctx, pairs_pre, pid, &pre_item, GRN_OBJ_SET);
+                grn_obj_set_value(ctx, pairs_post, pid, args[2], GRN_OBJ_SET);
+              }
+              grn_obj_set_value(ctx, pairs_freq2, pid, &v1, GRN_OBJ_INCR);
+            }
+            grn_token_close(ctx, token);
+          }
+        }
+        GRN_OBJ_FIN(ctx, &pre_type);
+        GRN_OBJ_FIN(ctx, &pre_time);
+        GRN_OBJ_FIN(ctx, &pre_item);
+        GRN_BULK_REWIND(&pre_events);
+      }
+      GRN_RECORD_SET(ctx, &pre_events, post_event);
+      grn_obj_set_value(ctx, seqs_events, seq, &pre_events, GRN_OBJ_APPEND);
+      GRN_OBJ_FIN(ctx, &pre_events);
+      GRN_OBJ_FIN(ctx, &v1);
+    }
+  }
+  if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) { GRN_UINT32_SET(ctx, obj, r); }
+  return obj;
+}
+
 grn_rc
 grn_module_init_suggest(grn_ctx *ctx)
 {




Groonga-commit メーリングリストの案内
Back to archive index