[Groonga-mysql-commit] mroonga/mroonga [master] [wrapper] support fast order and limit.

Back to archive index

null+****@clear***** null+****@clear*****
2011年 9月 25日 (日) 20:01:30 JST


Kouhei Sutou	2011-09-25 11:01:30 +0000 (Sun, 25 Sep 2011)

  New Revision: 98ef30703a5f6d3c546851bc83f3a300a30afb0d

  Log:
    [wrapper] support fast order and limit.

  Added files:
    test/sql/groonga_wrapper/r/order_limit_performance.result
    test/sql/groonga_wrapper/t/order_limit_performance.test
  Modified files:
    ha_mroonga.cc
    ha_mroonga.h
    mrn_sys.h

  Modified: ha_mroonga.cc (+187 -15)
===================================================================
--- ha_mroonga.cc    2011-09-25 07:28:45 +0000 (a0084bc)
+++ ha_mroonga.cc    2011-09-25 11:01:30 +0000 (ee33579)
@@ -1095,6 +1095,7 @@ static void mrn_wrapper_ft_close_search(FT_INFO *handler)
   MRN_DBUG_ENTER_FUNCTION();
   st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
   grn_obj_unlink(info->ctx, info->result);
+  grn_obj_unlink(info->ctx, info->sorted_result);
   grn_obj_unlink(info->ctx, info->score_column);
   grn_obj_unlink(info->ctx, &(info->key));
   grn_obj_unlink(info->ctx, &(info->score));
@@ -1202,6 +1203,7 @@ ha_mroonga::ha_mroonga(handlerton *hton, TABLE_SHARE *share)
   result0 = NULL;
   result_geo = NULL;
   score_column = NULL;
+  key_accessor = NULL;
   share = NULL;
   is_clone = FALSE;
   wrap_handler = NULL;
@@ -4764,12 +4766,18 @@ int ha_mroonga::wrapper_ft_init()
 {
   MRN_DBUG_ENTER_METHOD();
   int error;
-  cursor = grn_table_cursor_open(ctx, matched_record_keys, NULL, 0, NULL, 0, 0,
-                                 -1, 0);
-  if (ctx->rc)
-  {
-    error = ER_ERROR_ON_READ;
-    my_message(error, ctx->errbuf, MYF(0));
+  if (!cursor) {
+    cursor = grn_table_cursor_open(ctx, matched_record_keys, NULL, 0, NULL, 0, 0,
+                                   -1, 0);
+    if (ctx->rc)
+    {
+      error = ER_ERROR_ON_READ;
+      my_message(error, ctx->errbuf, MYF(0));
+    } else {
+      key_accessor = grn_obj_column(ctx, matched_record_keys,
+                                    MRN_COLUMN_NAME_KEY,
+                                    strlen(MRN_COLUMN_NAME_KEY));
+    }
   }
   DBUG_RETURN(error);
 }
@@ -4818,6 +4826,9 @@ void ha_mroonga::ft_end()
 FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key)
 {
   MRN_DBUG_ENTER_METHOD();
+
+  clear_cursor();
+
   struct st_mrn_ft_info *info = new st_mrn_ft_info();
   info->please = &mrn_wrapper_ft_vft;
   info->mroonga = this;
@@ -4885,7 +4896,29 @@ FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key)
   grn_obj_unlink(info->ctx, expression);
   grn_obj_unlink(info->ctx, match_columns);
 
-  merge_matched_record_keys(info->result);
+  grn_table_sort_key *sort_keys = NULL;
+  int n_sort_keys = 0;
+  longlong limit = -1;
+  check_fast_order_limit(&sort_keys, &n_sort_keys, &limit, info->score_column);
+  if (fast_order_limit) {
+    info->sorted_result = grn_table_create(ctx, NULL,
+                                           0, NULL,
+                                           GRN_OBJ_TABLE_NO_KEY, NULL,
+                                           info->result);
+    grn_table_sort(ctx, info->result, 0, limit, info->sorted_result,
+                   sort_keys, n_sort_keys);
+    cursor = grn_table_cursor_open(ctx, info->sorted_result,
+                                   NULL, 0, NULL, 0,
+                                   0, -1, 0);
+    key_accessor = grn_obj_column(ctx, info->sorted_result,
+                                  MRN_COLUMN_NAME_KEY,
+                                  strlen(MRN_COLUMN_NAME_KEY));
+  } else {
+    merge_matched_record_keys(info->result);
+  }
+  if (sort_keys) {
+    free(sort_keys);
+  }
 
   DBUG_RETURN((FT_INFO *)info);
 }
