[Groonga-commit] groonga/groonga at dedca43 [master] select slices: support match_columns, query, query_flags and query_expander

Back to archive index

Kouhei Sutou null+****@clear*****
Tue Sep 6 12:52:32 JST 2016


Kouhei Sutou	2016-09-06 12:52:32 +0900 (Tue, 06 Sep 2016)

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

  Message:
    select slices: support match_columns, query, query_flags and query_expander

  Added files:
    test/command/suite/select/slices/match_columns.expected
    test/command/suite/select/slices/match_columns.test
    test/command/suite/select/slices/output_columns.expected
    test/command/suite/select/slices/output_columns.test
    test/command/suite/select/slices/query_expander.expected
    test/command/suite/select/slices/query_expander.test
  Copied files:
    test/command/suite/select/slices/filter.expected
      (from test/command/suite/select/slices/full.expected)
    test/command/suite/select/slices/filter.test
      (from test/command/suite/select/slices/full.test)
    test/command/suite/select/slices/query.expected
      (from test/command/suite/select/slices/full.expected)
    test/command/suite/select/slices/query.test
      (from test/command/suite/select/slices/full.test)
    test/command/suite/select/slices/query_flags.expected
      (from test/command/suite/select/slices/full.expected)
    test/command/suite/select/slices/query_flags.test
      (from test/command/suite/select/slices/full.test)
  Modified files:
    lib/proc/proc_select.c
  Renamed files:
    test/command/suite/select/slices/sort_keys.expected
      (from test/command/suite/select/slices/full.expected)
    test/command/suite/select/slices/sort_keys.test
      (from test/command/suite/select/slices/full.test)

  Modified: lib/proc/proc_select.c (+344 -280)
