groonga 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/NEWS.ja.rdoc +18 -3
  2. data/NEWS.rdoc +18 -3
  3. data/README.ja.rdoc +2 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +14 -5
  6. data/TUTORIAL.ja.rdoc +82 -16
  7. data/benchmark/{read-write-small-many-items.rb → read-write-many-small-items.rb} +26 -23
  8. data/benchmark/{write-small-many-items.rb → write-many-small-items.rb} +26 -23
  9. data/example/bookmark.rb +49 -5
  10. data/ext/rb-grn-array.c +11 -1
  11. data/ext/rb-grn-column.c +132 -5
  12. data/ext/rb-grn-context.c +85 -80
  13. data/ext/rb-grn-database.c +182 -9
  14. data/ext/rb-grn-expression-builder.c +69 -0
  15. data/ext/rb-grn-expression.c +314 -0
  16. data/ext/rb-grn-fix-size-column.c +68 -89
  17. data/ext/rb-grn-hash.c +14 -5
  18. data/ext/rb-grn-index-column.c +14 -55
  19. data/ext/rb-grn-object.c +206 -75
  20. data/ext/rb-grn-operation.c +92 -0
  21. data/ext/rb-grn-patricia-trie.c +10 -32
  22. data/ext/rb-grn-query.c +9 -9
  23. data/ext/rb-grn-table-cursor.c +19 -80
  24. data/ext/rb-grn-table-key-support.c +33 -39
  25. data/ext/rb-grn-table.c +436 -79
  26. data/ext/rb-grn-type.c +10 -3
  27. data/ext/rb-grn-utils.c +131 -4
  28. data/ext/rb-grn-variable-size-column.c +36 -0
  29. data/ext/rb-grn-variable.c +90 -0
  30. data/ext/rb-grn.h +109 -56
  31. data/ext/rb-groonga.c +4 -0
  32. data/extconf.rb +39 -13
  33. data/html/index.html +2 -2
  34. data/lib/groonga.rb +22 -0
  35. data/lib/groonga/expression-builder.rb +141 -0
  36. data/lib/groonga/record.rb +25 -1
  37. data/lib/groonga/schema.rb +418 -0
  38. data/test/test-column.rb +11 -23
  39. data/test/test-context.rb +1 -1
  40. data/test/test-database.rb +60 -19
  41. data/test/test-expression-builder.rb +114 -0
  42. data/test/test-expression.rb +55 -0
  43. data/test/test-fix-size-column.rb +53 -0
  44. data/test/test-hash.rb +10 -3
  45. data/test/test-index-column.rb +24 -0
  46. data/test/test-patricia-trie.rb +9 -0
  47. data/test/test-procedure.rb +5 -5
  48. data/test/test-record.rb +71 -4
  49. data/test/test-schema.rb +207 -0
  50. data/test/test-table.rb +94 -12
  51. data/test/test-type.rb +18 -11
  52. data/test/test-variable-size-column.rb +53 -0
  53. data/test/test-variable.rb +28 -0
  54. metadata +18 -5
data/ext/rb-groonga.c CHANGED
@@ -84,6 +84,10 @@ Init_groonga (void)
84
84
  rb_grn_init_accessor(mGrn);
85
85
  rb_grn_init_record(mGrn);
86
86
  rb_grn_init_query(mGrn);
87
+ rb_grn_init_variable(mGrn);
88
+ rb_grn_init_operation(mGrn);
89
+ rb_grn_init_expression(mGrn);
90
+ rb_grn_init_expression_builder(mGrn);
87
91
  rb_grn_init_logger(mGrn);
88
92
  rb_grn_init_snippet(mGrn);
89
93
  }
data/extconf.rb CHANGED
@@ -21,6 +21,7 @@ require 'English'
21
21
  require 'mkmf'
22
22
  require 'pkg-config'
23
23
  require 'fileutils'
24
+ require 'shellwords'
24
25
 
25
26
  checking_for(checking_message("GCC")) do
26
27
  if macro_defined?("__GNUC__", "")
