[Groonga-commit] groonga/groonga at 997403f [master] Support "!" in multiple terms

Back to archive index

Kouhei Sutou null+****@clear*****
Wed Feb 15 11:37:52 JST 2017


Kouhei Sutou	2017-02-15 11:37:52 +0900 (Wed, 15 Feb 2017)

  New Revision: 997403f73bc6b7db28ecd520370319120e10fca0
  https://github.com/groonga/groonga/commit/997403f73bc6b7db28ecd520370319120e10fca0

  Message:
    Support "!" in multiple terms
    
    Supported patterns:
    
      * !(column @ "X") && (column @ "Y")
      * (column @ "Y") && !(column @ "X")
      * (column @ "Y") &! !(column @ "X")

  Added files:
    test/command/suite/select/filter/unary_operation/match_and_not_match.expected
    test/command/suite/select/filter/unary_operation/match_and_not_match.test
    test/command/suite/select/filter/unary_operation/match_and_not_not_match.expected
    test/command/suite/select/filter/unary_operation/match_and_not_not_match.test
    test/command/suite/select/filter/unary_operation/not_match_and_match.expected
    test/command/suite/select/filter/unary_operation/not_match_and_match.test
  Modified files:
    lib/expr.c
    lib/mrb/scripts/scan_info_builder.rb
    test/mruby/suite/query_optimizer/index/test_match.rb

  Modified: lib/expr.c (+56 -16)
===================================================================
--- lib/expr.c    2017-02-15 10:51:57 +0900 (2b6a433)
+++ lib/expr.c    2017-02-15 11:37:52 +0900 (1248b01)
@@ -4550,6 +4550,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
   scan_info **sis, *si = NULL;
   grn_expr_code *c, *ce;
   grn_expr *e = (grn_expr *)expr;
+  grn_operator next_code_op;
 
   if (!(var = grn_expr_get_var_by_offset(ctx, expr, 0))) { return NULL; }
   for (stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) {
@@ -4696,8 +4697,17 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
   }
   if (stat || m != o + 1) { return NULL; }
   if (!(sis = GRN_MALLOCN(scan_info *, m + m + o + n_nots))) { return NULL; }