@@ -4999,15 +5032,11 @@ int ha_mroonga::wrapper_ft_read(uchar *buf)
     record_id = grn_table_cursor_next(ctx, cursor);
     if (record_id == GRN_ID_NIL) {
       error = HA_ERR_END_OF_FILE;
-      grn_table_cursor_close(ctx, cursor);
-      cursor = NULL;
+      clear_cursor();
       break;
     } else {
-      grn_id relation_record_id;
-      grn_table_get_key(ctx, matched_record_keys, record_id,
-                        &relation_record_id, sizeof(grn_id));
-      grn_table_get_key(ctx, grn_table, relation_record_id,
-                        GRN_TEXT_VALUE(&pkey), table->key_info->key_length);
+      GRN_BULK_REWIND(&pkey);
+      grn_obj_get_value(ctx, key_accessor, record_id, &pkey);
       MRN_SET_WRAP_SHARE_KEY(share, table->s);
       MRN_SET_WRAP_TABLE_KEY(this, table);
 #ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_IDX_MAP
@@ -5193,9 +5222,13 @@ void ha_mroonga::push_warning_unsupported_spatial_index_search(enum ha_rkey_func
                       search_name);
 }
 
-void ha_mroonga::clear_search_result()
+void ha_mroonga::clear_cursor()
 {
   MRN_DBUG_ENTER_METHOD();
+  if (key_accessor) {
+    grn_obj_unlink(ctx, key_accessor);
+    key_accessor = NULL;
+  }
   if (cursor) {
     grn_table_cursor_close(ctx, cursor);
     cursor = NULL;
@@ -5204,6 +5237,13 @@ void ha_mroonga::clear_search_result()
     grn_table_cursor_close(ctx, index_table_cursor);
     index_table_cursor = NULL;
   }
+  DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_search_result()
+{
+  MRN_DBUG_ENTER_METHOD();
+  clear_cursor();
   if (result0) {
     grn_obj_unlink(ctx, result0);
     result0 = NULL;
@@ -5516,6 +5556,138 @@ void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys,
   DBUG_VOID_RETURN;
 }
 
+void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys,
+                                        int *n_sort_keys,
+                                        longlong *limit,
+                                        grn_obj *score_column)
+{
+  MRN_DBUG_ENTER_METHOD();
+  st_select_lex *select_lex = table->pos_in_table_list->select_lex;
+
+  if (
+    thd_sql_command(ha_thd()) == SQLCOM_SELECT &&
+    !select_lex->with_sum_func &&
+    !select_lex->group_list.elements &&
+    !select_lex->having &&
+    select_lex->table_list.elements == 1 &&
+    select_lex->order_list.elements &&
+    select_lex->explicit_limit &&
+    select_lex->select_limit &&
+    select_lex->select_limit->val_int() > 0
+  ) {
+    if (select_lex->offset_limit) {
+      *limit = select_lex->offset_limit->val_int();
+    } else {
+      *limit = 0;
+    }
+    *limit += select_lex->select_limit->val_int();
+    if (*limit > (longlong)INT_MAX) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+      fast_order_limit = FALSE;
+      DBUG_VOID_RETURN;
+    }
+    Item *info = (Item *)select_lex->item_list.first_node()->info;
+    Item *where;
+    where = select_lex->where;
+    if (!where ||
+        where->type() != Item::FUNC_ITEM ||
+        ((Item_func *)where)->functype() != Item_func::FT_FUNC) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+      fast_order_limit = FALSE;
+      DBUG_VOID_RETURN;
+    }
+    Item_func *match_against = (Item_func *)where;
+    where = where->next;
+    if (!where ||
+        where->type() != Item::STRING_ITEM) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+      fast_order_limit = FALSE;
+      DBUG_VOID_RETURN;
+    }
+    where = where->next;
+    if (!where ||
+        where->type() != Item::FIELD_ITEM) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+      fast_order_limit = FALSE;
+      DBUG_VOID_RETURN;
+    }
+    for (where = where->next; where; where = where->next) {
+      if (grn_columns && where->type() == Item::FIELD_ITEM)
+        continue;
+      if (where->type() == Item::FUNC_ITEM && match_against->eq(where, true)) {
+        for (int i = 0; i < match_against->arg_count; i++) {
+          where = where->next;
+        }
+        continue;
+      }
+      if (where == info)
+        continue;
+      break;
+    }
+    if (where && (where != info || !where->eq(info, true))) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+      fast_order_limit = FALSE;
+      DBUG_VOID_RETURN;
+    }
+    *n_sort_keys = select_lex->order_list.elements;
+    *sort_keys = (grn_table_sort_key *)malloc(sizeof(grn_table_sort_key) *
+                                              *n_sort_keys);
+    ORDER *order;
+    int i, col_field_index = -1;
+    for (order = (ORDER *) select_lex->order_list.first, i = 0; order;
+         order = order->next, i++) {
+      Item *item = *order->item;
+      if (grn_columns && item->type() == Item::FIELD_ITEM)
+      {
+        Field *field = ((Item_field *) (*order->item))->field;
+        const char *column_name = field->field_name;
+        int column_name_size = strlen(column_name);
+
+        if (strncmp(MRN_COLUMN_NAME_ID, column_name, column_name_size) == 0) {
+          (*sort_keys)[i].key = grn_obj_column(ctx, grn_table,
+                                               column_name, column_name_size);
+        } else if (strncmp(MRN_COLUMN_NAME_SCORE,
+                           column_name, column_name_size) == 0) {
+          (*sort_keys)[i].key = score_column;
+        } else {
+          (*sort_keys)[i].key = grn_columns[field->field_index];
+          col_field_index = field->field_index;
+        }
+      } else if (match_against->eq(item, true)) {
+        (*sort_keys)[i].key = score_column;
+      } else {
+        DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+        fast_order_limit = FALSE;
+        DBUG_VOID_RETURN;
+      }
+      (*sort_keys)[i].offset = 0;
+      if (MRN_ORDER_IS_ASC(order))
+      {
+        (*sort_keys)[i].flags = GRN_TABLE_SORT_ASC;
+      } else {
+        (*sort_keys)[i].flags = GRN_TABLE_SORT_DESC;
+      }
+    }
+    grn_obj *index;
+    if (i == 1 && col_field_index >= 0 &&
+        grn_column_index(ctx, grn_columns[col_field_index], GRN_OP_LESS,
+                         &index, 1, NULL)) {
+      DBUG_PRINT("info", ("mroonga fast_order_limit_with_index = TRUE"));
+      fast_order_limit_with_index = TRUE;
+    } else {
+      DBUG_PRINT("info", ("mroonga fast_order_limit_with_index = FALSE"));
+      fast_order_limit_with_index = FALSE;
+    }
+    DBUG_PRINT("info", ("mroonga fast_order_limit = TRUE"));
+    fast_order_limit = TRUE;
+    mrn_fast_order_limit++;
+    DBUG_VOID_RETURN;
+  }
+  DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE"));
+  fast_order_limit = FALSE;
+  DBUG_VOID_RETURN;
+}
+
 void ha_mroonga::store_fields_from_primary_table(uchar *buf, grn_id record_id)
 {
   MRN_DBUG_ENTER_METHOD();

  Modified: ha_mroonga.h (+6 -1)
===================================================================
--- ha_mroonga.h    2011-09-25 07:28:45 +0000 (3c1d906)
+++ ha_mroonga.h    2011-09-25 11:01:30 +0000 (266252b)
@@ -1,4 +1,4 @@
-/* 
+/*
   Copyright(C) 2010 Tetsuro IKEDA
   Copyright(C) 2010-2011 Kentoku SHIBA
   Copyright(C) 2011 Kouhei Sutou <kou****@clear*****>
@@ -86,6 +86,7 @@ struct st_mrn_ft_info
   grn_ctx *ctx;
   grn_obj *table;
   grn_obj *result;
+  grn_obj *sorted_result;
   grn_obj *score_column;
   grn_obj key;
   grn_obj score;
@@ -134,6 +135,7 @@ private:
   grn_table_cursor *index_table_cursor;
   grn_id record_id;
   grn_obj *score_column;
+  grn_obj *key_accessor;
 
   st_mrn_ft_info mrn_ft_info;
   grn_obj *matched_record_keys;
@@ -330,6 +332,7 @@ protected:
 
 private:
   void push_warning_unsupported_spatial_index_search(enum ha_rkey_function flag);
+  void clear_cursor();
   void clear_search_result();
   grn_obj *find_tokenizer(const char *name, int name_length);
   int storage_get_next_record(uchar *buf);
@@ -342,6 +345,8 @@ private:
   void check_count_skip(key_part_map start_key_part_map,
                         key_part_map end_key_part_map, bool fulltext);
   void check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys);
+  void check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys,
+                              longlong *limit, grn_obj *score_column);
   void store_fields_from_primary_table(uchar *buf, grn_id record_id);
   void set_pk_bitmap();
   int wrapper_create(const char *name, TABLE *table,

  Modified: mrn_sys.h (+1 -0)
===================================================================
--- mrn_sys.h    2011-09-25 07:28:45 +0000 (b9112b9)
+++ mrn_sys.h    2011-09-25 11:01:30 +0000 (3e8bf26)
@@ -35,6 +35,7 @@
 #define MRN_HASH_SUFFIX "_hash"
 #define MRN_PAT_SUFFIX "_pat"
 #define MRN_COLUMN_NAME_ID "_id"
+#define MRN_COLUMN_NAME_KEY "_key"
 #define MRN_COLUMN_NAME_SCORE "_score"
 #ifndef MRN_TOKENIZER_DEFAULT
 #  define MRN_TOKENIZER_DEFAULT "TokenBigram"

  Added: test/sql/groonga_wrapper/r/order_limit_performance.result (+66 -0) 100644
===================================================================
--- /dev/null
+++ test/sql/groonga_wrapper/r/order_limit_performance.result    2011-09-25 11:01:30 +0000 (63971d2)
@@ -0,0 +1,66 @@
+drop table if exists t1;
+create table t1 (
+c1 int primary key,
+c2 int,
+c3 text,
+key idx1(c2),
+fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+select *, match(c3) against("ii") from t1
+where match(c3) against("ii") order by c1 desc limit 1;
+c1	c2	c3	match(c3) against("ii")
+5	50	aa ii uu ii oo	2
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	0
+select *, match(c3) against("ii") from t1
+where match(c3) against("ii") order by c1 limit 1;
+c1	c2	c3	match(c3) against("ii")
+1	10	aa ii uu ee oo	1
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	0
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") desc;
+c3	match(c3) against("ii")
+ii si ii se ii	3
+aa ii uu ii oo	2
+aa ii uu ee oo	1
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	0
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1;
+c3	match(c3) against("ii")
+aa ii uu ii oo	2
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	1
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii");
+c3	match(c3) against("ii")
+aa ii uu ee oo	1
+aa ii uu ii oo	2
+ii si ii se ii	3
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	1
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") limit 1;
+c3	match(c3) against("ii")
+aa ii uu ee oo	1
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	2
+select count(*) from t1 where match(c3) against("ii") limit 1;
+count(*)
+3
+show status like 'groonga_fast_order_limit';
+Variable_name	Value
+groonga_fast_order_limit	2
+drop table t1;

  Added: test/sql/groonga_wrapper/t/order_limit_performance.test (+66 -0) 100644
===================================================================
--- /dev/null
+++ test/sql/groonga_wrapper/t/order_limit_performance.test    2011-09-25 11:01:30 +0000 (739f910)
@@ -0,0 +1,66 @@
+# Copyright(C) 2010 Kentoku SHIBA
+# Copyright(C) 2011 Kouhei Sutou <kou****@clear*****>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+--source suite/groonga_include/groonga_init.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (
+  c1 int primary key,
+  c2 int,
+  c3 text,
+  key idx1(c2),
+  fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+
+select *, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by c1 desc limit 1;
+show status like 'groonga_fast_order_limit';
+
+select *, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by c1 limit 1;
+show status like 'groonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by match(c3) against("ii") desc;
+show status like 'groonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1;
+show status like 'groonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by match(c3) against("ii");
+show status like 'groonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+  where match(c3) against("ii") order by match(c3) against("ii") limit 1;
+show status like 'groonga_fast_order_limit';
+
+select count(*) from t1 where match(c3) against("ii") limit 1;
+show status like 'groonga_fast_order_limit';
+
+drop table t1;
+
+--source suite/groonga_include/groonga_deinit.inc




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