@@ -35,18 +36,45 @@ package_name = "groonga"
35
36
  module_name = "groonga"
36
37
  ext_dir_name = "ext"
37
38
  src_dir = File.join(File.expand_path(File.dirname(__FILE__)), ext_dir_name)
38
- major, minor, micro = 0, 0, 8
39
+ major, minor, micro = 0, 1, 0
40
+
41
+ def local_groonga_base_dir
42
+ File.join(File.dirname(__FILE__), "vendor")
43
+ end
44
+
45
+ def local_groonga_install_dir
46
+ File.expand_path(File.join(local_groonga_base_dir, "local"))
47
+ end
48
+
49
+ def have_local_groonga?(package_name, major, minor, micro)
50
+ return false unless File.exist?(File.join(local_groonga_install_dir, "lib"))
51
+
52
+ prepend_pkg_config_path_for_local_groonga
53
+ PKGConfig.have_package(package_name, major, minor, micro)
54
+ end
55
+
56
+ def prepend_pkg_config_path_for_local_groonga
57
+ pkg_config_dir = File.join(local_groonga_install_dir, "lib", "pkgconfig")
58
+ PackageConfig.prepend_default_path(pkg_config_dir)
59
+
60
+ lib_dir = File.join(local_groonga_install_dir, "lib")
61
+ original_LDFLAGS = $LDFLAGS
62
+ checking_for(checking_message("-Wl,-rpath is available")) do
63
+ $LDFLAGS += " -Wl,-rpath,#{Shellwords.escape(lib_dir)}"
64
+ available = try_compile("int main() {return 0;}")
65
+ $LDFLAGS = original_LDFLAGS unless available
66
+ available
67
+ end
68
+ end
39
69
 
40
70
  def install_groonga_locally(major, minor, micro)
41
71
  require 'open-uri'
42
72
  require 'shellwords'
43
73
 
44
74
  tar_gz = "groonga-#{major}.#{minor}.#{micro}.tar.gz"
45
- base_dir = File.join(File.dirname(__FILE__), "vendor")
46
- install_dir = File.expand_path(File.join(base_dir, "local"))
47
- FileUtils.mkdir_p(base_dir)
75
+ FileUtils.mkdir_p(local_groonga_base_dir)
48
76
 
49
- Dir.chdir(base_dir) do
77
+ Dir.chdir(local_groonga_base_dir) do
50
78
  url = "http://groonga.org/files/groonga/#{tar_gz}"
51
79
  message("downloading %s...", url)
52
80
  open(url, "rb") do |input|
@@ -69,7 +97,7 @@ def install_groonga_locally(major, minor, micro)
69
97
  groonga_source_dir = "groonga-#{major}.#{minor}.#{micro}"
70
98
  Dir.chdir(groonga_source_dir) do
71
99
  message("configuring...")
72
- if xsystem("./configure --prefix=#{Shellwords.escape(install_dir)}")
100
+ if xsystem("./configure CFLAGS='-g -O0' --prefix=#{Shellwords.escape(install_dir)}")
73
101
  message(" done\n")
74
102
  else
75
103
  message(" failed\n")
@@ -94,16 +122,14 @@ def install_groonga_locally(major, minor, micro)
94
122
  end
95
123
  end
96
124
 
97
- pkg_config_dir = File.join(install_dir, "lib", "pkgconfig")
98
- PackageConfig.prepend_default_path(pkg_config_dir)
99
-
100
- lib_dir = File.join(install_dir, "lib")
101
- $LDFLAGS += " -Wl,-rpath,#{Shellwords.escape(lib_dir)}"
125
+ prepend_pkg_config_path_for_local_groonga
102
126
  end
103
127
 
104
128
  unless PKGConfig.have_package(package_name, major, minor, micro)
105
- install_groonga_locally(major, minor, micro)
106
- PKGConfig.have_package(package_name, major, minor, micro) or exit 1
129
+ unless have_local_groonga?(package_name, major, minor, micro)
130
+ install_groonga_locally(major, minor, micro)
131
+ PKGConfig.have_package(package_name, major, minor, micro) or exit 1
132
+ end
107
133
  end
