[Groonga-commit] ranguba/groonga-client at 92ea868 [master] Add groonga-client-index-recreate

Back to archive index

Kouhei Sutou null+****@clear*****
Wed Oct 25 16:07:24 JST 2017


Kouhei Sutou	2017-10-25 16:07:24 +0900 (Wed, 25 Oct 2017)

  New Revision: 92ea8687a10836e6d4ae25411e71034da9c5a7f9
  https://github.com/ranguba/groonga-client/commit/92ea8687a10836e6d4ae25411e71034da9c5a7f9

  Message:
    Add groonga-client-index-recreate
    
    TODO:
    
      * Add tests
      * Add code for exception cases

  Added files:
    bin/groonga-client-index-recreate
    lib/groonga/client/command-line/groonga-client-index-recreate.rb

  Added: bin/groonga-client-index-recreate (+23 -0) 100755
===================================================================
--- /dev/null
+++ bin/groonga-client-index-recreate    2017-10-25 16:07:24 +0900 (4e591fd)
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+# -*- ruby -*-
+#
+# Copyright (C) 2017  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/command-line/groonga-client-index-recreate"
+
+command_line = Groonga::Client::CommandLine::GroongaClientIndexRecreate.new
+exit(command_line.run(ARGV))

  Added: lib/groonga/client/command-line/groonga-client-index-recreate.rb (+277 -0) 100644
