[Groonga-commit] ranguba/groonga-client-rails at 6d2c1d6 [master] Add Groonga::Client::Searcher

Back to archive index

Kouhei Sutou null+****@clear*****
Tue Mar 22 16:27:26 JST 2016


Kouhei Sutou	2016-03-22 16:27:26 +0900 (Tue, 22 Mar 2016)

  New Revision: 6d2c1d6a477624c29bf0b615a9fb86feb79d091f
  https://github.com/ranguba/groonga-client-rails/commit/6d2c1d6a477624c29bf0b615a9fb86feb79d091f

  Message:
    Add Groonga::Client::Searcher

  Added files:
    lib/groonga/client/searcher.rb
    lib/groonga/client/searcher/request.rb
    lib/groonga/client/searcher/result_set.rb
    lib/groonga/client/searcher/schema.rb
    lib/groonga/client/searcher/schema_synchronizer.rb
  Copied files:
    lib/groonga/client/searcher/source_definition.rb
      (from lib/groonga-client-rails.rb)
  Modified files:
    lib/groonga-client-rails.rb

  Modified: lib/groonga-client-rails.rb (+2 -0)
===================================================================
--- lib/groonga-client-rails.rb    2016-03-22 16:25:54 +0900 (7954ec7)
+++ lib/groonga-client-rails.rb    2016-03-22 16:27:26 +0900 (ef68533)
@@ -16,6 +16,8 @@
 
 require "groonga/client/rails/version"
 
