[Groonga-commit] groonga/groonga at a725e2b [master] Support index search for "!(column == 29)"

Back to archive index

Kouhei Sutou null+****@clear*****
Wed Feb 15 10:44:30 JST 2017


Kouhei Sutou	2017-02-15 10:44:30 +0900 (Wed, 15 Feb 2017)

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

  Message:
    Support index search for "!(column == 29)"
    
    It's evaluated as "all_records() &! (column == 29)".

  Added files:
    test/mruby/suite/query_optimizer/index/test_compare.rb
    test/mruby/suite/query_optimizer/no_index/test_compare.rb
  Modified files:
    lib/expr.c
    lib/mrb/scripts/scan_info_builder.rb
    test/command/suite/select/filter/unary_operation/not_equal.expected

  Modified: lib/expr.c (+29 -11)
===================================================================
--- lib/expr.c    2017-02-15 10:37:00 +0900 (f612215)
+++ lib/expr.c    2017-02-15 10:44:30 +0900 (2b6a433)
@@ -4546,6 +4546,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
   grn_obj *var;
   scan_stat stat;
   int i, m = 0, o = 0;
+  int n_nots = 0;
   scan_info **sis, *si = NULL;
   grn_expr_code *c, *ce;
   grn_expr *e = (grn_expr *)expr;
@@ -4686,6 +4687,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
       }
       break;
     case GRN_OP_NOT :
+      n_nots++;
       break;
     default :
       return NULL;
@@ -4693,7 +4695,7 @@ 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))) { return NULL; }
+  if (!(sis = GRN_MALLOCN(scan_info *, m + m + o + n_nots))) { return NULL; }
   for (i = 0, stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) {
     switch (c->op) {
     case GRN_OP_MATCH :
@@ -4895,33 +4897,49 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n,
         switch (last_si->op) {
         case GRN_OP_LESS :
           last_si->op = GRN_OP_GREATER_EQUAL;
+          last_si->end++;
           break;
         case GRN_OP_LESS_EQUAL :
           last_si->op = GRN_OP_GREATER;
+          last_si->end++;
           break;
         case GRN_OP_GREATER :
           last_si->op = GRN_OP_LESS_EQUAL;
+          last_si->end++;
           break;
         case GRN_OP_GREATER_EQUAL :
           last_si->op = GRN_OP_LESS;
-          break;
-        case GRN_OP_EQUAL :
-          last_si->op = GRN_OP_NOT_EQUAL;
+          last_si->end++;
           break;
         case GRN_OP_NOT_EQUAL :
           last_si->op = GRN_OP_EQUAL;
+          last_si->end++;
           break;
         default :
-          {
-            int j;
-            for (j = 0; j < i; j++) {
-              SI_FREE(sis[j]);
+          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++;
+          } 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;
             }
-            GRN_FREE(sis);
           }
-          return NULL;
+          break;
         }
-        last_si->end++;
       }
       break;
     default :

  Modified: lib/mrb/scripts/scan_info_builder.rb (+28 -4)
===================================================================
--- lib/mrb/scripts/scan_info_builder.rb    2017-02-15 10:37:00 +0900 (f58bfa2)
+++ lib/mrb/scripts/scan_info_builder.rb    2017-02-15 10:44:30 +0900 (ec4e476)
@@ -150,20 +150,33 @@ module Groonga
           case last_data.op
           when Operator::LESS
             last_data.op = Operator::GREATER_EQUAL
+            last_data.end += 1
           when Operator::LESS_EQUAL
             last_data.op = Operator::GREATER
+            last_data.end += 1
           when Operator::GREATER
             last_data.op = Operator::LESS_EQUAL
+            last_data.end += 1
           when Operator::GREATER_EQUAL
             last_data.op = Operator::LESS
-          when Operator::EQUAL
-            last_data.op = Operator::NOT_EQUAL
+            last_data.end += 1
           when Operator::NOT_EQUAL
             last_data.op = Operator::EQUAL
+            last_data.end += 1
           else
-            return nil
+            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)
+            else
+              if last_data.op == Operator::EQUAL
+                last_data.op = Operator::NOT_EQUAL
+                last_data.end += 1
+              else
+                return nil
+              end
+            end
           end
-          last_data.end += 1
         end
       end
 
@@ -393,6 +406,17 @@ module Groonga
       end
     end
 
+    def create_all_match_data
+      data = ScanInfoData.new(0)
+      data.end = 0
+      data.flags = ScanInfo::Flags::PUSH
+      data.op = Operator::CALL
+      data.logical_op = Operator::OR
+      data.args = [Context.instance["all_records"]]
+      data.search_indexes = []
+      data
+    end
+
     def create_between_data(data, next_data)
       between_data = ScanInfoData.new(data.start)
       between_data.end = next_data.end + 1

  Modified: test/command/suite/select/filter/unary_operation/not_equal.expected (+2 -0)
===================================================================
--- test/command/suite/select/filter/unary_operation/not_equal.expected    2017-02-15 10:37:00 +0900 (891e677)
+++ test/command/suite/select/filter/unary_operation/not_equal.expected    2017-02-15 10:44:30 +0900 (0155154)
@@ -11,5 +11,7 @@ log_level --level info
 [[0,0.0,0.0],true]
 select Numbers --filter '!(_key == 29)'
 [[0,0.0,0.0],[[[2],[["_id","UInt32"],["_key","UInt32"]],[1,2],[3,2929]]]]
+#|i| [table][select][index][selector][no-index][all_records] <Numbers>
+#|i| [table][select][index][equal][accessor][key] <Numbers>
 log_level --level notice
 [[0,0.0,0.0],true]

  Added: test/mruby/suite/query_optimizer/index/test_compare.rb (+140 -0) 100644