+
+  next_code_op = -1;
   for (i = 0, stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) {
-    switch (c->op) {
+    grn_operator code_op;
+    if (next_code_op == -1) {
+      code_op = c->op;
+    } else {
+      code_op = next_code_op;
+      next_code_op = -1;
+    }
+    switch (code_op) {
     case GRN_OP_MATCH :
     case GRN_OP_NEAR :
     case GRN_OP_NEAR2 :
@@ -4716,7 +4726,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
     case GRN_OP_TERM_EXTRACT :
     case GRN_OP_REGEXP :
       stat = SCAN_START;
-      si->op = c->op;
+      si->op = code_op;
       si->end = c - e->codes;
       sis[i++] = si;
       scan_info_build_match(ctx, si);
@@ -4738,7 +4748,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
         sis[i++] = si;
         si = NULL;
       }
-      if (!put_logical_op(ctx, sis, &i, c->op, c - e->codes)) { return NULL; }
+      if (!put_logical_op(ctx, sis, &i, code_op, c - e->codes)) { return NULL; }
       stat = SCAN_START;
       break;
     case GRN_OP_PUSH :
@@ -4817,7 +4827,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
       if (!si) { SI_ALLOC(si, i, c - e->codes); }
       if ((c->flags & GRN_EXPR_CODE_RELATIONAL_EXPRESSION) || c + 1 == ce) {
         stat = SCAN_START;
-        si->op = c->op;
+        si->op = code_op;
         si->end = c - e->codes;
         sis[i++] = si;
         /* better index resolving framework for functions should be implemented */
@@ -4916,20 +4926,50 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
           last_si->end++;
           break;
         default :
-          if (i == 1 && GRN_BULK_VSIZE(&(last_si->index)) > 0) {
-            SI_ALLOC(si, 0, 0);
-            si->op = GRN_OP_CALL;
-            si->args[si->nargs++] = grn_ctx_get(ctx, "all_records", -1);
-            last_si->logical_op = GRN_OP_AND_NOT;
-            last_si->flags &= ~SCAN_PUSH;
-            sis[i] = sis[i - 1];
-            sis[i - 1] = si;
-            i++;
+          if (i == 1) {
+            if (GRN_BULK_VSIZE(&(last_si->index)) > 0) {
+              scan_info *all_records_si = NULL;
+              SI_ALLOC(all_records_si, 0, 0);
+              all_records_si->op = GRN_OP_CALL;
+              all_records_si->args[all_records_si->nargs++] =
+                grn_ctx_get(ctx, "all_records", -1);
+              last_si->logical_op = GRN_OP_AND_NOT;
+              last_si->flags &= ~SCAN_PUSH;
+              sis[i] = sis[i - 1];
+              sis[i - 1] = all_records_si;
+              i++;
+            } else {
+              if (last_si->op == GRN_OP_EQUAL) {
+                last_si->op = GRN_OP_NOT_EQUAL;
+                last_si->end++;
+              } else {
+                int j;
+                for (j = 0; j < i; j++) {
+                  SI_FREE(sis[j]);
+                }
+                GRN_FREE(sis);
+                return NULL;
+              }
+            }
           } else {
-            if (last_si->op == GRN_OP_EQUAL) {
-              last_si->op = GRN_OP_NOT_EQUAL;
-              last_si->end++;
+            grn_bool valid = GRN_TRUE;
+            grn_expr_code *next_code = c + 1;
+            if (next_code < ce) {
+              switch (next_code->op) {
+              case GRN_OP_AND :
+                next_code_op = GRN_OP_AND_NOT;
+                break;
+              case GRN_OP_AND_NOT :
+                next_code_op = GRN_OP_AND;
+                break;
+              default :
+                valid = GRN_FALSE;
+                break;
+              }
             } else {
+              valid = GRN_FALSE;
+            }
+            if (!valid) {
               int j;
               for (j = 0; j < i; j++) {
                 SI_FREE(sis[j]);

  Modified: lib/mrb/scripts/scan_info_builder.rb (+27 -11)
===================================================================
--- lib/mrb/scripts/scan_info_builder.rb    2017-02-15 10:51:57 +0900 (ec4e476)
+++ lib/mrb/scripts/scan_info_builder.rb    2017-02-15 11:37:52 +0900 (28dcbe2)
@@ -66,11 +66,14 @@ module Groonga
       data = nil
       codes =****@expre*****
       n_codes = codes.size
+      next_code_op = nil
       codes.each_with_index do |code, i|
-        case code.op
+        code_op = (next_code_op || code.op)
+        next_code_op = nil
+        case code_op
         when *RELATION_OPERATORS
           status = Status::START
-          data.op = code.op
+          data.op = code_op
           data.end = i
           data.match_resolve_index
           @data_list << data
@@ -82,7 +85,7 @@ module Groonga
             @data_list << data
             data = nil
           end
-          put_logical_op(code.op, i)
+          put_logical_op(code_op, i)
           # TODO: rescue and return nil
           status = Status::START
         when Operator::PUSH
@@ -124,7 +127,7 @@ module Groonga
           if (code.flags & ExpressionCode::Flags::RELATIONAL_EXPRESSION) != 0 or
               (i + 1) == n_codes
             status = Status::START
-            data.op = code.op
+            data.op = code_op
             data.end = i
             data.call_relational_resolve_indexes
             @data_list << data
@@ -164,14 +167,27 @@ module Groonga
             last_data.op = Operator::EQUAL
             last_data.end += 1
           else
-            if @data_list.size == 1 and not last_data.search_indexes.empty?
-              last_data.logical_op = Operator::AND_NOT
-              last_data.flags &= ~ScanInfo::Flags::PUSH
-              @data_list.unshift(create_all_match_data)
+            if @data_list.size == 1
+              if last_data.search_indexes.empty?
+                if last_data.op == Operator::EQUAL
+                  last_data.op = Operator::NOT_EQUAL
+                  last_data.end += 1
+                else
+                  return nil
+                end
+              else
+                last_data.logical_op = Operator::AND_NOT
+                last_data.flags &= ~ScanInfo::Flags::PUSH
+                @data_list.unshift(create_all_match_data)
+              end
             else
-              if last_data.op == Operator::EQUAL
-                last_data.op = Operator::NOT_EQUAL
-                last_data.end += 1
+              next_code = codes[i + 1]
+              return nil if next_code.nil?
+              case next_code.op
+              when Operator::AND
+                next_code_op = Operator::AND_NOT
+              when Operator::AND_NOT
+                next_code_op = Operator::AND
               else
                 return nil
               end

  Added: test/command/suite/select/filter/unary_operation/match_and_not_match.expected (+56 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/match_and_not_match.expected    2017-02-15 11:37:52 +0900 (4525656)
@@ -0,0 +1,56 @@
+table_create Texts TABLE_PAT_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 texts_key COLUMN_INDEX|WITH_POSITION   Texts _key
+[[0,0.0,0.0],true]
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+[[0,0.0,0.0],3]
+log_level --level info
+[[0,0.0,0.0],true]
+select Texts --filter '(_key @ "Rroonga") && !(_key @ "Groonga")'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        1
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        2,
+        "Rroonga and Ruby"
+      ]
+    ]
+  ]
+]
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Rroonga)
+#|i| n=1 (Rroonga)
+#|i| exact: 2
+#|i| hits=2
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Groonga)
+#|i| n=1 (Groonga)
+#|i| exact: 1
+#|i| hits=1
+log_level --level notice
+[[0,0.0,0.0],true]

  Added: test/command/suite/select/filter/unary_operation/match_and_not_match.test (+20 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/match_and_not_match.test    2017-02-15 11:37:52 +0900 (24e2653)
@@ -0,0 +1,20 @@
+table_create Texts TABLE_PAT_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms texts_key COLUMN_INDEX|WITH_POSITION \
+  Texts _key
+
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+
+log_level --level info
+#@add-important-log-levels info
+select Texts --filter '(_key @ "Rroonga") && !(_key @ "Groonga")'
+#@remove-important-log-levels info
+log_level --level notice

  Added: test/command/suite/select/filter/unary_operation/match_and_not_not_match.expected (+56 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/match_and_not_not_match.expected    2017-02-15 11:37:52 +0900 (2622fa1)
@@ -0,0 +1,56 @@
+table_create Texts TABLE_PAT_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 texts_key COLUMN_INDEX|WITH_POSITION   Texts _key
+[[0,0.0,0.0],true]
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+[[0,0.0,0.0],3]
+log_level --level info
+[[0,0.0,0.0],true]
+select Texts --filter '(_key @ "Rroonga") &! !(_key @ "Groonga")'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        1
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        1,
+        "Groonga and Rroonga"
+      ]
+    ]
+  ]
+]
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Rroonga)
+#|i| n=1 (Rroonga)
+#|i| exact: 2
+#|i| hits=2
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Groonga)
+#|i| n=1 (Groonga)
+#|i| exact: 1
+#|i| hits=1
+log_level --level notice
+[[0,0.0,0.0],true]

  Added: test/command/suite/select/filter/unary_operation/match_and_not_not_match.test (+20 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/match_and_not_not_match.test    2017-02-15 11:37:52 +0900 (814a3ab)
@@ -0,0 +1,20 @@
+table_create Texts TABLE_PAT_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms texts_key COLUMN_INDEX|WITH_POSITION \
+  Texts _key
+
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+
+log_level --level info
+#@add-important-log-levels info
+select Texts --filter '(_key @ "Rroonga") &! !(_key @ "Groonga")'
+#@remove-important-log-levels info
+log_level --level notice

  Added: test/command/suite/select/filter/unary_operation/not_match_and_match.expected (+57 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/not_match_and_match.expected    2017-02-15 11:37:52 +0900 (476899c)
@@ -0,0 +1,57 @@
+table_create Texts TABLE_PAT_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 texts_key COLUMN_INDEX|WITH_POSITION   Texts _key
+[[0,0.0,0.0],true]
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+[[0,0.0,0.0],3]
+log_level --level info
+[[0,0.0,0.0],true]
+select Texts --filter '!(_key @ "Groonga") && (_key @ "Rroonga")'
+[
+  [
+    0,
+    0.0,
+    0.0
+  ],
+  [
+    [
+      [
+        1
+      ],
+      [
+        [
+          "_id",
+          "UInt32"
+        ],
+        [
+          "_key",
+          "ShortText"
+        ]
+      ],
+      [
+        2,
+        "Rroonga and Ruby"
+      ]
+    ]
+  ]
+]
+#|i| [table][select][index][selector][no-index][all_records] <Texts>
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Groonga)
+#|i| n=1 (Groonga)
+#|i| exact: 1
+#|i| hits=1
+#|i| [object][search][index][key][exact] <Terms.texts_key>
+#|i| grn_ii_sel > (Rroonga)
+#|i| n=1 (Rroonga)
+#|i| exact: 1
+#|i| hits=1
+log_level --level notice
+[[0,0.0,0.0],true]

  Added: test/command/suite/select/filter/unary_operation/not_match_and_match.test (+20 -0) 100644
===================================================================
--- /dev/null
+++ test/command/suite/select/filter/unary_operation/not_match_and_match.test    2017-02-15 11:37:52 +0900 (2c7dddf)
@@ -0,0 +1,20 @@
+table_create Texts TABLE_PAT_KEY ShortText
+
+table_create Terms TABLE_PAT_KEY ShortText \
+  --default_tokenizer TokenBigram \
+  --normalizer NormalizerAuto
+column_create Terms texts_key COLUMN_INDEX|WITH_POSITION \
+  Texts _key
+
+load --table Texts
+[
+{"_key": "Groonga and Rroonga"},
+{"_key": "Rroonga and Ruby"},
+{"_key": "Mroonga and Groonga"}
+]
+
+log_level --level info
+#@add-important-log-levels info
+select Texts --filter '!(_key @ "Groonga") && (_key @ "Rroonga")'
+#@remove-important-log-levels info
+log_level --level notice

  Modified: test/mruby/suite/query_optimizer/index/test_match.rb (+56 -0)
===================================================================
--- test/mruby/suite/query_optimizer/index/test_match.rb    2017-02-15 10:51:57 +0900 (a89a4d0)
+++ test/mruby/suite/query_optimizer/index/test_match.rb    2017-02-15 11:37:52 +0900 (102b58c)
@@ -64,4 +64,60 @@ class TestIndexMatch < QueryOptimizerTestCase
   expr:       <0..2>
     DUMP
   end
+
+  def test_not_and_normal
+    assert_equal(<<-DUMP, dump_plan("!(message @ 'Groonga') && (message @ 'Rroonga')"))
+[0]
+  op:         <call>
+  logical_op: <or>
+  args[0]:    <#<proc:function all_records arguments:[]>>
+  expr:       <0..0>
+[1]
+  op:         <match>
+  logical_op: <and_not>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Groonga">
+  expr:       <0..2>
+[2]
+  op:         <match>
+  logical_op: <and>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Rroonga">
+  expr:       <4..6>
+    DUMP
+  end
+
+  def test_normal_and_not
+    assert_equal(<<-DUMP, dump_plan("(message @ 'Rroonga') && !(message @ 'Groonga')"))
+[0]
+  op:         <match>
+  logical_op: <or>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Rroonga">
+  expr:       <0..2>
+[1]
+  op:         <match>
+  logical_op: <and_not>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Groonga">
+  expr:       <3..5>
+    DUMP
+  end
+
+  def test_normal_and_not_not
+    assert_equal(<<-DUMP, dump_plan("(message @ 'Rroonga') &! !(message @ 'Groonga')"))
+[0]
+  op:         <match>
+  logical_op: <or>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Rroonga">
+  expr:       <0..2>
+[1]
+  op:         <match>
+  logical_op: <and>
+  index:      <[#<column:index Terms.Logs_message range:Logs sources:[Logs.message] flags:POSITION>]>
+  query:      <"Groonga">
+  expr:       <3..5>
+    DUMP
+  end
 end
-------------- next part --------------
HTML����������������������������...
Download 



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