===================================================================
--- lib/proc/proc_select.c    2016-09-06 12:32:06 +0900 (266aa96)
+++ lib/proc/proc_select.c    2016-09-06 12:52:32 +0900 (59ad99e)
@@ -77,8 +77,21 @@ typedef struct {
 } grn_columns;
 
 typedef struct {
-  grn_select_string label;
+  grn_select_string match_columns;
+  grn_select_string query;
+  grn_select_string query_expander;
+  grn_select_string query_flags;
   grn_select_string filter;
+  struct {
+    grn_obj *match_columns;
+    grn_obj *expression;
+  } condition;
+  grn_obj *filtered;
+} grn_filter_data;
+
+typedef struct {
+  grn_select_string label;
+  grn_filter_data filter;
   grn_select_string sort_keys;
   grn_select_string output_columns;
   int offset;
@@ -109,9 +122,7 @@ typedef struct _grn_select_output_formatter grn_select_output_formatter;
 typedef struct {
   /* inputs */
   grn_select_string table;
-  grn_select_string match_columns;
-  grn_select_string query;
-  grn_select_string filter;
+  grn_filter_data filter;
   grn_select_string scorer;
   grn_select_string sort_keys;
   grn_select_string output_columns;
@@ -122,8 +133,6 @@ typedef struct {
   grn_hash *drilldowns;
   grn_select_string cache;
   grn_select_string match_escalation_threshold;
-  grn_select_string query_expander;
-  grn_select_string query_flags;
   grn_select_string adjuster;
   grn_columns columns;
 
@@ -135,10 +144,6 @@ typedef struct {
     grn_obj *sorted;
     grn_obj *output;
   } tables;
-  struct {
-    grn_obj *match_columns;
-    grn_obj *expression;
-  } condition;
   uint16_t cacheable;
   uint16_t taintable;
   struct {
@@ -266,6 +271,24 @@ grn_column_stage_name(grn_column_stage stage)
   }
 }
 
+static void
+grn_select_string_lstrip(grn_ctx *ctx,
+                         grn_select_string *string)
+{
+  const char *end;
+  int space_len;
+
+  end = string->value + string->length;
+  while (string->value < end) {
+    space_len = grn_isspace(string->value, ctx->encoding);
+    if (space_len == 0) {
+      break;
+    }
+    string->value += space_len;
+    string->length -= space_len;
+  }
+}
+
 static grn_bool
 grn_column_data_init(grn_ctx *ctx,
                      const char *label,
@@ -608,6 +631,199 @@ grn_columns_fill(grn_ctx *ctx,
 }
 
 static void
+grn_filter_data_init(grn_ctx *ctx, grn_filter_data *data)
+{
+  GRN_SELECT_INIT_STRING(data->match_columns);
+  GRN_SELECT_INIT_STRING(data->query);
+  GRN_SELECT_INIT_STRING(data->query_expander);
+  GRN_SELECT_INIT_STRING(data->query_flags);
+  GRN_SELECT_INIT_STRING(data->filter);
+  data->condition.match_columns = NULL;
+  data->condition.expression = NULL;
+  data->filtered = NULL;
+}
+
+static void
+grn_filter_data_fin(grn_ctx *ctx, grn_filter_data *data)
+{
+  if (data->filtered) {
+    grn_obj_unlink(ctx, data->filtered);
+  }
+  if (data->condition.expression) {
+    grn_obj_close(ctx, data->condition.expression);
+  }
+  if (data->condition.match_columns) {
+    grn_obj_close(ctx, data->condition.match_columns);
+  }
+}
+
+static void
+grn_filter_data_fill(grn_ctx *ctx,
+                     grn_filter_data *data,
+                     grn_obj *match_columns,
+                     grn_obj *query,
+                     grn_obj *query_expander,
+                     grn_obj *query_flags,
+                     grn_obj *filter)
+{
+  GRN_SELECT_FILL_STRING(data->match_columns, match_columns);
+  GRN_SELECT_FILL_STRING(data->query, query);
+  GRN_SELECT_FILL_STRING(data->query_expander, query_expander);
+  GRN_SELECT_FILL_STRING(data->query_flags, query_flags);
+  GRN_SELECT_FILL_STRING(data->filter, filter);
+}
+
+static grn_bool
+grn_filter_data_execute(grn_ctx *ctx,
+                        grn_filter_data *data,
+                        grn_obj *table,
+                        const char *tag)
+{
+  grn_obj *variable;
+
+  if (data->query.length == 0 && data->filter.length == 0) {
+    return GRN_TRUE;
+  }
+
+  GRN_EXPR_CREATE_FOR_QUERY(ctx,
+                            table,
+                            data->condition.expression,
+                            variable);
+  if (!data->condition.expression) {
+    grn_rc rc = ctx->rc;
+    if (rc == GRN_SUCCESS) {
+      rc = GRN_NO_MEMORY_AVAILABLE;
+    }
+    GRN_PLUGIN_ERROR(ctx,
+                     rc,
+                     "%s[condition] "
+                     "failed to create expression for condition: %s",
+                     tag,
+                     ctx->errbuf);
+    return GRN_FALSE;
+  }
+
+  if (data->query.length > 0) {
+    if (data->match_columns.length > 0) {
+      GRN_EXPR_CREATE_FOR_QUERY(ctx,
+                                table,
+                                data->condition.match_columns,
+                                variable);
+      if (!data->condition.match_columns) {
+        grn_rc rc = ctx->rc;
+        if (rc == GRN_SUCCESS) {
+          rc = GRN_NO_MEMORY_AVAILABLE;
+        }
+        GRN_PLUGIN_ERROR(ctx,
+                         rc,
+                         "%s[match_columns] "
+                         "failed to create expression for match columns: "
+                         "<%.*s>: %s",
+                         tag,
+                         (int)(data->match_columns.length),
+                         data->match_columns.value,
+                         ctx->errbuf);
+        return GRN_FALSE;
+      }
+
+      grn_expr_parse(ctx,
+                     data->condition.match_columns,
+                     data->match_columns.value,
+                     data->match_columns.length,
+                     NULL, GRN_OP_MATCH, GRN_OP_AND,
+                     GRN_EXPR_SYNTAX_SCRIPT);
+      if (ctx->rc != GRN_SUCCESS) {
+        return GRN_FALSE;
+      }
+    }
+
+    {
+      grn_expr_flags flags;
+      grn_obj query_expander_buf;
+      const char *query = data->query.value;
+      unsigned int query_len = data->query.length;
+
+      flags = GRN_EXPR_SYNTAX_QUERY;
+      if (data->query_flags.length) {
+        flags |= grn_proc_expr_query_flags_parse(ctx,
+                                                 data->query_flags.value,
+                                                 data->query_flags.length,
+                                                 tag);
+        if (ctx->rc != GRN_SUCCESS) {
+          return GRN_FALSE;
+        }
+      } else {
+        flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
+      }
+
+      GRN_TEXT_INIT(&query_expander_buf, 0);
+      if (data->query_expander.length > 0) {
+        grn_rc rc;
+        rc = grn_proc_syntax_expand_query(ctx,
+                                          data->query.value,
+                                          data->query.length,
+                                          flags,
+                                          data->query_expander.value,
+                                          data->query_expander.length,
+                                          &query_expander_buf,
+                                          tag);
+        if (rc == GRN_SUCCESS) {
+          query = GRN_TEXT_VALUE(&query_expander_buf);
+          query_len = GRN_TEXT_LEN(&query_expander_buf);
+        } else {
+          GRN_OBJ_FIN(ctx, &query_expander_buf);
+          return GRN_FALSE;
+        }
+      }
+
+      grn_expr_parse(ctx,
+                     data->condition.expression,
+                     query,
+                     query_len,
+                     data->condition.match_columns,
+                     GRN_OP_MATCH,
+                     GRN_OP_AND,
+                     flags);
+      GRN_OBJ_FIN(ctx, &query_expander_buf);
+
+      if (ctx->rc != GRN_SUCCESS) {
+        return GRN_FALSE;
+      }
+    }
+  }
+
+  if (data->filter.length > 0) {
+    grn_expr_parse(ctx,
+                   data->condition.expression,
+                   data->filter.value,
+                   data->filter.length,
+                   data->condition.match_columns,
+                   GRN_OP_MATCH,
+                   GRN_OP_AND,
+                   GRN_EXPR_SYNTAX_SCRIPT);
+    if (ctx->rc != GRN_SUCCESS) {
+      return GRN_FALSE;
+    }
+
+    if (data->query.length > 0) {
+      grn_expr_append_op(ctx, data->condition.expression, GRN_OP_AND, 2);
+    }
+
+    if (ctx->rc != GRN_SUCCESS) {
+      return GRN_FALSE;
+    }
+  }
+
+  data->filtered = grn_table_select(ctx,
+                                    table,
+                                    data->condition.expression,
+                                    NULL,
+                                    GRN_OP_OR);
+
+  return ctx->rc == GRN_SUCCESS;
+}
+
+static void
 grn_slice_data_init(grn_ctx *ctx,
                     grn_slice_data *slice,
                     const char *label,
@@ -615,7 +831,7 @@ grn_slice_data_init(grn_ctx *ctx,
 {
   slice->label.value = label;
   slice->label.length = label_len;
-  GRN_SELECT_INIT_STRING(slice->filter);
+  grn_filter_data_init(ctx, &(slice->filter));
   GRN_SELECT_INIT_STRING(slice->sort_keys);
   GRN_SELECT_INIT_STRING(slice->output_columns);
   slice->offset = 0;
@@ -626,21 +842,29 @@ grn_slice_data_init(grn_ctx *ctx,
 static void
 grn_slice_data_fin(grn_ctx *ctx, grn_slice_data *slice)
 {
-  if (slice->table) {
-    grn_obj_unlink(ctx, slice->table);
-  }
+  grn_filter_data_fin(ctx, &(slice->filter));
 }
 
 static void
 grn_slice_data_fill(grn_ctx *ctx,
                     grn_slice_data *slice,
+                    grn_obj *match_columns,
+                    grn_obj *query,
+                    grn_obj *query_expander,
+                    grn_obj *query_flags,
                     grn_obj *filter,
                     grn_obj *sort_keys,
                     grn_obj *output_columns,
                     grn_obj *offset,
                     grn_obj *limit)
 {
-  GRN_SELECT_FILL_STRING(slice->filter, filter);
+  grn_filter_data_fill(ctx,
+                       &(slice->filter),
+                       match_columns,
+                       query,
+                       query_expander,
+                       query_flags,
+                       filter);
 
   GRN_SELECT_FILL_STRING(slice->sort_keys, sort_keys);
 
@@ -1075,7 +1299,7 @@ grn_select_apply_columns(grn_ctx *ctx,
     }
     grn_select_expression_set_condition(ctx,
                                         expression,
-                                        data->condition.expression);
+                                        data->filter.condition.expression);
 
     if (column_data->window.sort_keys.length > 0) {
       grn_window_definition definition;
@@ -1180,151 +1404,28 @@ static grn_bool
 grn_select_filter(grn_ctx *ctx,
                   grn_select_data *data)
 {
-  grn_obj *v;
-
-  if (data->query.length == 0 && data->filter.length == 0) {
-    data->tables.result = data->tables.initial;
-    return GRN_TRUE;
-  }
-
-  GRN_EXPR_CREATE_FOR_QUERY(ctx,
-                            data->tables.initial,
-                            data->condition.expression,
-                            v);
-  if (!data->condition.expression) {
-    GRN_PLUGIN_ERROR(ctx,
-                     GRN_NO_MEMORY_AVAILABLE,
-                     "[select][condition] "
-                     "failed to create expression for condition: %s",
-                     ctx->errbuf);
+  if (!grn_filter_data_execute(ctx,
+                               &(data->filter),
+                               data->tables.initial,
+                               "[select]")) {
     return GRN_FALSE;
   }
 
-  if (data->match_columns.length > 0) {
-    GRN_EXPR_CREATE_FOR_QUERY(ctx,
-                              data->tables.initial,
-                              data->condition.match_columns,
-                              v);
-    if (!data->condition.match_columns) {
-      GRN_PLUGIN_ERROR(ctx,
-                       GRN_NO_MEMORY_AVAILABLE,
-                       "[select][match_columns] "
-                       "failed to create expression for match columns: "
-                       "<%.*s>: %s",
-                       (int)(data->match_columns.length),
-                       data->match_columns.value,
-          ctx->errbuf);
-      return GRN_FALSE;
-    }
-
-    grn_expr_parse(ctx,
-                   data->condition.match_columns,
-                   data->match_columns.value,
-                   data->match_columns.length,
-                   NULL, GRN_OP_MATCH, GRN_OP_AND,
-                   GRN_EXPR_SYNTAX_SCRIPT);
-    if (ctx->rc) {
-      return GRN_FALSE;
-    }
-  }
-
-  if (data->query.length > 0) {
-    grn_expr_flags flags;
-    grn_obj query_expander_buf;
-    const char *query = data->query.value;
-    unsigned int query_len = data->query.length;
-
-    flags = GRN_EXPR_SYNTAX_QUERY;
-    if (data->query_flags.length) {
-      flags |= grn_proc_expr_query_flags_parse(ctx,
-                                               data->query_flags.value,
-                                               data->query_flags.length,
-                                               "[select]");
-      if (ctx->rc) {
-        return GRN_FALSE;
-      }
-    } else {
-      flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
-    }
-
-    GRN_TEXT_INIT(&query_expander_buf, 0);
-    if (data->query_expander.length > 0) {
-      grn_rc rc;
-      rc = grn_proc_syntax_expand_query(ctx,
-                                        data->query.value,
-                                        data->query.length,
-                                        flags,
-                                        data->query_expander.value,
-                                        data->query_expander.length,
-                                        &query_expander_buf,
-                                        "[select]");
-      if (rc == GRN_SUCCESS) {
-        query = GRN_TEXT_VALUE(&query_expander_buf);
-        query_len = GRN_TEXT_LEN(&query_expander_buf);
-      } else {
-        GRN_OBJ_FIN(ctx, &query_expander_buf);
-        return GRN_FALSE;
-      }
-    }
-
-    grn_expr_parse(ctx,
-                   data->condition.expression,
-                   query,
-                   query_len,
-                   data->condition.match_columns,
-                   GRN_OP_MATCH,
-                   GRN_OP_AND,
-                   flags);
-    GRN_OBJ_FIN(ctx, &query_expander_buf);
-
-    if (ctx->rc != GRN_SUCCESS) {
-      return GRN_FALSE;
-    }
+  data->tables.result = data->filter.filtered;
+  if (!data->tables.result) {
+    data->tables.result = data->tables.initial;
   }
 
-  if (data->filter.length > 0) {
-    grn_expr_parse(ctx,
-                   data->condition.expression,
-                   data->filter.value,
-                   data->filter.length,
-                   data->condition.match_columns,
-                   GRN_OP_MATCH,
-                   GRN_OP_AND,
-                   GRN_EXPR_SYNTAX_SCRIPT);
-    if (ctx->rc != GRN_SUCCESS) {
-      return GRN_FALSE;
-    }
-
-    if (data->query.length > 0) {
-      grn_expr_append_op(ctx, data->condition.expression, GRN_OP_AND, 2);
-    }
-
-    if (ctx->rc != GRN_SUCCESS) {
-      return GRN_FALSE;
+  {
+    grn_expr *expression;
+    expression = (grn_expr *)(data->filter.condition.expression);
+    if (expression) {
+      data->cacheable *= expression->cacheable;
+      data->taintable += expression->taintable;
     }
   }
 
-  data->cacheable *= ((grn_expr *)(data->condition.expression))->cacheable;
-  data->taintable += ((grn_expr *)(data->condition.expression))->taintable;
-  /*
-    grn_obj strbuf;
-    GRN_TEXT_INIT(&strbuf, 0);
-    grn_expr_inspect(ctx, &strbuf, cond);
-    GRN_TEXT_PUTC(ctx, &strbuf, '\0');
-    GRN_LOG(ctx, GRN_LOG_NOTICE, "query=(%s)", GRN_TEXT_VALUE(&strbuf));
-    GRN_OBJ_FIN(ctx, &strbuf);
-  */
-  if (ctx->rc != GRN_SUCCESS) {
-    return GRN_FALSE;
-  }
-
-  data->tables.result = grn_table_select(ctx,
-                                         data->tables.initial,
-                                         data->condition.expression,
-                                         NULL,
-                                         GRN_OP_OR);
-
-  return ctx->rc == GRN_SUCCESS;
+  return GRN_TRUE;
 }
 
 static grn_bool
@@ -1684,7 +1785,7 @@ grn_select_output_match_open(grn_ctx *ctx,
                                         data->limit,
                                         data->output_columns.value,
                                         data->output_columns.length,
-                                        data->condition.expression,
+                                        data->filter.condition.expression,
                                         n_additional_elements);
   GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
                 ":", "output(%d)", data->limit);
@@ -1727,66 +1828,28 @@ grn_select_slice_execute(grn_ctx *ctx,
                          grn_obj *table,
                          grn_slice_data *slice)
 {
-  if (slice->filter.length > 0) {
-    grn_obj *expression;
-    grn_obj *record;
-    GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expression, record);
-    if (!expression) {
-      GRN_PLUGIN_ERROR(ctx,
-                       GRN_INVALID_ARGUMENT,
-                       "[select][slices][%.*s][filter] "
-                       "failed to create expression for filter: %s",
-                       (int)(slice->label.length),
-                       slice->label.value,
-                       ctx->errbuf);
-      return GRN_FALSE;
-    }
-    grn_expr_parse(ctx,
-                   expression,
-                   slice->filter.value,
-                   slice->filter.length,
-                   NULL,
-                   GRN_OP_MATCH,
-                   GRN_OP_AND,
-                   GRN_EXPR_SYNTAX_SCRIPT);
-    if (ctx->rc != GRN_SUCCESS) {
-      grn_obj_close(ctx, expression);
-      GRN_PLUGIN_ERROR(ctx,
-                       GRN_INVALID_ARGUMENT,
-                       "[select][slices][%.*s][filter] "
-                       "failed to parse filter: <%.*s>: %s",
-                       (int)(slice->label.length),
-                       slice->label.value,
-                       (int)(slice->filter.length),
-                       slice->filter.value,
-                       ctx->errbuf);
-      return GRN_FALSE;
-    }
-    slice->table = grn_table_select(ctx,
-                                    table,
-                                    expression,
-                                    NULL,
-                                    GRN_OP_OR);
-    if (ctx->rc != GRN_SUCCESS) {
-      grn_obj_close(ctx, expression);
-      if (slice->table) {
-        grn_obj_close(ctx, slice->table);
-        slice->table = NULL;
-      }
-      GRN_PLUGIN_ERROR(ctx,
-                       GRN_INVALID_ARGUMENT,
-                       "[select][slices][%.*s][filter] "
-                       "failed to execute filter: <%.*s>: %s",
-                       (int)(slice->label.length),
-                       slice->label.value,
-                       (int)(slice->filter.length),
-                       slice->filter.value,
-                       ctx->errbuf);
-      return GRN_FALSE;
-    }
-    grn_obj_close(ctx, expression);
+  char tag[GRN_TABLE_MAX_KEY_SIZE];
+  grn_filter_data *filter;
+
+  grn_snprintf(tag, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE,
+               "[select][slices][%.*s]",
+               (int)(slice->label.length),
+               slice->label.value);
+  filter = &(slice->filter);
+  if (filter->query.length == 0 && filter->filter.length == 0) {
+    GRN_PLUGIN_ERROR(ctx,
+                     GRN_INVALID_ARGUMENT,
+                     "%s slice requires query or filter",
+                     tag);
+    return GRN_FALSE;
   }
 
+  if (!grn_filter_data_execute(ctx, filter, table, tag)) {
+    return GRN_FALSE;
+  }
+
+  slice->table = filter->filtered;
+
   return GRN_TRUE;
 }
 
@@ -1892,7 +1955,7 @@ grn_select_output_slices(grn_ctx *ctx,
                                               limit,
                                               slice->output_columns.value,
                                               slice->output_columns.length,
-                                              data->condition.expression)) {
+                                              slice->filter.condition.expression)) {
             succeeded = GRN_FALSE;
           }
           grn_obj_unlink(ctx, sorted);
@@ -1907,7 +1970,7 @@ grn_select_output_slices(grn_ctx *ctx,
                                           limit,
                                           slice->output_columns.value,
                                           slice->output_columns.length,
-                                          data->condition.expression)) {
+                                          slice->filter.condition.expression)) {
         succeeded = GRN_FALSE;
       }
     }
@@ -2477,7 +2540,7 @@ grn_select_output_drilldowns(grn_ctx *ctx,
                                               limit,
                                               drilldown->output_columns.value,
                                               drilldown->output_columns.length,
-                                              data->condition.expression)) {
+                                              data->filter.condition.expression)) {
             succeeded = GRN_FALSE;
           }
           grn_obj_unlink(ctx, sorted);
@@ -2492,7 +2555,7 @@ grn_select_output_drilldowns(grn_ctx *ctx,
                                           limit,
                                           drilldown->output_columns.value,
                                           drilldown->output_columns.length,
-                                          data->condition.expression)) {
+                                          data->filter.condition.expression)) {
         succeeded = GRN_FALSE;
       }
     }
@@ -2741,30 +2804,19 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
 
   data->output.n_elements = 0;
 
-  {
-    const char *query_end = data->query.value + data->query.length;
-    int space_len;
-    while (data->query.value < query_end) {
-      space_len = grn_isspace(data->query.value, ctx->encoding);
-      if (space_len == 0) {
-        break;
-      }
-      data->query.value += space_len;
-      data->query.length -= space_len;
-    }
-  }
+  grn_select_string_lstrip(ctx, &(data->filter.query));
 
   cache_key_size =
     data->table.length + 1 +
-    data->match_columns.length + 1 +
-    data->query.length + 1 +
-    data->filter.length + 1 +
+    data->filter.match_columns.length + 1 +
+    data->filter.query.length + 1 +
+    data->filter.filter.length + 1 +
     data->scorer.length + 1 +
     data->sort_keys.length + 1 +
     data->output_columns.length + 1 +
     data->match_escalation_threshold.length + 1 +
-    data->query_expander.length + 1 +
-    data->query_flags.length + 1 +
+    data->filter.query_expander.length + 1 +
+    data->filter.query_flags.length + 1 +
     data->adjuster.length + 1 +
     sizeof(grn_content_type) +
     sizeof(int) * 2 +
@@ -2774,8 +2826,13 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
     GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
       grn_slice_data *slice;
       grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
+      grn_select_string_lstrip(ctx, &(slice->filter.query));
       cache_key_size +=
-        slice->filter.length + 1 +
+        slice->filter.match_columns.length + 1 +
+        slice->filter.query.length + 1 +
+        slice->filter.query_expander.length + 1 +
+        slice->filter.query_flags.length + 1 +
+        slice->filter.filter.length + 1 +
         slice->sort_keys.length + 1 +
         slice->output_columns.length + 1 +
         slice->label.length + 1 +
@@ -2814,9 +2871,9 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
     *cp++ = '\0'
 
     PUT_CACHE_KEY(data->table);
-    PUT_CACHE_KEY(data->match_columns);
-    PUT_CACHE_KEY(data->query);
-    PUT_CACHE_KEY(data->filter);
+    PUT_CACHE_KEY(data->filter.match_columns);
+    PUT_CACHE_KEY(data->filter.query);
+    PUT_CACHE_KEY(data->filter.filter);
     PUT_CACHE_KEY(data->scorer);
     PUT_CACHE_KEY(data->sort_keys);
     PUT_CACHE_KEY(data->output_columns);
@@ -2824,7 +2881,11 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
       GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
         grn_slice_data *slice;
         grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
-        PUT_CACHE_KEY(slice->filter);
+        PUT_CACHE_KEY(slice->filter.match_columns);
+        PUT_CACHE_KEY(slice->filter.query);
+        PUT_CACHE_KEY(slice->filter.query_expander);
+        PUT_CACHE_KEY(slice->filter.query_flags);
+        PUT_CACHE_KEY(slice->filter.filter);
         PUT_CACHE_KEY(slice->sort_keys);
         PUT_CACHE_KEY(slice->output_columns);
         PUT_CACHE_KEY(slice->label);
@@ -2864,8 +2925,8 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
     }
 #undef PUT_CACHE_KEY_DRILLDOWN
     PUT_CACHE_KEY(data->match_escalation_threshold);