108
134
 
109
135
  real_version = PKGConfig.modversion(package_name)
data/html/index.html CHANGED
@@ -42,7 +42,7 @@
42
42
 
43
43
  <h3>Ruby/groongaの最新リリース</h3>
44
44
  <p>
45
- 2009-04-30にリリースされた0.0.1が最新です。
45
+ 2009-06-04にリリースされた0.0.2が最新です。
46
46
  </p>
47
47
 
48
48
  <h3 id="install-ruby-groonga">Ruby/groongaのインストール</h3>
@@ -79,7 +79,7 @@
79
79
 
80
80
  <h3>ActiveGroongaの最新リリース</h3>
81
81
  <p>
82
- 2009-04-30にリリースされた0.0.1が最新です。
82
+ 2009-06-05にリリースされた0.0.2が最新です。
83
83
  </p>
84
84
 
85
85
  <h3 id="install-active-groonga">ActiveGroongaのインストール</h3>
data/lib/groonga.rb CHANGED
@@ -15,7 +15,27 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
 
18
+ require 'pathname'
19
+
20
+ base_dir = Pathname.new(__FILE__).dirname.dirname
21
+ local_groonga_library_dir = base_dir + "vendor" + "local" + "lib"
22
+ if local_groonga_library_dir.exist?
23
+ prepend_path = Proc.new do |environment_name, separator|
24
+ paths = (ENV[environment_name] || '').split(/#{separator}/)
25
+ unless paths.include?(local_groonga_library_dir.to_s)
26
+ paths = [local_groonga_library_dir.to_s] + paths
27
+ ENV[environment_name] = paths.join(separator)
28
+ end
29
+ end
30
+
31
+ case RUBY_PLATFORM
32
+ when /mingw|mswin/
33
+ prepend_path.call("PATH", ";")
34
+ end
35
+ end
36
+
18
37
  require 'groonga/record'
38
+ require 'groonga/expression-builder'
19
39
  require 'groonga.so'
20
40
 
21
41
  ##
@@ -48,3 +68,5 @@ module Groonga
48
68
  end
49
69
  end
50
70
  end
71
+
72
+ require 'groonga/schema'
@@ -0,0 +1,141 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ module Groonga
19
+ module ExpressionBuildable
20
+ attr_accessor :builder, :expression, :variable
21
+ def initialize(*args)
22
+ @builder = self
23
+ @expression = nil
24
+ @variable = nil
25
+ @table = nil
26
+ @name = nil
27
+ @query = nil
28
+ @default_column = nil
29
+ end
30
+
31
+ def build
32
+ @expression = Expression.new(:table => @table,
33
+ :name => @name,
34
+ :query => @query,
35
+ :default_column => @default_column)
36
+ if block_given?
37
+ @variable = @expression.define_variable(:domain => @table)
38
+ @expression.append_object(@variable)
39
+ yield(self)
40
+ @expression.compile
41
+ end
42
+ @expression
43
+ end
44
+ end
45
+
46
+ class RecordExpressionBuilder
47
+ include ExpressionBuildable
48
+
49
+ def initialize(table, name)
50
+ super
51
+ @table = table
52
+ @name = name
53
+ end
54
+
55
+ def [](name)
56
+ column = @table.column(name)
57
+ if column.nil?
58
+ message = "unknown column <#{name.inspect}> " +
59
+ "for table <#{@table.inspect}>"
60
+ raise ArgumentError, message
61
+ end
62
+ builder = ColumnExpressionBuilder.new(column, nil, nil)
63
+ builder.builder = @builder
64
+ builder.expression = @expression
65
+ builder.variable = @variable
66
+ builder
67
+ end
68
+
69
+ def &(other)
70
+ @expression.append_operation(Groonga::Operation::AND, 2)
71
+ @builder
72
+ end
73
+ end
74
+
75
+ class ColumnExpressionBuilder
76
+ include ExpressionBuildable
77
+
78
+ def initialize(column, name, query)
79
+ super
80
+ @table = column.table
81
+ @column = column
82
+ @default_column = column.local_name
83
+ @name = name
84
+ @query = query
85
+ end
86
+
87
+ def ==(other)
88
+ @expression.append_object(@variable)
89
+ @expression.append_constant(@column.local_name)
90
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
91
+ @expression.append_constant(other)
92
+ @expression.append_operation(Groonga::Operation::EQUAL, 2)
93
+ @builder
94
+ end
95
+
96
+ def =~(other)
97
+ @expression.append_object(@variable)
98
+ @expression.append_constant(@column.local_name)
99
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
100
+ @expression.append_constant(other)
101
+ @expression.append_operation(Groonga::Operation::MATCH, 2)
102
+ @builder
103
+ end
104
+
105
+ def <(other)
106
+ @expression.append_object(@variable)
107
+ @expression.append_constant(@column.local_name)
108
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
109
+ @expression.append_constant(other)
110
+ @expression.append_operation(Groonga::Operation::LESS, 2)
111
+ @builder
112
+ end
113
+
114
+ def <=(other)
115
+ @expression.append_object(@variable)
116
+ @expression.append_constant(@column.local_name)
117
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
118
+ @expression.append_constant(other)
119
+ @expression.append_operation(Groonga::Operation::LESS_EQUAL, 2)
120
+ @builder
121
+ end
122
+
123
+ def >(other)
124
+ @expression.append_object(@variable)
125
+ @expression.append_constant(@column.local_name)
126
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
127
+ @expression.append_constant(other)
128
+ @expression.append_operation(Groonga::Operation::GREATER, 2)
129
+ @builder
130
+ end
131
+
132
+ def >=(other)
133
+ @expression.append_object(@variable)
134
+ @expression.append_constant(@column.local_name)
135
+ @expression.append_operation(Groonga::Operation::OBJECT_GET_VALUE, 2)
136
+ @expression.append_constant(other)
137
+ @expression.append_operation(Groonga::Operation::GREATER_EQUAL, 2)
138
+ @builder
139
+ end
140
+ end
141
+ end
@@ -34,7 +34,7 @@ module Groonga
34
34
  end
35
35
 
36
36
  def [](column_name)
37
- (column(column_name) || {})[@id]
37
+ column(column_name, true)[@id]
38
38
  end
39
39
 
40
40
  def []=(column_name, value)
@@ -73,6 +73,14 @@ module Groonga
73
73
  @table[@id] = value
74
74
  end
75
75
 
76
+ def increment!(name, delta=nil)
77
+ column(name, true).increment!(@id, delta)
78
+ end
79
+
80
+ def decrement!(name, delta=nil)
81
+ column(name, true).decrement!(@id, delta)
82
+ end
83
+
76
84
  def columns
77
85
  @table.columns
78
86
  end
@@ -91,6 +99,22 @@ module Groonga
91
99
  @table.delete(@id)
92
100
  end
93
101
 
102
+ def lock(options={}, &block)
103
+ @table.lock(options.merge(:id => @id), &block)
104
+ end
105
+
106
+ def unlock(options={})
107
+ @table.unlock(options.merge(:id => @id))
108
+ end
109
+
110
+ def clear_lock(options={})
111
+ @table.clear_lock(options.merge(:id => @id))
112
+ end
113
+
114
+ def locked?(options={})
115
+ @table.locked?(options.merge(:id => @id))
116
+ end
117
+
94
118
  private
95
119
  def column(name, required=false)
96
120
  _column = @table.column(name.to_s)
@@ -0,0 +1,418 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+
19
+ module Groonga
20
+
21
+ # groongaのスキーマ(データ構造)を管理するクラス。
22
+ class Schema
23
+ class << self
24
+
25
+ # call-seq:
26
+ # Groonga::Schema.define(options={}) {|schema| ...}
27
+ #
28
+ # スキーマを定義する。ブロックにはGroonga::Schemaオブ
29
+ # ジェクトがわたるので、そのオブジェクトを利用してスキー
30
+ # マを定義する。
31
+ #
32
+ # _options_に指定可能な値は以下の通り。
33
+ #
34
+ # [+:context+]
35
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
36
+ # 省略した場合はGroonga::Context.defaultを使用する。
37
+ def define(options={})
38
+ schema = new(options)
39
+ yield(schema)
40
+ schema.define
41
+ end
42
+
43
+ # call-seq:
44
+ # Groonga::Schema.create_table(name, options={}) {|table| ...}
45
+ #
46
+ # 名前が_name_のテーブルを作成する。以下の省略形。
47
+ #
48
+ # Groonga::Schema.define do |schema|
49
+ # schema.create_table(name, options) do |table|
50
+ # ...
51
+ # end
52
+ # end
53
+ #
54
+ # ブロックにはGroonga::Schema::TableDefinitionオブジェ
55
+ # クトがわたるので、そのオブジェクトを利用してテーブル
56
+ # の詳細を定義する。
57
+ #
58
+ # _options_に指定可能な値は以下の通り。
59
+ #
60
+ # [+:context+]
61
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
62
+ # 省略した場合はGroonga::Context.defaultを使用する。
63
+ #
64
+ # [+:path+]
65
+ # テーブルを保存するパスを指定する。パスを指定すると
66
+ # 永続テーブルになる。
67
+ #
68
+ # [+:persistent+]
69
+ # テーブルを永続テーブルとする。+:path:+を省略した場
70
+ # 合はパス名は自動的に作成される。デフォルトでは永続
71
+ # テーブルとなる。
72
+ #
73
+ # [+:value_size+]
74
+ # 値のサイズを指定する。デフォルトは0。
75
+ def create_table(name, options={}, &block)
76
+ define do |schema|
77
+ schema.create_table(name, options, &block)
78
+ end
79
+ end
80
+
81
+ # スキーマの内容を文字列で返す。返された値は
82
+ # Groonga::Schema#restoreすることによりスキーマ内に組
83
+ # み込むことができる。
84
+ #
85
+ # dump.rb:
86
+ # File.open("/tmp/groonga-schema.rb", "w") do |schema|
87
+ # dumped_text = Groonga::Schema.dump
88
+ # end
89
+ #
90
+ # restore.rb:
91
+ # dumped_text = Groonga::Schema.dump
92
+ # Groonga::Database.create(:path => "/tmp/new-db.grn")
93
+ # Groonga::Schema.define do |schema|
94
+ # schema.restore(dumped_text)
95
+ # end
96
+ #
97
+ # _options_に指定可能な値は以下の通り。
98
+ #
99
+ # [+:context+]
100
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
101
+ # 省略した場合はGroonga::Context.defaultを使用する。
102
+ def dump(options={})
103
+ Dumper.new(options).dump
104
+ end
105
+
106
+ def normalize_type(type) # :nodoc:
107
+ return type if type.nil?
108
+ return type if type.is_a?(Groonga::Object)
109
+ case type.to_s
110
+ when "string"
111
+ "ShortText"
112
+ when "text"
113
+ "Text"
114
+ when "int", "integer"
115
+ "Int32"
116
+ when "float"
117
+ "Float"
118
+ when "decimal"
119
+ "Int64"
120
+ when "datetime", "timestamp", "time", "date"
121
+ "Time"
122
+ when "binary"
123
+ "LongText"
124
+ when "boolean"
125
+ "Bool"
126
+ else
127
+ type
128
+ end
129
+ end
130
+ end
131
+
132
+ # スキーマ定義を開始する。
133
+ #
134
+ # _options_に指定可能な値は以下の通り。
135
+ #
136
+ # [+:context+]
137
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
138
+ # 省略した場合はGroonga::Context.defaultを使用する。
139
+ def initialize(options={})
140
+ @options = (options || {}).dup
141
+ @definitions = []
142
+ end
143
+
144
+ # 定義されたスキーマ定義を実際に実行する。
145
+ def define
146
+ @definitions.each do |definition|
147
+ definition.define
148
+ end
149
+ end
150
+
151
+ # Groonga::Schema.dumpで返されたスキーマの内容を読み込む。
152
+ #
153
+ # 読み込まれた内容は#defineを呼び出すまでは実行されない
154
+ # ことに注意すること。
155
+ def restore(dumped_text)
156
+ instance_eval(dumped_text)
157
+ end
158
+
159
+ # 名前が_name_のテーブルを作成する。
160
+ #
161
+ # 作成したテーブルは#defineを呼び出すまでは実行されない
162
+ # ことに注意すること。
163
+ #
164
+ # _options_に指定可能な値は以下の通り。
165
+ #
166
+ # [+:context+]
167
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
168
+ # 省略した場合はGroonga::Schema.newで指定した
169
+ # Groonga::Contextを使用する。Groonga::Schema.newで指
170
+ # 定していない場合はGroonga::Context.defaultを使用する。
171
+ #
172
+ # [+:path+]
173
+ # テーブルを保存するパスを指定する。パスを指定すると
174
+ # 永続テーブルになる。
175
+ #
176
+ # [+:persistent+]
177
+ # テーブルを永続テーブルとする。+:path:+を省略した場
178
+ # 合はパス名は自動的に作成される。デフォルトでは永続
179
+ # テーブルとなる。
180
+ #
181
+ # [+:value_size+]
182
+ # 値のサイズを指定する。デフォルトは0。
183
+ def create_table(name, options={})
184
+ definition = TableDefinition.new(name, @options.merge(options || {}))
185
+ yield(definition)
186
+ @definitions << definition
187
+ end
188
+
189
+ class TableDefinition
190
+ def initialize(name, options)
191
+ @name = name
192
+ @name = @name.to_s if @name.is_a?(Symbol)
193
+ @columns = []
194
+ @options = options
195
+ @table_type = table_type
196
+ end
197
+
198
+ def define
199
+ table = @table_type.create(create_options)
200
+ @columns.each do |column|
201
+ column.define(table)
202
+ end
203
+ table
204
+ end
205
+
206
+ def column(name, type, options={})
207
+ column = self[name] || ColumnDefinition.new(name, options)
208
+ column.type = type
209
+ column.options.merge!(options)
210
+ @columns << column unless @columns.include?(column)
211
+ self
212
+ end
213
+
214
+ def integer32(name, options={})
215
+ column(name, "Int32", options)
216
+ end
217
+ alias_method :integer, :integer32
218
+ alias_method :int32, :integer32
219
+
220
+ def integer64(name, options={})
221
+ column(name, "Int64", options)
222
+ end
223
+ alias_method :int64, :integer64
224
+
225
+ def unsigned_integer32(name, options={})
226
+ column(name, "UInt32", options)
227
+ end
228
+ alias_method :unsigned_integer, :unsigned_integer32
229
+ alias_method :uint32, :unsigned_integer32
230
+
231
+ def unsigned_integer64(name, options={})
232
+ column(name, "UInt64", options)
233
+ end
234
+ alias_method :uint64, :unsigned_integer64
235
+
236
+ def float(name, options={})
237
+ column(name, "Float", options)
238
+ end
239
+
240
+ def time(name, options={})
241
+ column(name, "Time", options)
242
+ end
243
+
244
+ def short_text(name, options={})
245
+ column(name, "ShortText", options)
246
+ end
247
+ alias_method :string, :short_text
248
+
249
+ def text(name, options={})
250
+ column(name, "Text", options)
251
+ end
252
+
253
+ def long_text(name, options={})
254
+ column(name, "LongText", options)
255
+ end
256
+
257
+ def index(name, target_column, options={})
258
+ column = self[name] || IndexColumnDefinition.new(name, options)
259
+ column.target = target_column
260
+ column.options.merge!(options)
261
+ @columns << column unless @columns.include?(column)
262
+ self
263
+ end
264
+
265
+ def [](name)
266
+ @columns.find {|column| column.name == name}
267
+ end
268
+
269
+ private
270
+ def table_type
271
+ type = @options[:type]
272
+ case type
273
+ when :array, nil
274
+ Groonga::Array
275
+ when :hash
276
+ Groonga::Hash
277
+ when :patricia_trie
278
+ Groonga::PatriciaTrie
279
+ else
280
+ raise ArgumentError, "unknown table type: #{type.inspect}"
281
+ end
282
+ end
283
+
284
+ def create_options
285
+ common = {
286
+ :name => @name,
287
+ :path => @options[:path],
288
+ :persistent => persistent?,
289
+ :value_size => @options[:value_size],
290
+ :context => context,
291
+ }
292
+ key_support_table_common = {
293
+ :key_type => Schema.normalize_type(@options[:key_type]),
294
+ :default_tokenizer => @options[:default_tokenizer],
295
+ }
296
+
297
+ if @table_type == Groonga::Array
298
+ common
299
+ elsif @table_type == Groonga::Hash
300
+ common.merge(key_support_table_common)
301
+ elsif @table_type == Groonga::PatriciaTrie
302
+ options = {
303
+ :key_normalize => @options[:key_normalize],
304
+ :key_with_sis => @options[:key_with_sis],
305
+ }
306
+ common.merge(key_support_table_common).merge(options)
307
+ else
308
+ raise ArgumentError, "unknown table type: #{@table_type.inspect}"
309
+ end
310
+ end
311
+
312
+ def persistent?
313
+ @options[:persistent].nil? ? true : @options[:persistent]
314
+ end
315
+
316
+ def context
317
+ @options[:context] || Groonga::Context.default
318
+ end
319
+ end
320
+
321
+ class ColumnDefinition
322
+ attr_accessor :name, :type
323
+ attr_reader :options
324
+
325
+ def initialize(name, options={})
326
+ @name = name
327
+ @name = @name.to_s if @name.is_a?(Symbol)
328
+ @options = (options || {}).dup
329
+ @type = nil
330
+ end
331
+
332
+ def define(table)
333
+ table.define_column(@name,
334
+ Schema.normalize_type(@type),
335
+ @options)
336
+ end
337
+ end
338
+
339
+ class IndexColumnDefinition
340
+ attr_accessor :name, :target
341
+ attr_reader :options
342
+
343
+ def initialize(name, options={})
344
+ @name = name
345
+ @name = @name.to_s if @name.is_a?(Symbol)
346
+ @options = (options || {}).dup
347
+ @target = nil
348
+ end
349
+
350
+ def define(table)
351
+ target = @target
352
+ target = context[target] unless target.is_a?(Groonga::Object)
353
+ index = table.define_index_column(@name,
354
+ target.table,
355
+ @options)
356
+ index.source = target
357
+ index
358
+ end
359
+
360
+ private
361
+ def context
362
+ @options[:context] || Groonga::Context.default
363
+ end
364
+ end
365
+
366
+ class Dumper
367
+ def initialize(options={})
368
+ @options = (options || {}).dup
369
+ end
370
+
371
+ def dump
372
+ context = @options[:context] || Groonga::Context.default
373
+ database = context.database
374
+ return nil if database.nil?
375
+
376
+ schema = ""
377
+ database.each do |object|
378
+ next unless object.is_a?(Groonga::Table)
379
+ next if object.name == "<ranguba:objects>"
380
+ schema << "create_table(#{object.name.inspect}) do |table|\n"
381
+ object.columns.each do |column|
382
+ type = column_method(column)
383
+ name = column.local_name
384
+ schema << " table.#{type}(#{name.inspect})\n"
385
+ end
386
+ schema << "end\n"
387
+ end
388
+ schema
389
+ end
390
+
391
+ private
392
+ def column_method(column)
393
+ case column.range.name
394
+ when "Int32"
395
+ "integer32"
396
+ when "Int64"
397
+ "integer64"
398
+ when "UInt32"
399
+ "unsigned_integer32"
400
+ when "UInt64"
401
+ "unsigned_integer64"
402
+ when "Float"
403
+ "float"
404
+ when "Time"
405
+ "time"
406
+ when "ShortText"
407
+ "short_text"
408
+ when "Text"
409
+ "text"
410
+ when "LongText"
411
+ "long_text"
412
+ else
413
+ raise ArgumentError, "unsupported column: #{column.inspect}"
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end