===================================================================
--- /dev/null
+++ lib/groonga/client/command-line/groonga-client-index-recreate.rb    2017-10-25 16:07:24 +0900 (d7319b0)
@@ -0,0 +1,277 @@
+# Copyright (C) 2017  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 "optparse"
+require "json"
+
+require "groonga/client"
+
+module Groonga
+  class Client
+    module CommandLine
+      class GroongaClientIndexRecreate
+        def initialize
+          @protocol = :http
+          @host     = "localhost"
+          @port     = nil
+
+          @read_timeout = -1
+
+          @n_workers = 0
+        end
+
+        def run(argv)
+          target_indexes = parse_command_line(argv)
+
+          @client = Client.new(:protocol => @protocol,
+                               :host     => @host,
+                               :port     => @port,
+                               :read_timeout => @read_timeout,
+                               :backend  => :synchronous)
+
+          runner = Runner.new(@client, target_indexes)
+          runner.run do
+            @n_workers.times do
+              @client.database_unmap
+            end
+          end
+        end
+
+        private
+        def parse_command_line(argv)
+          parser = OptionParser.new
+          parser.version = VERSION
+          parser.banner += " LEXICON1.INDEX1 LEXICON2.INDEX2 ..."
+
+          parser.separator("")
+
+          parser.separator("Connection:")
+
+          available_protocols = [:http, :gqtp]
+          parser.on("--protocol=PROTOCOL", [:http, :gqtp],
+                    "Protocol to connect to Groonga server.",
+                    "[#{available_protocols.join(", ")}]",
+                    "(#{@protocol})") do |protocol|
+            @protocol = protocol
+          end
+
+          parser.on("--host=HOST",
+                    "Groonga server to be connected.",
+                    "(#{@host})") do |host|
+            @host = host
+          end
+
+          parser.on("--port=PORT", Integer,
+                    "Port number of Groonga server to be connected.",
+                    "(auto)") do |port|
+            @port = port
+          end
+
+          parser.on("--read-timeout=TIMEOUT", Integer,
+                    "Timeout on reading response from Groonga server.",
+                    "You can disable timeout by specifying -1.",
+                    "(#{@read_timeout})") do |timeout|
+            @read_timeout = timeout
+          end
+
+          parser.on("--n-workers=N", Integer,
+                    "The number of groonga-httpd workers.",
+                    "This options is meaningless for groonga -s.",
+                    "(#{@n_workers})") do |n|
+            @n_workers = n
+          end
+
+          target_indexes = parser.parse(argv)
+
+          @port ||= default_port(@protocol)
+
+          target_indexes
+        end
+
+        def default_port(protocol)
+          case protocol
+          when :http
+            10041
+          when :gqtp
+            10043
+          end
+        end
+
+        class Runner
+          def initialize(client, target_indexes)
+            @client = client
+            @target_indexes = target_indexes
+          end
+
+          def run
+            catch do |tag|
+              @abort_tag = tag
+              alias_column = ensure_alias_column
+              @target_indexes.each do |index|
+                current_index = recreate_index(index, alias_column)
+                remove_old_indexes(index, current_index)
+              end
+              yield if block_given?
+              true
+            end
+          end
+
+          private
+          def execute_command(name, arguments={})
+            response =****@clien*****(name, arguments)
+            unless response.success?
+              puts("failed to run #{name}: #{response.inspect}")
+              throw(@abort_tag, false)
+            end
+            response
+          end
+
+          def config_get(key)
+            execute_command(:config_get, :key => key).body
+          end
+
+          def config_set(key, value)
+            execute_command(:config_set, :key => key, :value => value).body
+          end
+
+          def object_exist?(name)
+            execute_command(:object_exist, :name => name).body
+          end
+
+          def column_rename(table, name, new_name)
+            execute_command(:column_rename,
+                            :table => table,
+                            :name => name,
+                            :new_name => new_name).body
+          end
+
+          def column_list(table)
+            execute_command(:column_list, :table => table)
+          end
+
+          def column_remove(table, column)
+            execute_command(:column_remove,
+                            :table => table,
+                            :name => column)
+          end
+
+          def column_create_similar(table, column_name, base_column_name)
+            info = execute_command(:schema)["#{table}.#{base_column_name}"]
+            arguments = info.command.arguments.merge("name" => column_name)
+            execute_command(:column_create, arguments).body
+          end
+
+          def set_alias(alias_column, alias_name, real_name)
+            table, column = alias_column.split(".", 2)
+            values = [
+              {
+                "_key" => alias_name,
+                column => real_name,
+              },
+            ]
+            # TODO: check return value
+            execute_command(:load,
+                            :table => table,
+                            :values => JSON.generate(values))
+          end
+
+          def resolve_alias(alias_column, key)
+            table, column = alias_column.split(".", 2)
+            filter = "_key == #{ScriptSyntax.format_string(key)}"
+            response = execute_command(:select,
+                                       :table => table,
+                                       :filter => filter,
+                                       :output_columns => column)
+            return nil if response.n_hits.zero?
+            response.records.first[column]
+          end
+
+          def ensure_alias_column
+            alias_column = config_get("alias.column")
+            if alias_column.empty?
+              table = "Aliases"
+              column = "real_name"
+              alias_column = "#{table}.#{column}"
+              unless object_exist?(table)
+                execute_command(:table_create,
+                                :name => table,
+                                :flags => "TABLE_HASH_KEY",
+                                :key_type => "ShortText")
+              end
+              unless object_exist?(alias_column)
+                execute_command(:column_create,
+                                :table => table,
+                                :name => column,
+                                :flags => "COLUMN_SCALAR",
+                                :type => "ShortText")
+              end
+              config_set("alias.column", alias_column)
+            end
+            alias_column
+          end
+
+          def recreate_index(full_index_name, alias_column)
+            revision = Time.now.strftime("%Y%m%d")
+            table_name, index_name = full_index_name.split(".", 2)
+            real_index_name = "#{index_name}_#{revision}"
+            real_full_index_name = "#{table_name}.#{real_index_name}"
+            if object_exist?(full_index_name)
+              set_alias(alias_column, full_index_name, real_full_index_name)
+              column_rename(table_name, index_name, real_index_name)
+              nil
+            elsif object_exist?(real_full_index_name)
+              nil
+            else
+              full_current_index_name =
+                resolve_alias(alias_column, full_index_name)
+              current_table_name, current_index_name =
+                full_current_index_name.split(".", 2)
+              if current_table_name != table_name
+                puts("!!!")
+              end
+              if current_index_name == real_index_name
+                puts("Same")
+                return nil
+              end
+              column_create_similar(table_name,
+                                    real_index_name,
+                                    current_index_name)
+              set_alias(alias_column, full_index_name, real_full_index_name)
+              full_current_index_name
+            end
+          end
+
+          def remove_old_indexes(full_base_index_name, full_current_index_name)
+            return if full_current_index_name.nil?
+
+            table_name, base_index_name = full_base_index_name.split(".", 2)
+            _, current_index_name = full_current_index_name.split(".", 2)
+
+            target_index_columns = column_list(table_name).find_all do |column|
+              column.name.start_with?("#{base_index_name}_") and
+                column.index?
+            end
+            target_index_columns.collect(&:name).sort.each do |index_name|
+              next unless /_(\d{4})(\d{2})(\d{2})\z/ =~ index_name
+              next if index_name >= current_index_name
+              column_remove(table_name, index_name)
+            end
+          end
+        end
+      end
+    end
+  end
+end
-------------- next part --------------
HTML����������������������������...
URL: https://lists.osdn.me/mailman/archives/groonga-commit/attachments/20171025/1bbadf3c/attachment-0001.htm 



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