-    PUT_CACHE_KEY(data->query_expander);
-    PUT_CACHE_KEY(data->query_flags);
+    PUT_CACHE_KEY(data->filter.query_expander);
+    PUT_CACHE_KEY(data->filter.query_flags);
     PUT_CACHE_KEY(data->adjuster);
     grn_memcpy(cp, &output_type, sizeof(grn_content_type));
     cp += sizeof(grn_content_type);
@@ -2914,11 +2975,12 @@ grn_select(grn_ctx *ctx, grn_select_data *data)
   }
 
   {
-    if (data->filter.length > 0 && (data->filter.value[0] == '?') &&
+    if (data->filter.filter.length > 0 &&
+        (data->filter.filter.value[0] == '?') &&
         (ctx->impl->output.type == GRN_CONTENT_JSON)) {
       ctx->rc = grn_ts_select(ctx, data->tables.target,
-                              data->filter.value + 1,
-                              data->filter.length - 1,
+                              data->filter.filter.value + 1,
+                              data->filter.filter.length - 1,
                               data->scorer.value,
                               data->scorer.length,
                               data->sort_keys.value,
@@ -3117,6 +3179,10 @@ grn_select_data_fill_slices(grn_ctx *ctx,
     grn_slice_data *slice;
     char slice_label[GRN_TABLE_MAX_KEY_SIZE];
     char key_name[GRN_TABLE_MAX_KEY_SIZE];
+    grn_obj *match_columns;
+    grn_obj *query;
+    grn_obj *query_expander;
+    grn_obj *query_flags;
     grn_obj *filter;
     grn_obj *sort_keys;
     grn_obj *output_columns;
@@ -3139,6 +3205,10 @@ grn_select_data_fill_slices(grn_ctx *ctx,
                    "%s%s", slice_label, #name);                         \
       name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1);
 
+      GET_VAR(match_columns);
+      GET_VAR(query);
+      GET_VAR(query_expander);
+      GET_VAR(query_flags);
       GET_VAR(filter);
       GET_VAR(sort_keys);
       GET_VAR(output_columns);
@@ -3149,6 +3219,10 @@ grn_select_data_fill_slices(grn_ctx *ctx,
 
       grn_slice_data_fill(ctx,
                           slice,
+                          match_columns,
+                          query,
+                          query_expander,
+                          query_flags,
                           filter,
                           sort_keys,
                           output_columns,
@@ -3372,15 +3446,13 @@ command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data
   grn_select_data data;
 
   grn_columns_init(ctx, &(data.columns));
+  grn_filter_data_init(ctx, &(data.filter));
 
   data.tables.target = NULL;
   data.tables.initial = NULL;
   data.tables.result = NULL;
   data.tables.sorted = NULL;
 
-  data.condition.match_columns = NULL;
-  data.condition.expression = NULL;
-
   data.slices = NULL;
   grn_drilldown_data_init(ctx, &(data.drilldown), NULL, 0);
   data.drilldowns = NULL;
@@ -3388,18 +3460,27 @@ command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data
   data.table.value = grn_plugin_proc_get_var_string(ctx, user_data,
                                                     "table", -1,
                                                     &(data.table.length));
-  data.match_columns.value =
-    grn_plugin_proc_get_var_string(ctx, user_data,
-                                   "match_columns", -1,
-                                   &(data.match_columns.length));
-  data.query.value =
-    grn_plugin_proc_get_var_string(ctx, user_data,
-                                   "query", -1,
-                                   &(data.query.length));
-  data.filter.value =
-    grn_plugin_proc_get_var_string(ctx, user_data,
-                                   "filter", -1,
-                                   &(data.filter.length));
+#define GET_VAR(name)                                           \
+  grn_plugin_proc_get_var(ctx, user_data, name, strlen(name))
+
+  {
+    grn_obj *query_expander;
+
+    query_expander = GET_VAR("query_expander");
+    if (GRN_TEXT_LEN(query_expander) == 0) {
+      query_expander = GET_VAR("query_expansion");
+    }
+
+    grn_filter_data_fill(ctx,
+                         &(data.filter),
+                         GET_VAR("match_columns"),
+                         GET_VAR("query"),
+                         query_expander,
+                         GET_VAR("query_flags"),
+                         GET_VAR("filter"));
+  }
+#undef GET_VAR
+
   data.scorer.value =
     grn_plugin_proc_get_var_string(ctx, user_data,
                                    "scorer", -1,
@@ -3437,20 +3518,6 @@ command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data
     grn_plugin_proc_get_var_string(ctx, user_data,
                                    "match_escalation_threshold", -1,
                                    &(data.match_escalation_threshold.length));
-  data.query_expander.value =
-    grn_plugin_proc_get_var_string(ctx, user_data,
-                                   "query_expander", -1,
-                                   &(data.query_expander.length));
-  if (!data.query_expander.value) {
-    data.query_expander.value =
-      grn_plugin_proc_get_var_string(ctx, user_data,
-                                     "query_expansion", -1,
-                                     &(data.query_expander.length));
-  }
-  data.query_flags.value =
-    grn_plugin_proc_get_var_string(ctx, user_data,
-                                   "query_flags", -1,
-                                   &(data.query_flags.length));
 
   data.adjuster.value =
     grn_plugin_proc_get_var_string(ctx, user_data,
@@ -3497,18 +3564,15 @@ exit :
     grn_hash_close(ctx, data.slices);
   }
 
-  if (data.condition.expression) {
-    grn_obj_unlink(ctx, data.condition.expression);
-  }
-
-  if (data.condition.match_columns) {
-    grn_obj_unlink(ctx, data.condition.match_columns);
-  }
-
   if (data.tables.sorted) {
     grn_obj_unlink(ctx, data.tables.sorted);
   }
 
+  if (data.tables.result == data.filter.filtered) {
+    data.tables.result = NULL;
+  }
+  grn_filter_data_fin(ctx, &(data.filter));
+
   if (data.tables.result &&
       data.tables.result != data.tables.initial &&
       data.tables.result != data.tables.target) {

  Copied: test/command/suite/select/slices/filter.expected (+15 -3) 84%
===================================================================
--- test/command/suite/select/slices/full.expected    2016-09-06 12:32:06 +0900 (3f92798)
+++ test/command/suite/select/slices/filter.expected    2016-09-06 12:52:32 +0900 (536504f)
@@ -14,7 +14,7 @@ load --table Memos
 {"_key": "Rroonga is fast!", "date": "2016-05-19 12:00:03", "tag": "Rroonga"}
 ]
 [[0,0.0,0.0],4]
-select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].sort_keys 'date'   --slices[groonga].output_columns '_key, date'
+select Memos   --slices[groonga].filter 'tag == "Groonga"'
 [
   [
     0,
@@ -76,21 +76,33 @@ select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].s
         ],
         [
           [
+            "_id",
+            "UInt32"
+          ],
+          [
             "_key",
             "ShortText"
           ],
           [
             "date",
             "Time"
+          ],
+          [
+            "tag",
+            "Tags"
           ]
         ],
         [
+          1,
           "Groonga is fast!",
-          1463626800.0
+          1463626800.0,
+          "Groonga"
         ],
         [
+          3,
           "Groonga sticker!",
-          1463626802.0
+          1463626802.0,
+          "Groonga"
         ]
       ]
     }

  Copied: test/command/suite/select/slices/filter.test (+1 -3) 79%
===================================================================
--- test/command/suite/select/slices/full.test    2016-09-06 12:32:06 +0900 (50735a3)
+++ test/command/suite/select/slices/filter.test    2016-09-06 12:52:32 +0900 (9603c82)
@@ -13,6 +13,4 @@ load --table Memos
 ]
 
 select Memos \
-  --slices[groonga].filter 'tag == "Groonga"' \
-  --slices[groonga].sort_keys 'date' \
-  --slices[groonga].output_columns '_key, date'
+  --slices[groonga].filter 'tag == "Groonga"'

  Added: test/command/suite/select/slices/match_columns.expected (+80 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/match_columns.expected    2016-09-06 12:52:32 +0900 (b45e359)
@@ -0,0 +1,80 @@
+table_create Memos TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+table_create Terms TABLE_PAT_KEY ShortText   --default_tokenizer TokenBigram   --normalizer NormalizerAuto
+[[0,0.0,0.0],true]
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+[[0,0.0,0.0],true]
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"}
+]
+[[0,0.0,0.0],4]
+select Memos   --slices[groonga].match_columns '_key'   --slices[groonga].query 'Groonga'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        4
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        1,
+        "Groonga is fast!"
+      ],
+      [
+        2,
+        "Mroonga is fast!"
+      ],
+      [
+        3,
+        "Groonga sticker!"
+      ],
+      [
+        4,
+        "Rroonga is fast!"
+      ]
+    ],
+    {
+      "groonga": [
+        [
+          2
+        ],
+        [
+          [
+            "_id",
+            "UInt32"
+          ],
+          [
+            "_key",
+            "ShortText"
+          ]
+        ],
+        [
+          1,
+          "Groonga is fast!"
+        ],
+        [
+          3,
+          "Groonga sticker!"
+        ]
+      ]
+    }
+  ]
+]

  Added: test/command/suite/select/slices/match_columns.test (+18 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/match_columns.test    2016-09-06 12:52:32 +0900 (f52d8a5)
@@ -0,0 +1,18 @@
+table_create Memos TABLE_HASH_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"}
+]
+
+select Memos \
+  --slices[groonga].match_columns '_key' \
+  --slices[groonga].query 'Groonga'

  Added: test/command/suite/select/slices/output_columns.expected (+80 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/output_columns.expected    2016-09-06 12:52:32 +0900 (aa0b6d4)
@@ -0,0 +1,80 @@
+table_create Memos TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+table_create Terms TABLE_PAT_KEY ShortText   --default_tokenizer TokenBigram   --normalizer NormalizerAuto
+[[0,0.0,0.0],true]
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+[[0,0.0,0.0],true]
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"}
+]
+[[0,0.0,0.0],4]
+select Memos   --slices[groonga].match_columns '_key'   --slices[groonga].query 'Groonga'   --slices[groonga].output_columns '_key, highlight_html(_key)'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        4
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        1,
+        "Groonga is fast!"
+      ],
+      [
+        2,
+        "Mroonga is fast!"
+      ],
+      [
+        3,
+        "Groonga sticker!"
+      ],
+      [
+        4,
+        "Rroonga is fast!"
+      ]
+    ],
+    {
+      "groonga": [
+        [
+          2
+        ],
+        [
+          [
+            "_key",
+            "ShortText"
+          ],
+          [
+            "highlight_html",
+            null
+          ]
+        ],
+        [
+          "Groonga is fast!",
+          "<span class=\"keyword\">Groonga</span> is fast!"
+        ],
+        [
+          "Groonga sticker!",
+          "<span class=\"keyword\">Groonga</span> sticker!"
+        ]
+      ]
+    }
+  ]
+]

  Added: test/command/suite/select/slices/output_columns.test (+19 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/output_columns.test    2016-09-06 12:52:32 +0900 (b7ecc3e)
@@ -0,0 +1,19 @@
+table_create Memos TABLE_HASH_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"}
+]
+
+select Memos \
+  --slices[groonga].match_columns '_key' \
+  --slices[groonga].query 'Groonga' \
+  --slices[groonga].output_columns '_key, highlight_html(_key)'

  Copied: test/command/suite/select/slices/query.expected (+15 -3) 84%