===================================================================
--- /dev/null
+++ test/mruby/suite/query_optimizer/index/test_compare.rb    2017-02-15 10:44:30 +0900 (82669b1)
@@ -0,0 +1,140 @@
+class TestIndexCompare < QueryOptimizerTestCase
+  def setup
+    Groonga::Schema.define do |schema|
+      schema.create_table("Values") do |table|
+        table.int32("number")
+      end
+
+      schema.create_table("Numbers",
+                          :type => :patricia_trie,
+                          :key_type => "Int32") do |table|
+        table.index("Values", "number")
+      end
+    end
+
+    @values = Groonga["Values"]
+    setup_expression(@values)
+  end
+
+  def teardown
+    teardown_expression
+  end
+
+  def test_less
+    assert_equal(<<-DUMP, dump_plan("number < 29"))
+[0]
+  op:         <less>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..2>
+    DUMP
+  end
+
+  def test_not_less
+    assert_equal(<<-DUMP, dump_plan("!(number < 29)"))
+[0]
+  op:         <greater_equal>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_less_equal
+    assert_equal(<<-DUMP, dump_plan("!(number <= 29)"))
+[0]
+  op:         <greater>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_greater
+    assert_equal(<<-DUMP, dump_plan("!(number > 29)"))
+[0]
+  op:         <less_equal>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_greater_equal
+    assert_equal(<<-DUMP, dump_plan("!(number >= 29)"))
+[0]
+  op:         <less>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_equal
+    assert_equal(<<-DUMP, dump_plan("!(number == 29)"))
+[0]
+  op:         <call>
+  logical_op: <or>
+  args[0]:    <#<proc:function all_records arguments:[]>>
+  expr:       <0..0>
+[1]
+  op:         <equal>
+  logical_op: <and_not>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..2>
+    DUMP
+  end
+
+  def test_not_not_equal
+    assert_equal(<<-DUMP, dump_plan("!(number != 29)"))
+[0]
+  op:         <equal>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  sub_test_case("multiple conditions") do
+    test("not at the first") do
+      assert_equal(<<-DUMP, dump_plan("!(number < 29) && _id == 29"))
+[0]
+  op:         <greater_equal>
+  logical_op: <or>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <0..3>
+[1]
+  op:         <equal>
+  logical_op: <and>
+  index:      <[#<accessor _id(Values)>]>
+  query:      <29>
+  expr:       <4..6>
+      DUMP
+    end
+
+    test("not at the last") do
+      assert_equal(<<-DUMP, dump_plan("_id == 29 && !(number < 29)"))
+[0]
+  op:         <equal>
+  logical_op: <or>
+  index:      <[#<accessor _id(Values)>]>
+  query:      <29>
+  expr:       <0..2>
+[1]
+  op:         <greater_equal>
+  logical_op: <and>
+  index:      <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]>
+  query:      <29>
+  expr:       <3..6>
+      DUMP
+    end
+  end
+end

  Added: test/mruby/suite/query_optimizer/no_index/test_compare.rb (+129 -0) 100644
===================================================================
--- /dev/null
+++ test/mruby/suite/query_optimizer/no_index/test_compare.rb    2017-02-15 10:44:30 +0900 (59185c5)
@@ -0,0 +1,129 @@
+class TestNoIndexCompare < QueryOptimizerTestCase
+  def setup
+    Groonga::Schema.define do |schema|
+      schema.create_table("Values") do |table|
+        table.int32("number")
+      end
+    end
+
+    @values = Groonga["Values"]
+    setup_expression(@values)
+  end
+
+  def teardown
+    teardown_expression
+  end
+
+  def test_less
+    assert_equal(<<-DUMP, dump_plan("number < 29"))
+[0]
+  op:         <less>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..2>
+    DUMP
+  end
+
+  def test_not_less
+    assert_equal(<<-DUMP, dump_plan("!(number < 29)"))
+[0]
+  op:         <greater_equal>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_less_equal
+    assert_equal(<<-DUMP, dump_plan("!(number <= 29)"))
+[0]
+  op:         <greater>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_greater
+    assert_equal(<<-DUMP, dump_plan("!(number > 29)"))
+[0]
+  op:         <less_equal>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_greater_equal
+    assert_equal(<<-DUMP, dump_plan("!(number >= 29)"))
+[0]
+  op:         <less>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_equal
+    assert_equal(<<-DUMP, dump_plan("!(number == 29)"))
+[0]
+  op:         <not_equal>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  def test_not_not_equal
+    assert_equal(<<-DUMP, dump_plan("!(number != 29)"))
+[0]
+  op:         <equal>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+    DUMP
+  end
+
+  sub_test_case("multiple conditions") do
+    test("not at the first") do
+      assert_equal(<<-DUMP, dump_plan("!(number < 29) && _id == 29"))
+[0]
+  op:         <greater_equal>
+  logical_op: <or>
+  index:      <[]>
+  query:      <29>
+  expr:       <0..3>
+[1]
+  op:         <equal>
+  logical_op: <and>
+  index:      <[#<accessor _id(Values)>]>
+  query:      <29>
+  expr:       <4..6>
+      DUMP
+    end
+
+    test("not at the last") do
+      assert_equal(<<-DUMP, dump_plan("_id == 29 && !(number < 29)"))
+[0]
+  op:         <equal>
+  logical_op: <or>
+  index:      <[#<accessor _id(Values)>]>
+  query:      <29>
+  expr:       <0..2>
+[1]
+  op:         <greater_equal>
+  logical_op: <and>
+  index:      <[]>
+  query:      <29>
+  expr:       <3..6>
+      DUMP
+    end
+  end
+end
-------------- next part --------------
HTML����������������������������...
下载 



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