+require "groonga/client/searcher"
+
 if defined?(Rails)
   require "groonga/client/railtie"
 end

  Added: lib/groonga/client/searcher.rb (+139 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/searcher.rb    2016-03-22 16:27:26 +0900 (af14499)
@@ -0,0 +1,139 @@
+# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+require "groonga/client/searcher/request"
+require "groonga/client/searcher/result_set"
+require "groonga/client/searcher/schema"
+require "groonga/client/searcher/schema_synchronizer"
+require "groonga/client/searcher/source_definition"
+
+module Groonga
+  class Client
+    class Searcher
+      class << self
+        def schema
+          @schema ||= Schema.new(default_table_name)
+        end
+
+        def add_source(model_class, columns:)
+          sources[model_class] = SourceDefinition.new(model_class, columns)
+
+          searcher_class = self
+          model_class.after_create do |model|
+            searcher = searcher_class.new
+            searcher.create(model)
+          end
+
+          model_class.after_update do |model|
+            searcher = searcher_class.new
+            searcher.update(model)
+          end
+
+          model_class.after_destroy do |model|
+            searcher = searcher_class.new
+            searcher.destroy(model)
+          end
+        end
+
+        def fetch_source_definition(source)
+          sources.fetch(source.class)
+        end
+
+        def sync
+          sync_schema
+          sync_records
+        end
+
+        private
+        def sources
+          @sources ||= {}
+        end
+
+        def default_table_name
+          name.gsub(/Searcher\z/, "").tableize
+        end
+
+        def sync_schema
+          current_schema = Client.open do |client|
+            client.schema
+          end
+          syncher = SchemaSynchronizer.new(schema, current_schema)
+          syncher.sync
+        end
+
+        def sync_records
+          ensure_model_classes_loaded
+          sources.each do |model_class, definition|
+            all_records = model_class.all
+            if all_records.respond_to?(:find_each)
+              enumerator = all_records.find_each
+            else
+              enumerator = all_records.each
+            end
+            searcher = new
+            enumerator.each do |model|
+              searcher.upsert(model)
+            end
+          end
+        end
+
+        def ensure_model_classes_loaded
+          ::Rails.application.eager_load!
+        end
+      end
+
+      def inititalize
+      end
+
+      def upsert(source)
+        definition = self.class.fetch_source_definition(source)
+        record = {}
+        definition.columns.each do |name, _|
+          record[name.to_s] = source.__send__(name)
+        end
+        record["_key"] = source_key(source)
+        Client.open do |client|
+          client.load(:table => self.class.schema.table,
+                      :values => [record])
+        end
+      end
+
+      def create(source)
+        upsert(source)
+      end
+
+      def update(source)
+        upsert(source)
+      end
+
+      def destroy(source)
+        Client.open do |client|
+          client.delete(table: self.class.schema.table,
+                        key: source_key)
+        end
+      end
+
+      def search
+        TableRequest.new(self.class.schema.table)
+      end
+
+      private
+      def source_key(source)
+        "#{source.class.name}-#{source.id}"
+      end
+    end
+  end
+end

  Added: lib/groonga/client/searcher/request.rb (+109 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/searcher/request.rb    2016-03-22 16:27:26 +0900 (2b9b77e)
@@ -0,0 +1,109 @@
+# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module Groonga
+  class Client
+    class Searcher
+      class Request
+        def result_set
+          @result_set ||= Client.open do |client|
+            ResultSet.new(client.select(to_parameters))
+          end
+        end
+
+        def match_columns(value)
+          OverwriteRequest.new(self,
+                               MatchColumnsRequest.new(value))
+        end
+
+        def query(value)
+          QueryMergeRequest.new(self,
+                                QueryRequest.new(value))
+        end
+      end
+
+      class TableRequest < Request
+        def initialize(table)
+          @table = table
+        end
+
+        def to_parameters
+          {
+            table: @table,
+          }
+        end
+      end
+
+      # @private
+      class OverwriteRequest < Request
+        def initialize(request1, request2)
+          @request1 = request1
+          @request2 = request2
+        end
+
+        def to_parameters
+          @request1.to_parameters.merge(@request2.to_parameters)
+        end
+      end
+
+      # @private
+      class QueryMergeRequest < Request
+        def initialize(request1, request2)
+          @request1 = request1
+          @request2 = request2
+        end
+
+        def to_parameters
+          params1 =****@reque*****_parameters
+          params2 =****@reque*****_parameters
+          params = params1.merge(params2)
+          query1 = params1[:query]
+          query2 = params2[:query]
+          if query1.present? and query2.present?
+            params[:query] = "(#{query1}) (#{query2})"
+          else
+            params[:query] = (query1 || query2)
+          end
+          params
+        end
+      end
+
+      class MatchColumnsRequest < Request
+        def initialize(match_columns)
+          @match_columns = match_columns
+        end
+
+        def to_parameters
+          {
+            match_columns: @match_columns,
+          }
+        end
+      end
+
+      class QueryRequest < Request
+        def initialize(query)
+          @query = query
+        end
+
+        def to_parameters
+          {
+            query: @query,
+          }
+        end
+      end
+    end
+  end
+end

  Added: lib/groonga/client/searcher/result_set.rb (+74 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/searcher/result_set.rb    2016-03-22 16:27:26 +0900 (26a7ea8)
@@ -0,0 +1,74 @@
+# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module Groonga
+  class Client
+    class Searcher
+      class ResultSet
+        def initialize(response)
+          @response = response
+        end
+
+        def n_hits
+          @response.n_hits
+        end
+        # For Kaminari
+        alias_method :total_count, :n_hits
+
+        # For Kaminari
+        def limit_value
+          (@response.command[:limit] || 10).to_i
+        end
+
+        # For Kaminari
+        def offset_value
+          (@response.command[:offset] || 0).to_i
+        end
+
+        def records
+          @response.records
+        end
+
+        def sources
+          @sources ||= fetch_sources
+        end
+
+        def drilldowns
+          @response.drilldowns
+        end
+
+        private
+        def fetch_sources
+          source_ids = {}
+          records.collect do |record|
+            model_name, id = record["_key"].split(/-/, 2)
+            source_ids[model_name] ||= []
+            source_ids[model_name] << id
+          end
+          sources = {}
+          source_ids.each do |model_name, ids|
+            model_name.constantize.find(ids).each_with_index do |model, i|
+              sources["#{model_name}-#{ids[i]}"] = model
+            end
+          end
+          records.collect do |record|
+            sources[record["_key"]]
+          end
+        end
+      end
+    end
+  end
+end

  Added: lib/groonga/client/searcher/schema.rb (+69 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/searcher/schema.rb    2016-03-22 16:27:26 +0900 (90ccbd9)
@@ -0,0 +1,69 @@
+# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module Groonga
+  class Client
+    class Searcher
+      class Schema
+        attr_reader :table
+        attr_reader :columns
+        def initialize(table)
+          @table = table
+          @columns = {}
+        end
+
+        def table=(name)
+          name = name.to_s if name.is_a?(Symbol)
+          @table = name
+        end
+
+        def column(name, options)
+          name = normalize_name(name)
+          @columns[name] = Column.new(name, options)
+        end
+
+        private
+        def normalize_name(name)
+          if name.is_a?(Symbol)
+            name.to_s
+          else
+            name
+          end
+        end
+
+        class Column
+          attr_reader :name
+          def initialize(name, options)
+            @name = name
+            @options = options
+          end
+
+          def type
+            @options[:type] || "Text"
+          end
+
+          def have_index?
+            @options[:index]
+          end
+
+          def have_full_text_search_index?
+            have_index? and @options[:index_type] == :full_text_search
+          end
+        end
+      end
+    end
+  end
+end

  Added: lib/groonga/client/searcher/schema_synchronizer.rb (+137 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/searcher/schema_synchronizer.rb    2016-03-22 16:27:26 +0900 (f3612bd)
@@ -0,0 +1,137 @@
+# Copyright (C) 2016  Kouhei Sutou <kou �� clear-code.com>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+module Groonga
+  class Client
+    class Searcher
+      class SchemaSynchronizer
+        def initialize(schema, current_schema)
+          @schema = schema
+          @current_schema = current_schema
+        end
+
+        def sync
+          sync_table
+          sync_columns
+        end
+
+        private
+        def sync_table
+          current_table = find_current_table
+          return if current_table # TODO: validation
+
+          Client.open do |client|
+            client.table_create(:name => @schema.table,
+                                :flags => "TABLE_PAT_KEY",
+                                :key_type => "ShortText")
+          end
+        end
+
+        def sync_columns
+          current_columns = find_current_columns
+          @schema.columns.each do |_, column|
+            sync_column(column, current_columns[column.name])
+          end
+        end
+
+        def sync_column(column, current_column)
+          if current_column.nil?
+            Client.open do |client|
+              client.column_create(:table => @schema.table,
+                                   :name => column.name,
+                                   :type => column.type)
+            end
+          end
+
+          sync_column_index(column, current_column)
+        end
+
+        def sync_column_index(column, current_column)
+          if column.have_index?
+            lexicon_name = "lexicon_#{@schema.table}_#{column.name}"
+            index_column_name = "index"
+            if current_column
+              indexes = current_column.indexes
+            else
+              indexes = []
+            end
+            indexes.each do |index|
+              return if index.full_name == "#{lexicon_name}.#{index_column_name}"
+            end
+            sync_lexicon(column, lexicon_name)
+            create_index_column(column, lexicon_name, index_column_name)
+          else
+            remove_indexes(current_column)
+          end
+        end
+
+        def sync_lexicon(column, lexicon_name)
+          return if @current_schema.tables[lexicon_name]
+
+          parameters = {
+            :name => lexicon_name,
+            :flags => "TABLE_PAT_KEY",
+          }
+          if column.have_full_text_search_index?
+            parameters[:key_type] = "ShortText"
+            parameters[:default_tokenizer] = "TokenBigram"
+            parameters[:normalizer] = "NormalizerAuto"
+          else
+            parameters[:key_type] = column.type
+          end
+          Client.open do |client|
+            client.table_create(parameters)
+          end
+        end
+
+        def create_index_column(column, lexicon_name, index_column_name)
+          flags = "COLUMN_INDEX"
+          flags += "|WITH_POSITION" if column.have_full_text_search_index?
+          Client.open do |client|
+            client.column_create(:table => lexicon_name,
+                                 :name => index_column_name,
+                                 :flags => flags,
+                                 :type => @schema.table,
+                                 :source => column.name)
+          end
+        end
+
+        def remove_indexes(current_column)
+          return if current_column.nil?
+          current_column.indexes.each do |index|
+            Client.open do |client|
+              clinet.column_remove(:table => index.table.name,
+                                   :name => index.name)
+            end
+          end
+        end
+
+        def find_current_table
+          @current_schema.tables[@schema.table]
+        end
+
+        def find_current_columns
+          current_table = find_current_table
+          if current_table.nil?
+            {}
+          else
+            current_table.columns
+          end
+        end
+      end
+    end
+  end
+end

  Copied: lib/groonga/client/searcher/source_definition.rb (+13 -4) 73%
===================================================================
--- lib/groonga-client-rails.rb    2016-03-22 16:25:54 +0900 (7954ec7)
+++ lib/groonga/client/searcher/source_definition.rb    2016-03-22 16:27:26 +0900 (7a4e4d2)
@@ -14,8 +14,17 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
-require "groonga/client/rails/version"
-
-if defined?(Rails)
-  require "groonga/client/railtie"
+module Groonga
+  class Client
+    class Searcher
+      class SourceDefinition
+        attr_reader :model_class
+        attr_reader :columns
+        def initialize(model_class, columns)
+          @model_class = model_class
+          @columns = columns
+        end
+      end
+    end
+  end
 end
-------------- next part --------------
HTML����������������������������...
Descargar 



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