===================================================================
--- test/command/suite/select/slices/full.expected    2016-09-06 12:32:06 +0900 (3f92798)
+++ test/command/suite/select/slices/query.expected    2016-09-06 12:52:32 +0900 (2fe4f7c)
@@ -14,7 +14,7 @@ load --table Memos
 {"_key": "Rroonga is fast!", "date": "2016-05-19 12:00:03", "tag": "Rroonga"}
 ]
 [[0,0.0,0.0],4]
-select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].sort_keys 'date'   --slices[groonga].output_columns '_key, date'
+select Memos   --slices[groonga].query 'tag:Groonga'
 [
   [
     0,
@@ -76,21 +76,33 @@ select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].s
         ],
         [
           [
+            "_id",
+            "UInt32"
+          ],
+          [
             "_key",
             "ShortText"
           ],
           [
             "date",
             "Time"
+          ],
+          [
+            "tag",
+            "Tags"
           ]
         ],
         [
+          1,
           "Groonga is fast!",
-          1463626800.0
+          1463626800.0,
+          "Groonga"
         ],
         [
+          3,
           "Groonga sticker!",
-          1463626802.0
+          1463626802.0,
+          "Groonga"
         ]
       ]
     }

  Copied: test/command/suite/select/slices/query.test (+1 -3) 79%
===================================================================
--- test/command/suite/select/slices/full.test    2016-09-06 12:32:06 +0900 (50735a3)
+++ test/command/suite/select/slices/query.test    2016-09-06 12:52:32 +0900 (dec6459)
@@ -13,6 +13,4 @@ load --table Memos
 ]
 
 select Memos \
-  --slices[groonga].filter 'tag == "Groonga"' \
-  --slices[groonga].sort_keys 'date' \
-  --slices[groonga].output_columns '_key, date'
+  --slices[groonga].query 'tag:Groonga'

  Added: test/command/suite/select/slices/query_expander.expected (+100 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/query_expander.expected    2016-09-06 12:52:32 +0900 (e92e85e)
@@ -0,0 +1,100 @@
+table_create Memos TABLE_HASH_KEY ShortText
+[[0,0.0,0.0],true]
+table_create Terms TABLE_PAT_KEY ShortText   --default_tokenizer TokenBigram   --normalizer NormalizerAuto
+[[0,0.0,0.0],true]
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+[[0,0.0,0.0],true]
+table_create Synonyms TABLE_PAT_KEY ShortText --normalizer NormalizerAuto
+[[0,0.0,0.0],true]
+column_create Synonyms words COLUMN_VECTOR ShortText
+[[0,0.0,0.0],true]
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"},
+{"_key": "Ruby is good!"},
+{"_key": "Ruby and Groonga combination is good!"}
+]
+[[0,0.0,0.0],6]
+load --table Synonyms
+[
+["_key", "words"],
+["Rroonga", ["Rroonga", "Ruby Groonga"]]
+]
+[[0,0.0,0.0],1]
+select Memos   --slices[rroonga].match_columns '_key'   --slices[rroonga].query 'Rroonga'   --slices[rroonga].query_expander Synonyms.words
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        6
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        1,
+        "Groonga is fast!"
+      ],
+      [
+        2,
+        "Mroonga is fast!"
+      ],
+      [
+        3,
+        "Groonga sticker!"
+      ],
+      [
+        4,
+        "Rroonga is fast!"
+      ],
+      [
+        5,
+        "Ruby is good!"
+      ],
+      [
+        6,
+        "Ruby and Groonga combination is good!"
+      ]
+    ],
+    {
+      "rroonga": [
+        [
+          2
+        ],
+        [
+          [
+            "_id",
+            "UInt32"
+          ],
+          [
+            "_key",
+            "ShortText"
+          ]
+        ],
+        [
+          4,
+          "Rroonga is fast!"
+        ],
+        [
+          6,
+          "Ruby and Groonga combination is good!"
+        ]
+      ]
+    }
+  ]
+]

  Added: test/command/suite/select/slices/query_expander.test (+30 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/slices/query_expander.test    2016-09-06 12:52:32 +0900 (70a50fe)
@@ -0,0 +1,30 @@
+table_create Memos TABLE_HASH_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms memos_key COLUMN_INDEX|WITH_POSITION Memos _key
+
+table_create Synonyms TABLE_PAT_KEY ShortText --normalizer NormalizerAuto
+column_create Synonyms words COLUMN_VECTOR ShortText
+
+load --table Memos
+[
+{"_key": "Groonga is fast!"},
+{"_key": "Mroonga is fast!"},
+{"_key": "Groonga sticker!"},
+{"_key": "Rroonga is fast!"},
+{"_key": "Ruby is good!"},
+{"_key": "Ruby and Groonga combination is good!"}
+]
+
+load --table Synonyms
+[
+["_key", "words"],
+["Rroonga", ["Rroonga", "Ruby Groonga"]]
+]
+
+select Memos \
+  --slices[rroonga].match_columns '_key' \
+  --slices[rroonga].query 'Rroonga' \
+  --slices[rroonga].query_expander Synonyms.words

  Copied: test/command/suite/select/slices/query_flags.expected (+18 -6) 77%
===================================================================
--- test/command/suite/select/slices/full.expected    2016-09-06 12:32:06 +0900 (3f92798)
+++ test/command/suite/select/slices/query_flags.expected    2016-09-06 12:52:32 +0900 (e312c7d)
@@ -14,7 +14,7 @@ load --table Memos
 {"_key": "Rroonga is fast!", "date": "2016-05-19 12:00:03", "tag": "Rroonga"}
 ]
 [[0,0.0,0.0],4]
-select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].sort_keys 'date'   --slices[groonga].output_columns '_key, date'
+select Memos   --slices[groonga].match_columns 'tag'   --slices[groonga].query '-Groonga'   --slices[groonga].query_flags ALLOW_LEADING_NOT
 [
   [
     0,
@@ -70,27 +70,39 @@ select Memos   --slices[groonga].filter 'tag == "Groonga"'   --slices[groonga].s
       ]
     ],
     {
-      "groonga": [
+      "not_groonga": [
         [
           2
         ],
         [
           [
+            "_id",
+            "UInt32"
+          ],
+          [
             "_key",
             "ShortText"
           ],
           [
             "date",
             "Time"
+          ],
+          [
+            "tag",
+            "Tags"
           ]
         ],
         [
-          "Groonga is fast!",
-          1463626800.0
+          2,
+          "Mroonga is fast!",
+          1463626801.0,
+          "Mroonga"
         ],
         [
-          "Groonga sticker!",
-          1463626802.0
+          4,
+          "Rroonga is fast!",
+          1463626803.0,
+          "Rroonga"
         ]
       ]
     }

  Copied: test/command/suite/select/slices/query_flags.test (+3 -3) 78%
===================================================================
--- test/command/suite/select/slices/full.test    2016-09-06 12:32:06 +0900 (50735a3)
+++ test/command/suite/select/slices/query_flags.test    2016-09-06 12:52:32 +0900 (05bf351)
@@ -13,6 +13,6 @@ load --table Memos
 ]
 
 select Memos \
-  --slices[groonga].filter 'tag == "Groonga"' \
-  --slices[groonga].sort_keys 'date' \
-  --slices[groonga].output_columns '_key, date'
+  --slices[not_groonga].match_columns 'tag' \
+  --slices[not_groonga].query '-Groonga' \
+  --slices[not_groonga].query_flags ALLOW_LEADING_NOT

  Renamed: test/command/suite/select/slices/sort_keys.expected (+0 -0) 100%
===================================================================

  Renamed: test/command/suite/select/slices/sort_keys.test (+0 -0) 100%
===================================================================
-------------- next part --------------
HTML����������������������������...
Download 



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