rroonga 0.9.2-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. data/AUTHORS +5 -0
  2. data/NEWS.ja.rdoc +114 -0
  3. data/NEWS.rdoc +116 -0
  4. data/README.ja.rdoc +65 -0
  5. data/README.rdoc +66 -0
  6. data/Rakefile +206 -0
  7. data/benchmark/common.rb +49 -0
  8. data/benchmark/read-write-many-small-items.rb +144 -0
  9. data/benchmark/write-many-small-items.rb +135 -0
  10. data/example/bookmark.rb +161 -0
  11. data/example/index-html.rb +89 -0
  12. data/example/search/config.ru +230 -0
  13. data/example/search/public/css/groonga.css +122 -0
  14. data/ext/.gitignore +2 -0
  15. data/ext/groonga/extconf.rb +93 -0
  16. data/ext/groonga/rb-grn-accessor.c +52 -0
  17. data/ext/groonga/rb-grn-array-cursor.c +36 -0
  18. data/ext/groonga/rb-grn-array.c +210 -0
  19. data/ext/groonga/rb-grn-column.c +573 -0
  20. data/ext/groonga/rb-grn-context.c +662 -0
  21. data/ext/groonga/rb-grn-database.c +472 -0
  22. data/ext/groonga/rb-grn-encoding-support.c +64 -0
  23. data/ext/groonga/rb-grn-encoding.c +257 -0
  24. data/ext/groonga/rb-grn-exception.c +1110 -0
  25. data/ext/groonga/rb-grn-expression-builder.c +75 -0
  26. data/ext/groonga/rb-grn-expression.c +731 -0
  27. data/ext/groonga/rb-grn-fix-size-column.c +166 -0
  28. data/ext/groonga/rb-grn-hash-cursor.c +38 -0
  29. data/ext/groonga/rb-grn-hash.c +294 -0
  30. data/ext/groonga/rb-grn-index-column.c +488 -0
  31. data/ext/groonga/rb-grn-logger.c +504 -0
  32. data/ext/groonga/rb-grn-object.c +1369 -0
  33. data/ext/groonga/rb-grn-operation.c +198 -0
  34. data/ext/groonga/rb-grn-patricia-trie-cursor.c +39 -0
  35. data/ext/groonga/rb-grn-patricia-trie.c +488 -0
  36. data/ext/groonga/rb-grn-procedure.c +52 -0
  37. data/ext/groonga/rb-grn-query.c +260 -0
  38. data/ext/groonga/rb-grn-record.c +40 -0
  39. data/ext/groonga/rb-grn-snippet.c +334 -0
  40. data/ext/groonga/rb-grn-table-cursor-key-support.c +69 -0
  41. data/ext/groonga/rb-grn-table-cursor.c +247 -0
  42. data/ext/groonga/rb-grn-table-key-support.c +714 -0
  43. data/ext/groonga/rb-grn-table.c +1977 -0
  44. data/ext/groonga/rb-grn-type.c +181 -0
  45. data/ext/groonga/rb-grn-utils.c +769 -0
  46. data/ext/groonga/rb-grn-variable-size-column.c +36 -0
  47. data/ext/groonga/rb-grn-variable.c +108 -0
  48. data/ext/groonga/rb-grn-view-accessor.c +53 -0
  49. data/ext/groonga/rb-grn-view-cursor.c +35 -0
  50. data/ext/groonga/rb-grn-view-record.c +41 -0
  51. data/ext/groonga/rb-grn-view.c +421 -0
  52. data/ext/groonga/rb-grn.h +698 -0
  53. data/ext/groonga/rb-groonga.c +107 -0
  54. data/extconf.rb +130 -0
  55. data/html/bar.svg +153 -0
  56. data/html/developer.html +117 -0
  57. data/html/developer.svg +469 -0
  58. data/html/download.svg +253 -0
  59. data/html/favicon.ico +0 -0
  60. data/html/favicon.xcf +0 -0
  61. data/html/footer.html.erb +28 -0
  62. data/html/head.html.erb +4 -0
  63. data/html/header.html.erb +17 -0
  64. data/html/index.html +147 -0
  65. data/html/install.svg +636 -0
  66. data/html/logo.xcf +0 -0
  67. data/html/ranguba.css +250 -0
  68. data/html/tutorial.svg +559 -0
  69. data/lib/1.8/groonga.so +0 -0
  70. data/lib/1.9/groonga.so +0 -0
  71. data/lib/groonga.rb +90 -0
  72. data/lib/groonga/context.rb +184 -0
  73. data/lib/groonga/expression-builder.rb +324 -0
  74. data/lib/groonga/patricia-trie.rb +85 -0
  75. data/lib/groonga/record.rb +311 -0
  76. data/lib/groonga/schema.rb +1191 -0
  77. data/lib/groonga/view-record.rb +56 -0
  78. data/license/GPL +340 -0
  79. data/license/LGPL +504 -0
  80. data/license/RUBY +59 -0
  81. data/misc/grnop2ruby.rb +49 -0
  82. data/pkg-config.rb +333 -0
  83. data/rroonga-build.rb +57 -0
  84. data/test-unit/Rakefile +40 -0
  85. data/test-unit/TODO +5 -0
  86. data/test-unit/bin/testrb +5 -0
  87. data/test-unit/html/classic.html +15 -0
  88. data/test-unit/html/index.html +25 -0
  89. data/test-unit/html/index.html.ja +27 -0
  90. data/test-unit/lib/test/unit.rb +323 -0
  91. data/test-unit/lib/test/unit/assertionfailederror.rb +25 -0
  92. data/test-unit/lib/test/unit/assertions.rb +1230 -0
  93. data/test-unit/lib/test/unit/attribute.rb +125 -0
  94. data/test-unit/lib/test/unit/autorunner.rb +360 -0
  95. data/test-unit/lib/test/unit/collector.rb +36 -0
  96. data/test-unit/lib/test/unit/collector/descendant.rb +23 -0
  97. data/test-unit/lib/test/unit/collector/dir.rb +108 -0
  98. data/test-unit/lib/test/unit/collector/load.rb +144 -0
  99. data/test-unit/lib/test/unit/collector/objectspace.rb +34 -0
  100. data/test-unit/lib/test/unit/color-scheme.rb +102 -0
  101. data/test-unit/lib/test/unit/color.rb +96 -0
  102. data/test-unit/lib/test/unit/diff.rb +724 -0
  103. data/test-unit/lib/test/unit/error.rb +130 -0
  104. data/test-unit/lib/test/unit/exceptionhandler.rb +39 -0
  105. data/test-unit/lib/test/unit/failure.rb +136 -0
  106. data/test-unit/lib/test/unit/fixture.rb +176 -0
  107. data/test-unit/lib/test/unit/notification.rb +129 -0
  108. data/test-unit/lib/test/unit/omission.rb +191 -0
  109. data/test-unit/lib/test/unit/pending.rb +150 -0
  110. data/test-unit/lib/test/unit/priority.rb +180 -0
  111. data/test-unit/lib/test/unit/runner/console.rb +52 -0
  112. data/test-unit/lib/test/unit/runner/emacs.rb +8 -0
  113. data/test-unit/lib/test/unit/runner/tap.rb +8 -0
  114. data/test-unit/lib/test/unit/testcase.rb +476 -0
  115. data/test-unit/lib/test/unit/testresult.rb +89 -0
  116. data/test-unit/lib/test/unit/testsuite.rb +110 -0
  117. data/test-unit/lib/test/unit/ui/console/outputlevel.rb +14 -0
  118. data/test-unit/lib/test/unit/ui/console/testrunner.rb +466 -0
  119. data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +63 -0
  120. data/test-unit/lib/test/unit/ui/tap/testrunner.rb +92 -0
  121. data/test-unit/lib/test/unit/ui/testrunner.rb +28 -0
  122. data/test-unit/lib/test/unit/ui/testrunnermediator.rb +77 -0
  123. data/test-unit/lib/test/unit/ui/testrunnerutilities.rb +41 -0
  124. data/test-unit/lib/test/unit/util/backtracefilter.rb +41 -0
  125. data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
  126. data/test-unit/lib/test/unit/util/observable.rb +90 -0
  127. data/test-unit/lib/test/unit/util/procwrapper.rb +48 -0
  128. data/test-unit/lib/test/unit/version.rb +7 -0
  129. data/test-unit/sample/adder.rb +13 -0
  130. data/test-unit/sample/subtracter.rb +12 -0
  131. data/test-unit/sample/test_adder.rb +20 -0
  132. data/test-unit/sample/test_subtracter.rb +20 -0
  133. data/test-unit/sample/test_user.rb +23 -0
  134. data/test-unit/test/collector/test-descendant.rb +133 -0
  135. data/test-unit/test/collector/test-load.rb +442 -0
  136. data/test-unit/test/collector/test_dir.rb +406 -0
  137. data/test-unit/test/collector/test_objectspace.rb +100 -0
  138. data/test-unit/test/run-test.rb +15 -0
  139. data/test-unit/test/test-attribute.rb +86 -0
  140. data/test-unit/test/test-color-scheme.rb +67 -0
  141. data/test-unit/test/test-color.rb +47 -0
  142. data/test-unit/test/test-diff.rb +518 -0
  143. data/test-unit/test/test-emacs-runner.rb +60 -0
  144. data/test-unit/test/test-fixture.rb +287 -0
  145. data/test-unit/test/test-notification.rb +33 -0
  146. data/test-unit/test/test-omission.rb +81 -0
  147. data/test-unit/test/test-pending.rb +70 -0
  148. data/test-unit/test/test-priority.rb +119 -0
  149. data/test-unit/test/test-testcase.rb +544 -0
  150. data/test-unit/test/test_assertions.rb +1151 -0
  151. data/test-unit/test/test_error.rb +26 -0
  152. data/test-unit/test/test_failure.rb +33 -0
  153. data/test-unit/test/test_testresult.rb +113 -0
  154. data/test-unit/test/test_testsuite.rb +129 -0
  155. data/test-unit/test/testunit-test-util.rb +14 -0
  156. data/test-unit/test/ui/test_testrunmediator.rb +20 -0
  157. data/test-unit/test/util/test-method-owner-finder.rb +38 -0
  158. data/test-unit/test/util/test_backtracefilter.rb +41 -0
  159. data/test-unit/test/util/test_observable.rb +102 -0
  160. data/test-unit/test/util/test_procwrapper.rb +36 -0
  161. data/test/.gitignore +1 -0
  162. data/test/groonga-test-utils.rb +134 -0
  163. data/test/run-test.rb +58 -0
  164. data/test/test-array.rb +90 -0
  165. data/test/test-column.rb +316 -0
  166. data/test/test-context-select.rb +93 -0
  167. data/test/test-context.rb +73 -0
  168. data/test/test-database.rb +113 -0
  169. data/test/test-encoding.rb +33 -0
  170. data/test/test-exception.rb +93 -0
  171. data/test/test-expression-builder.rb +217 -0
  172. data/test/test-expression.rb +134 -0
  173. data/test/test-fix-size-column.rb +65 -0
  174. data/test/test-gqtp.rb +72 -0
  175. data/test/test-hash.rb +305 -0
  176. data/test/test-index-column.rb +81 -0
  177. data/test/test-logger.rb +37 -0
  178. data/test/test-patricia-trie.rb +205 -0
  179. data/test/test-procedure.rb +37 -0
  180. data/test/test-query.rb +22 -0
  181. data/test/test-record.rb +243 -0
  182. data/test/test-remote.rb +54 -0
  183. data/test/test-schema-view.rb +90 -0
  184. data/test/test-schema.rb +459 -0
  185. data/test/test-snippet.rb +130 -0
  186. data/test/test-table-cursor.rb +153 -0
  187. data/test/test-table-offset-and-limit.rb +102 -0
  188. data/test/test-table-select-normalize.rb +53 -0
  189. data/test/test-table-select.rb +150 -0
  190. data/test/test-table.rb +594 -0
  191. data/test/test-type.rb +71 -0
  192. data/test/test-variable-size-column.rb +98 -0
  193. data/test/test-variable.rb +28 -0
  194. data/test/test-vector-column.rb +76 -0
  195. data/test/test-version.rb +31 -0
  196. data/test/test-view.rb +72 -0
  197. data/text/TUTORIAL.ja.rdoc +392 -0
  198. data/text/expression.rdoc +284 -0
  199. metadata +276 -0
@@ -0,0 +1,85 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2009-2010 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
+ class PatriciaTrie
20
+ # call-seq:
21
+ # patricia_trie.tag_keys(text, options={}) {|record, word| ...} -> String
22
+ #
23
+ # _text_を走査し、レコードのキーとマッチする部分文字列ごとに
24
+ # そのレコードが_record_として、その部分文字列が_word_として、
25
+ # ブロックが呼び出される。ブロックから返された文字列が元の部
26
+ # 分文字列と置換される。全てのヒットに対してのその置換処理が
27
+ # 行われた文字列が返される。
28
+ #
29
+ # _options_に指定可能な値は以下の通り。
30
+ #
31
+ # [+:other_text_handler+]
32
+ # マッチした部分文字列の前後の文字列を変換するProcを指
33
+ # 定する。
34
+ #
35
+ # 例:
36
+ # include ERB::Util
37
+ # Groonga::Context.default_options = {:encoding => "utf-8"}
38
+ # words = Groonga::PatriciaTrie.create(:key_type => "ShortText",
39
+ # :key_normalize => true)
40
+ # words.add('ガッ')
41
+ # words.add('MUTEKI')
42
+ #
43
+ # text = 'muTEki マッチしない <> ガッ'
44
+ # other_text_handler = Proc.new do |string|
45
+ # h(string)
46
+ # end
47
+ # words.tag_keys(text) do |record, word|
48
+ # "<span class=\"keyword\">#{h(word)}(#{h(record.key)})</span>\n"
49
+ # end
50
+ # # =>
51
+ # # "<span class=\"keyword\">muTEki(muteki)</span>\n" +
52
+ # # " マッチしない &lt;&gt; " +
53
+ # # "<span class=\"keyword\">ガッ(ガッ)</span>\n"
54
+ def tag_keys(text, options={})
55
+ options ||= {}
56
+ other_text_handler = options[:other_text_handler]
57
+ position = 0
58
+ result = ''
59
+ if text.respond_to?(:encoding)
60
+ encoding = text.encoding
61
+ bytes = text.dup.force_encoding("ascii-8bit")
62
+ else
63
+ encoding = nil
64
+ bytes = text
65
+ end
66
+ scan(text) do |record, word, start, length|
67
+ previous_text = bytes[position...start]
68
+ previous_text.force_encoding(encoding) if encoding
69
+ if other_text_handler
70
+ previous_text = other_text_handler.call(previous_text)
71
+ end
72
+ result << previous_text
73
+ result << yield(record, word)
74
+ position = start + length
75
+ end
76
+ last_text = bytes[position..-1]
77
+ unless last_text.empty?
78
+ last_text.force_encoding(encoding) if encoding
79
+ last_text = other_text_handler.call(last_text) if other_text_handler
80
+ result << last_text
81
+ end
82
+ result
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,311 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2009-2010 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
+ class Record
20
+ # レコードが所属するテーブル
21
+ attr_reader :table
22
+ # レコードのID
23
+ attr_reader :id
24
+ # _table_の_id_に対応するレコードを作成する。_values_には各
25
+ # カラムに設定する値を以下のような形式で指定する。
26
+ #
27
+ # [
28
+ # ["カラム名", 値],
29
+ # ["カラム名", 値],
30
+ # ...,
31
+ # ]
32
+ def initialize(table, id, values=nil)
33
+ @table = table
34
+ @id = id
35
+ if values
36
+ values.each do |name, value|
37
+ self[name] = value
38
+ end
39
+ end
40
+ end
41
+
42
+ # call-seq:
43
+ # record == other -> true/false
44
+ #
45
+ # _record_と_other_が同じgroongaのレコードなら+true+を返し、
46
+ # そうでなければ+false+を返す。
47
+ def ==(other)
48
+ self.class == other.class and
49
+ [table, id] == [other.table, other.id]
50
+ end
51
+
52
+ # call-seq:
53
+ # record[column_name] -> 値
54
+ #
55
+ # このレコードの_column_name_で指定されたカラムの値を返す。
56
+ def [](column_name)
57
+ @table.column_value(@id, column_name, :id => true)
58
+ end
59
+
60
+ # call-seq:
61
+ # record[column_name] = 値
62
+ #
63
+ # このレコードの_column_name_で指定されたカラムの値を設定す
64
+ # る。
65
+ def []=(column_name, value)
66
+ @table.set_column_value(@id, column_name, value, :id => true)
67
+ end
68
+
69
+ # call-seq:
70
+ # record.append(column_name, value)
71
+ #
72
+ # このレコードの_column_name_で指定されたカラムの値の最後に
73
+ # _value_を追加する。
74
+ def append(column_name, value)
75
+ column(column_name).append(@id, value)
76
+ end
77
+
78
+ # call-seq:
79
+ # record.prepend(column_name, value)
80
+ #
81
+ # このレコードの_column_name_で指定されたカラムの値の最初に
82
+ # _value_を追加する。
83
+ def prepend(column_name, value)
84
+ column(column_name).prepend(@id, value)
85
+ end
86
+
87
+ # call-seq:
88
+ # record.have_column?(name) -> true/false
89
+ #
90
+ # 名前が_name_のカラムがレコードの所属するテーブルで定義され
91
+ # ているなら+true+を返す。
92
+ def have_column?(name)
93
+ column(name).is_a?(Groonga::Column)
94
+ rescue Groonga::NoSuchColumn
95
+ false
96
+ end
97
+
98
+ # call-seq:
99
+ # record.reference_column?(name) -> true/false
100
+ #
101
+ # 名前が_name_のカラムが参照カラムであるなら+true+を返す。
102
+ def reference_column?(name)
103
+ column(name).range.is_a?(Groonga::Table)
104
+ end
105
+
106
+ # call-seq:
107
+ # record.search(name, query, options={}) -> Groonga::Hash
108
+ #
109
+ # 名前が_name_のGroonga::IndexColumnのsearchメソッドを呼ぶ。
110
+ # _query_と_options_はそのメソッドにそのまま渡される。詳しく
111
+ # はGroonga::IndexColumn#searchを参照。
112
+ def search(name, query, options={})
113
+ column(name).search(query, options)
114
+ end
115
+
116
+ # call-seq:
117
+ # record.key -> 主キー
118
+ #
119
+ # レコードの主キーを返す。
120
+ #
121
+ # _record_が所属するテーブルがGroonga:::Arrayの場合は常
122
+ # に+nil+を返す。
123
+ def key
124
+ if @table.is_a?(Groonga::Array)
125
+ nil
126
+ else
127
+ @key ||= @table.key(@id)
128
+ end
129
+ end
130
+
131
+ # call-seq:
132
+ # record.score -> スコア値
133
+ #
134
+ # レコードのスコア値を返す。検索結果として生成されたテーブル
135
+ # のみに定義される。
136
+ def score
137
+ self["._score"]
138
+ end
139
+
140
+ # call-seq:
141
+ # record.n_sub_records -> 件数
142
+ #
143
+ # 主キーの値が同一であったレコードの件数を返す。検索結果とし
144
+ # て生成されたテーブルのみに定義される。
145
+ def n_sub_records
146
+ self["._nsubrecs"]
147
+ end
148
+
149
+ # call-seq:
150
+ # record.value -> 値
151
+ #
152
+ # レコードの値を返す。
153
+ def value
154
+ @table.value(@id, :id => true)
155
+ end
156
+
157
+ # call-seq:
158
+ # record.value = 値
159
+ #
160
+ # レコードの値を設定する。既存の値は上書きされる。
161
+ def value=(value)
162
+ @table.set_value(@id, value, :id => true)
163
+ end
164
+
165
+ # call-seq:
166
+ # record.increment!(name, delta=nil)
167
+ #
168
+ # このレコードの_name_で指定されたカラムの値を_delta_だけ増
169
+ # 加する。_delta_が+nil+の場合は1増加する。
170
+ def increment!(name, delta=nil)
171
+ column(name).increment!(@id, delta)
172
+ end
173
+
174
+ # call-seq:
175
+ # record.decrement!(name, delta=nil)
176
+ #
177
+ # このレコードの_name_で指定されたカラムの値を_delta_だけ減
178
+ # 少する。_delta_が+nil+の場合は1減少する。
179
+ def decrement!(name, delta=nil)
180
+ column(name).decrement!(@id, delta)
181
+ end
182
+
183
+ # call-seq:
184
+ # record.columns -> Groonga::Columnの配列
185
+ #
186
+ # レコードが所属するテーブルの全てのカラムを返す。
187
+ def columns
188
+ @table.columns
189
+ end
190
+
191
+ # call-seq:
192
+ # attributes -> Hash
193
+ #
194
+ # レコードが所属しているテーブルで定義されているインデックス
195
+ # 型のカラムでない全カラムを対象とし、カラムの名前をキーとし
196
+ # たこのレコードのカラムの値のハッシュを返す。
197
+ def attributes
198
+ attributes = {"id" => id}
199
+ _key = key
200
+ attributes["key"] = _key if _key
201
+ table_name = @table.name
202
+ columns.each do |column|
203
+ next if column.is_a?(Groonga::IndexColumn)
204
+ value = column[@id]
205
+ # TODO: support recursive reference.
206
+ value = value.attributes if value.is_a?(Groonga::Record)
207
+ attributes[column.local_name] = value
208
+ end
209
+ attributes
210
+ end
211
+
212
+ # call-seq:
213
+ # record.delete
214
+ #
215
+ # レコードを削除する。
216
+ def delete
217
+ @table.delete(@id)
218
+ end
219
+
220
+ # call-seq:
221
+ # record.lock(options={})
222
+ # record.lock(options={}) {...}
223
+ #
224
+ # レコードが所属するテーブルをロックする。ロックに失敗した場
225
+ # 合はGroonga::ResourceDeadlockAvoided例外が発生する。
226
+ #
227
+ # ブロックを指定した場合はブロックを抜けたときにunlockする。
228
+ #
229
+ # 利用可能なオプションは以下の通り。
230
+ #
231
+ # [_:timeout_]
232
+ # ロックを獲得できなかった場合は_:timeout_秒間ロックの獲
233
+ # 得を試みる。_:timeout_秒以内にロックを獲得できなかった
234
+ # 場合は例外が発生する。
235
+ def lock(options={}, &block)
236
+ @table.lock(options.merge(:id => @id), &block)
237
+ end
238
+
239
+ # call-seq:
240
+ # record.unlock(options={})
241
+ #
242
+ # レコードが所属するテーブルのロックを解除する。
243
+ #
244
+ # 利用可能なオプションは現在は無い。
245
+ def unlock(options={})
246
+ @table.unlock(options.merge(:id => @id))
247
+ end
248
+
249
+ # call-seq:
250
+ # record.clear_lock(options={})
251
+ #
252
+ # レコードが所属するテーブルのロックを強制的に解除する。
253
+ #
254
+ # 利用可能なオプションは現在は無い。
255
+ def clear_lock(options={})
256
+ @table.clear_lock(options.merge(:id => @id))
257
+ end
258
+
259
+ # call-seq:
260
+ # record.locked?(options={}) -> true/false
261
+ #
262
+ # レコードが所属するテーブルがロックされていれば+true+を返す。
263
+ #
264
+ # 利用可能なオプションは現在は無い。
265
+ def locked?(options={})
266
+ @table.locked?(options.merge(:id => @id))
267
+ end
268
+
269
+ def methods(include_inherited=true) # :nodoc:
270
+ _methods = super
271
+ return _methods unless include_inherited
272
+ columns.each do |column|
273
+ name = column.local_name
274
+ _methods << name
275
+ _methods << "#{name}="
276
+ end
277
+ _methods
278
+ end
279
+
280
+ def respond_to?(name) # :nodoc:
281
+ super or !@table.column(name.to_s.sub(/=\z/, '')).nil?
282
+ end
283
+
284
+ private
285
+ def column(name) # :nodoc:
286
+ _column = @table.column(name.to_s)
287
+ raise NoSuchColumn, "column(#{name.inspect}) is nil" if _column.nil?
288
+ _column
289
+ end
290
+
291
+ def method_missing(name, *args, &block)
292
+ if /=\z/ =~ name.to_s
293
+ base_name = $PREMATCH
294
+ is_setter = true
295
+ else
296
+ base_name = name.to_s
297
+ is_setter = false
298
+ end
299
+ _column = @table.column(base_name)
300
+ if _column
301
+ if is_setter
302
+ _column.send("[]=", @id, *args, &block)
303
+ else
304
+ _column.send("[]", @id, *args, &block)
305
+ end
306
+ else
307
+ super
308
+ end
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,1191 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2009-2010 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
+ #
23
+ # Groonga::Schemaを使うことにより簡単にテーブルやカラムを
24
+ # 追加・削除することができる。
25
+ #
26
+ # http://qwik.jp/senna/senna2.files/rect4605.png
27
+ # のようなスキーマを定義する場合は以下のようになる。
28
+ #
29
+ # Groonga::Schema.define do |schema|
30
+ # schema.create_table("Items") do |table|
31
+ # table.short_text("title")
32
+ # end
33
+ #
34
+ # schema.create_table("Users") do |table|
35
+ # table.short_text("name")
36
+ # end
37
+ #
38
+ # schema.create_table("comments") do |table|
39
+ # table.reference("item", "Items")
40
+ # table.reference("author", "Users")
41
+ # table.text("content")
42
+ # table.time("issued")
43
+ # end
44
+ # end
45
+ class Schema
46
+ class << self
47
+
48
+ # call-seq:
49
+ # Groonga::Schema.define(options={}) {|schema| ...}
50
+ #
51
+ # スキーマを定義する。ブロックにはGroonga::Schemaオブ
52
+ # ジェクトがわたるので、そのオブジェクトを利用してスキー
53
+ # マを定義する。以下の省略形。
54
+ #
55
+ # schema = Groonga::Scheme.new(options)
56
+ # ...
57
+ # schema.define
58
+ #
59
+ # _options_に指定可能な値は以下の通り。
60
+ #
61
+ # [+:context+]
62
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
63
+ # 省略した場合はGroonga::Context.defaultを使用する。
64
+ def define(options={})
65
+ schema = new(options)
66
+ yield(schema)
67
+ schema.define
68
+ end
69
+
70
+ # call-seq:
71
+ # Groonga::Schema.create_table(name, options={}) {|table| ...}
72
+ #
73
+ # 名前が_name_のテーブルを作成する。以下の省略形。
74
+ #
75
+ # Groonga::Schema.define do |schema|
76
+ # schema.create_table(name, options) do |table|
77
+ # ...
78
+ # end
79
+ # end
80
+ #
81
+ # ブロックにはGroonga::Schema::TableDefinitionオブジェ
82
+ # クトがわたるので、そのオブジェクトを利用してテーブル
83
+ # の詳細を定義する。
84
+ #
85
+ # _options_に指定可能な値は以下の通り。
86
+ #
87
+ # [+:force+]
88
+ # +true+を指定すると既存の同名のテーブルが存在してい
89
+ # ても、強制的にテーブルを作成する。
90
+ #
91
+ # [+:type+]
92
+ # テーブルの型を指定する。+:array+, +:hash+,
93
+ # +:patricia_trie+のいずれかを指定する。デフォルトで
94
+ # は+:array+になる。
95
+ #
96
+ # [+:context+]
97
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
98
+ # 省略した場合はGroonga::Context.defaultを使用する。
99
+ #
100
+ # [+:path+]
101
+ # テーブルを保存するパスを指定する。パスを指定すると
102
+ # 永続テーブルになる。
103
+ #
104
+ # [+:persistent+]
105
+ # テーブルを永続テーブルとする。+:path:+を省略した場
106
+ # 合はパス名は自動的に作成される。デフォルトでは永続
107
+ # テーブルとなる。
108
+ #
109
+ # [+:value_type+]
110
+ # 値の型を指定する。省略すると値のための領域を確保しない。
111
+ # 値を保存したい場合は必ず指定すること。
112
+ #
113
+ # [+:sub_records+]
114
+ # +true+を指定するとGroonga::Table#groupでグループ化
115
+ # したときに、Groonga::Record#n_sub_recordsでグルー
116
+ # プに含まれるレコードの件数を取得できる。
117
+ #
118
+ # 以下は+:type+に+:hash+あるいは+:patricia_trie+を指定
119
+ # した時に指定可能。
120
+ #
121
+ # [+:key_type+]
122
+ # キーの種類を示すオブジェクトを指定する。キーの種類
123
+ # には型名("Int32"や"ShortText"など)または
124
+ # Groonga::Typeまたはテーブル(Groonga::Array、
125
+ # Groonga::Hash、Groonga::PatriciaTrieのどれか)を指
126
+ # 定する。
127
+ #
128
+ # Groonga::Typeを指定した場合は、その型が示す範囲の
129
+ # 値をキーとして使用する。ただし、キーの最大サイズは
130
+ # 4096バイトであるため、Groonga::Type::TEXTや
131
+ # Groonga::Type::LONG_TEXTは使用できない。
132
+ #
133
+ # テーブルを指定した場合はレコードIDをキーとして使用
134
+ # する。指定したテーブルのGroonga::Recordをキーとし
135
+ # て使用することもでき、その場合は自動的に
136
+ # Groonga::RecordからレコードIDを取得する。
137
+ #
138
+ # 省略した場合は文字列をキーとして使用する。この場合、
139
+ # 4096バイトまで使用可能である。
140
+ #
141
+ # [+:default_tokenizer+]
142
+ # Groonga::IndexColumnで使用するトークナイザを指定す
143
+ # る。デフォルトでは何も設定されていないので、テーブ
144
+ # ルにGroonga::IndexColumnを定義する場合は
145
+ # <tt>"TokenBigram"</tt>などを指定する必要がある。
146
+ #
147
+ # 以下は+:type+に+:patricia_trie+を指定した時に指定可能。
148
+ #
149
+ # [+:key_normalize+]
150
+ # +true+を指定するとキーを正規化する。
151
+ #
152
+ # [+:key_with_sis+]
153
+ # +true+を指定するとキーの文字列の全suffixが自動的に
154
+ # 登録される。
155
+ def create_table(name, options={}, &block)
156
+ define do |schema|
157
+ schema.create_table(name, options, &block)
158
+ end
159
+ end
160
+
161
+ # 名前が_name_のテーブルを削除する。
162
+ #
163
+ # _options_に指定可能な値は以下の通り。
164
+ #
165
+ # [+:context+]
166
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
167
+ # 省略した場合はGroonga::Context.defaultを使用する。
168
+ def remove_table(name, options={})
169
+ define do |schema|
170
+ schema.remove_table(name, options)
171
+ end
172
+ end
173
+
174
+ # call-seq:
175
+ # Groonga::Schema.change_table(name, options={}) {|table| ...}
176
+ #
177
+ # 名前が_name_のテーブルを変更する。以下の省略形。
178
+ #
179
+ # Groonga::Schema.define do |schema|
180
+ # schema.change_table(name, options) do |table|
181
+ # ...
182
+ # end
183
+ # end
184
+ #
185
+ # ブロックにはGroonga::Schema::TableDefinitionオブジェ
186
+ # クトがわたるので、そのオブジェクトを利用してテーブル
187
+ # の詳細を定義する。
188
+ #
189
+ # _options_に指定可能な値は以下の通り。
190
+ #
191
+ # [+:context+]
192
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
193
+ # 省略した場合はGroonga::Context.defaultを使用する。
194
+ def change_table(name, options={}, &block)
195
+ define do |schema|
196
+ schema.change_table(name, options, &block)
197
+ end
198
+ end
199
+
200
+ # call-seq:
201
+ # Groonga::Schema.create_view(name, options={}) {|view| ...}
202
+ #
203
+ # 名前が_name_のビューを作成する。以下の省略形。
204
+ #
205
+ # Groonga::Schema.define do |schema|
206
+ # schema.create_view(name, options) do |view|
207
+ # ...
208
+ # end
209
+ # end
210
+ #
211
+ # ブロックにはGroonga::Schema::ViewDefinitionオブジェ
212
+ # クトがわたるので、そのオブジェクトを利用してビュー
213
+ # の詳細を定義する。
214
+ #
215
+ # _options_に指定可能な値は以下の通り。
216
+ #
217
+ # [+:force+]
218
+ # +true+を指定すると既存の同名のビューが存在してい
219
+ # ても、強制的にビューを作成する。
220
+ #
221
+ # [+:context+]
222
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
223
+ # 省略した場合はGroonga::Context.defaultを使用する。
224
+ #
225
+ # [+:path+]
226
+ # ビューを保存するパスを指定する。パスを指定すると
227
+ # 永続ビューになる。
228
+ #
229
+ # [+:persistent+]
230
+ # ビューを永続ビューとする。+:path:+を省略した場
231
+ # 合はパス名は自動的に作成される。デフォルトでは永続
232
+ # ビューとなる。
233
+ def create_view(name, options={}, &block)
234
+ define do |schema|
235
+ schema.create_view(name, options, &block)
236
+ end
237
+ end
238
+
239
+ # 名前が_name_のテーブルを削除する。
240
+ #
241
+ # _options_に指定可能な値は以下の通り。
242
+ #
243
+ # [+:context+]
244
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
245
+ # 省略した場合はGroonga::Context.defaultを使用する。
246
+ def remove_view(name, options={})
247
+ define do |schema|
248
+ schema.remove_view(name, options)
249
+ end
250
+ end
251
+
252
+ # call-seq:
253
+ # Groonga::Schema.change_view(name, options={}) {|view| ...}
254
+ #
255
+ # 名前が_name_のビューを変更する。以下の省略形。
256
+ #
257
+ # Groonga::Schema.define do |schema|
258
+ # schema.change_view(name, options) do |view|
259
+ # ...
260
+ # end
261
+ # end
262
+ #
263
+ # ブロックにはGroonga::Schema::ViewDefinitionオブジェ
264
+ # クトがわたるので、そのオブジェクトを利用してテーブル
265
+ # の詳細を定義する。
266
+ #
267
+ # _options_に指定可能な値は以下の通り。
268
+ #
269
+ # [+:context+]
270
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
271
+ # 省略した場合はGroonga::Context.defaultを使用する。
272
+ def change_view(name, options={}, &block)
273
+ define do |schema|
274
+ schema.change_view(name, options, &block)
275
+ end
276
+ end
277
+
278
+ # スキーマの内容を文字列で返す。返された値は
279
+ # Groonga::Schema.restoreすることによりスキーマ内に組
280
+ # み込むことができる。
281
+ #
282
+ # dump.rb:
283
+ # File.open("/tmp/groonga-schema.rb", "w") do |schema|
284
+ # dumped_text = Groonga::Schema.dump
285
+ # end
286
+ #
287
+ # restore.rb:
288
+ # dumped_text = Groonga::Schema.dump
289
+ # Groonga::Database.create(:path => "/tmp/new-db.grn")
290
+ # Groonga::Schema.restore(dumped_text)
291
+ #
292
+ # _options_に指定可能な値は以下の通り。
293
+ #
294
+ # [+:context+]
295
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
296
+ # 省略した場合はGroonga::Context.defaultを使用する。
297
+ def dump(options={})
298
+ Dumper.new(options).dump
299
+ end
300
+
301
+ # Groonga::Schema.dumpで文字列化したスキーマを組み込む。
302
+ def restore(dumped_text, options={})
303
+ define(options) do |schema|
304
+ schema.load(dumped_text)
305
+ end
306
+ end
307
+
308
+ def normalize_type(type) # :nodoc:
309
+ return type if type.nil?
310
+ return type if type.is_a?(Groonga::Object)
311
+ case type.to_s
312
+ when "string"
313
+ "ShortText"
314
+ when "text"
315
+ "Text"
316
+ when "int", "integer"
317
+ "Int32"
318
+ when "float"
319
+ "Float"
320
+ when "decimal"
321
+ "Int64"
322
+ when "datetime", "timestamp", "time", "date"
323
+ "Time"
324
+ when "binary"
325
+ "LongText"
326
+ when "boolean"
327
+ "Bool"
328
+ else
329
+ type
330
+ end
331
+ end
332
+ end
333
+
334
+ # スキーマ定義を開始する。
335
+ #
336
+ # _options_に指定可能な値は以下の通り。
337
+ #
338
+ # [+:context+]
339
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
340
+ # 省略した場合はGroonga::Context.defaultを使用する。
341
+ def initialize(options={})
342
+ @options = (options || {}).dup
343
+ @definitions = []
344
+ end
345
+
346
+ # 定義されたスキーマ定義を実際に実行する。
347
+ def define
348
+ @definitions.each do |definition|
349
+ definition.define
350
+ end
351
+ end
352
+
353
+ # Groonga::Schema.dumpで返されたスキーマの内容を読み込む。
354
+ #
355
+ # 読み込まれた内容は#defineを呼び出すまでは実行されない
356
+ # ことに注意すること。
357
+ def load(dumped_text)
358
+ instance_eval(dumped_text)
359
+ end
360
+
361
+ # call-seq:
362
+ # schema.create_table(name, options={}) {|table| ...}
363
+ #
364
+ # 名前が_name_のテーブルを作成する。
365
+ #
366
+ # テーブルの作成は#defineを呼び出すまでは実行されないこ
367
+ # とに注意すること。
368
+ #
369
+ # _options_に指定可能な値は以下の通り。
370
+ #
371
+ # [+:force+]
372
+ # +true+を指定すると既存の同名のテーブルが存在してい
373
+ # ても、強制的にテーブルを作成する。
374
+ #
375
+ # [+:type+]
376
+ # テーブルの型を指定する。+:array+, +:hash+,
377
+ # +:patricia_trie+のいずれかを指定する。デフォルトで
378
+ # は+:array+になる。
379
+ #
380
+ # [+:context+]
381
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
382
+ # 省略した場合はGroonga::Schema.newで指定した
383
+ # Groonga::Contextを使用する。Groonga::Schema.newで指
384
+ # 定していない場合はGroonga::Context.defaultを使用する。
385
+ #
386
+ # [+:path+]
387
+ # テーブルを保存するパスを指定する。パスを指定すると
388
+ # 永続テーブルになる。
389
+ #
390
+ # [+:persistent+]
391
+ # テーブルを永続テーブルとする。+:path:+を省略した場
392
+ # 合はパス名は自動的に作成される。デフォルトでは永続
393
+ # テーブルとなる。
394
+ #
395
+ # [+:value_type+]
396
+ # 値の型を指定する。省略すると値のための領域を確保しな
397
+ # い。値を保存したい場合は必ず指定すること。
398
+ #
399
+ # 参考: Groonga::Type.new
400
+ #
401
+ # [+:sub_records+]
402
+ # +true+を指定するとGroonga::Table#groupでグループ化
403
+ # したときに、Groonga::Record#n_sub_recordsでグルー
404
+ # プに含まれるレコードの件数を取得できる。
405
+ #
406
+ # 以下は+:type+に+:hash+あるいは+:patricia_trie+を指定
407
+ # した時に指定可能。
408
+ #
409
+ # [+:key_type+]
410
+ # キーの種類を示すオブジェクトを指定する。キーの種類
411
+ # には型名("Int32"や"ShortText"など)または
412
+ # Groonga::Typeまたはテーブル(Groonga::Array、
413
+ # Groonga::Hash、Groonga::PatriciaTrieのどれか)を指
414
+ # 定する。
415
+ #
416
+ # Groonga::Typeを指定した場合は、その型が示す範囲の
417
+ # 値をキーとして使用する。ただし、キーの最大サイズは
418
+ # 4096バイトであるため、Groonga::Type::TEXTや
419
+ # Groonga::Type::LONG_TEXTは使用できない。
420
+ #
421
+ # テーブルを指定した場合はレコードIDをキーとして使用
422
+ # する。指定したテーブルのGroonga::Recordをキーとし
423
+ # て使用することもでき、その場合は自動的に
424
+ # Groonga::RecordからレコードIDを取得する。
425
+ #
426
+ # 省略した場合は文字列をキーとして使用する。この場合、
427
+ # 4096バイトまで使用可能である。
428
+ #
429
+ # [+:default_tokenizer+]
430
+ # Groonga::IndexColumnで使用するトークナイザを指定す
431
+ # る。デフォルトでは何も設定されていないので、テーブ
432
+ # ルにGroonga::IndexColumnを定義する場合は
433
+ # <tt>"TokenBigram"</tt>などを指定する必要がある。
434
+ #
435
+ # 以下は+:type+に+:patricia_trie+を指定した時に指定可能。
436
+ #
437
+ # [+:key_normalize+]
438
+ # +true+を指定するとキーを正規化する。
439
+ #
440
+ # [+:key_with_sis+]
441
+ # +true+を指定するとキーの文字列の全suffixが自動的に
442
+ # 登録される。
443
+ def create_table(name, options={})
444
+ definition = TableDefinition.new(name, @options.merge(options || {}))
445
+ yield(definition)
446
+ @definitions << definition
447
+ end
448
+
449
+ # 名前が_name_のテーブルを削除する。
450
+ #
451
+ # テーブルの削除は#defineを呼び出すまでは実行されないこ
452
+ # とに注意すること。
453
+ #
454
+ # _options_に指定可能な値は以下の通り。
455
+ #
456
+ # [+:context+]
457
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
458
+ # 省略した場合はGroonga::Context.defaultを使用する。
459
+ def remove_table(name, options={})
460
+ definition = TableRemoveDefinition.new(name, @options.merge(options || {}))
461
+ @definitions << definition
462
+ end
463
+
464
+ # call-seq:
465
+ # schema.change_table(name, options={}) {|table| ...}
466
+ #
467
+ # 名前が_name_のテーブルを変更する。
468
+ #
469
+ # テーブルの変更は#defineを呼び出すまでは実行されないこ
470
+ # とに注意すること。
471
+ #
472
+ # _options_に指定可能な値は以下の通り。
473
+ #
474
+ # [+:context+]
475
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
476
+ # 省略した場合はGroonga::Context.defaultを使用する。
477
+ def change_table(name, options={})
478
+ options = @options.merge(options || {}).merge(:change => true)
479
+ definition = TableDefinition.new(name, options)
480
+ yield(definition)
481
+ @definitions << definition
482
+ end
483
+
484
+ # call-seq:
485
+ # schema.create_view(name, options={}) {|view| ...}
486
+ #
487
+ # 名前が_name_のビューを作成する。
488
+ #
489
+ # ビューの作成は#defineを呼び出すまでは実行されないこ
490
+ # とに注意すること。
491
+ #
492
+ # _options_に指定可能な値は以下の通り。
493
+ #
494
+ # [+:force+]
495
+ # +true+を指定すると既存の同名のビューが存在してい
496
+ # ても、強制的にビューを作成する。
497
+ #
498
+ # [+:context+]
499
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
500
+ # 省略した場合はGroonga::Schema.newで指定した
501
+ # Groonga::Contextを使用する。Groonga::Schema.newで指
502
+ # 定していない場合はGroonga::Context.defaultを使用する。
503
+ #
504
+ # [+:path+]
505
+ # テーブルを保存するパスを指定する。パスを指定すると
506
+ # 永続テーブルになる。
507
+ #
508
+ # [+:persistent+]
509
+ # テーブルを永続テーブルとする。+:path:+を省略した場
510
+ # 合はパス名は自動的に作成される。デフォルトでは永続
511
+ # テーブルとなる。
512
+ def create_view(name, options={})
513
+ definition = ViewDefinition.new(name, @options.merge(options || {}))
514
+ yield(definition)
515
+ @definitions << definition
516
+ end
517
+
518
+ # 名前が_name_のビューを削除する。
519
+ #
520
+ # ビューの削除は#defineを呼び出すまでは実行されないことに
521
+ # 注意すること。
522
+ #
523
+ # _options_に指定可能な値は以下の通り。
524
+ #
525
+ # [+:context+]
526
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
527
+ # 省略した場合はGroonga::Context.defaultを使用する。
528
+ def remove_view(name, options={})
529
+ definition = ViewRemoveDefinition.new(name, @options.merge(options || {}))
530
+ @definitions << definition
531
+ end
532
+
533
+ # call-seq:
534
+ # schema.change_view(name, options={}) {|table| ...}
535
+ #
536
+ # 名前が_name_のビューを変更する。
537
+ #
538
+ # ビューの変更は#defineを呼び出すまでは実行されないこ
539
+ # とに注意すること。
540
+ #
541
+ # _options_に指定可能な値は以下の通り。
542
+ #
543
+ # [+:context+]
544
+ # スキーマ定義時に使用するGroonga::Contextを指定する。
545
+ # 省略した場合はGroonga::Context.defaultを使用する。
546
+ def change_view(name, options={})
547
+ options = @options.merge(options || {}).merge(:change => true)
548
+ definition = ViewDefinition.new(name, options)
549
+ yield(definition)
550
+ @definitions << definition
551
+ end
552
+
553
+ # スキーマ定義時にGroonga::Schema.create_tableや
554
+ # Groonga::Schema#create_tableからブロックに渡されてくる
555
+ # オブジェクト
556
+ class TableDefinition
557
+ # テーブルの名前
558
+ attr_reader :name
559
+
560
+ def initialize(name, options) # :nodoc:
561
+ @name = name
562
+ @name = @name.to_s if @name.is_a?(Symbol)
563
+ @definitions = []
564
+ validate_options(options)
565
+ @options = options
566
+ @table_type = table_type
567
+ end
568
+
569
+ def define # :nodoc:
570
+ table = context[@name]
571
+ if @options[:change]
572
+ raise ArgumentError, "table doesn't exist: #{@name}" if table.nil?
573
+ else
574
+ if table and @options[:force]
575
+ table.remove
576
+ table = nil
577
+ end
578
+ table ||= @table_type.create(create_options)
579
+ end
580
+ @definitions.each do |definition|
581
+ definition.define(self, table)
582
+ end
583
+ table
584
+ end
585
+
586
+ # 名前が_name_で型が_type_のカラムを作成する。
587
+ #
588
+ # _options_に指定可能な値は以下の通り。
589
+ #
590
+ # [+:force+]
591
+ # +true+を指定すると既存の同名のカラムが存在してい
592
+ # ても、強制的に新しいカラムを作成する。
593
+ #
594
+ # [+:path+]
595
+ # カラムを保存するパス。
596
+ #
597
+ # [+:persistent+]
598
+ # +true+を指定すると永続カラムとなる。+:path+を省略
599
+ # した場合は自動的にパスが付加される。
600
+ #
601
+ # [+:type+]
602
+ # カラムの値の格納方法について指定する。省略した場合は、
603
+ # +:scalar+になる。
604
+ #
605
+ # [+:scalar+]
606
+ # スカラ値(単独の値)を格納する。
607
+ #
608
+ # [+:vector+]
609
+ # 値の配列を格納する。
610
+ #
611
+ # [+:compress+]
612
+ # 値の圧縮方法を指定する。省略した場合は、圧縮しない。
613
+ #
614
+ # [+:zlib+]
615
+ # 値をzlib圧縮して格納する。
616
+ #
617
+ # [+:lzo+]
618
+ # 値をlzo圧縮して格納する。
619
+ def column(name, type, options={})
620
+ definition = self[name, ColumnDefinition]
621
+ if definition.nil?
622
+ definition = ColumnDefinition.new(name, options)
623
+ update_definition(name, ColumnDefinition, definition)
624
+ end
625
+ definition.type = type
626
+ definition.options.merge!(column_options.merge(options))
627
+ self
628
+ end
629
+
630
+ # 名前が_name_のカラムを削除する。
631
+ #
632
+ # _options_に指定可能な値はない(TODO _options_は不要?)。
633
+ #
634
+ def remove_column(name, options={})
635
+ definition = self[name, ColumnRemoveDefinition]
636
+ if definition.nil?
637
+ definition = ColumnRemoveDefinition.new(name, options)
638
+ update_definition(name, ColumnRemoveDefinition, definition)
639
+ end
640
+ definition.options.merge!(options)
641
+ self
642
+ end
643
+
644
+ # call-seq:
645
+ # table.index(target_column_full_name, options={})
646
+ # table.index(target_table, target_column, options={})
647
+ #
648
+ # _target_table_の_target_column_を対象とするインデッ
649
+ # クスカラムを作成する。
650
+ #
651
+ # _target_column_full_name_で指定するときはテーブル名
652
+ # とカラム名を"."でつなげます。例えば、「Users」テーブ
653
+ # ルの「name」カラムのインデックスカラムを指定する場合
654
+ # はこうなります。
655
+ #
656
+ # table.index("Users.name")
657
+ #
658
+ # _options_に指定可能な値は以下の通り。
659
+ #
660
+ # [+:name+]
661
+ # インデックスカラムのカラム名を任意に指定する。
662
+ #
663
+ # [+:force+]
664
+ # +true+を指定すると既存の同名のカラムが存在してい
665
+ # ても、強制的に新しいカラムを作成する。
666
+ #
667
+ # [+:path+]
668
+ # カラムを保存するパス。
669
+ #
670
+ # [+:persistent+]
671
+ # +true+を指定すると永続カラムとなる。+:path+を省略
672
+ # した場合は自動的にパスが付加される。
673
+ #
674
+ # [+:with_section+]
675
+ # 転置索引にsection(段落情報)を合わせて格納する。
676
+ #
677
+ # [+:with_weight+]
678
+ # 転置索引にweight情報を合わせて格納する。
679
+ #
680
+ # [+:with_position+]
681
+ # 転置索引に出現位置情報を合わせて格納する。
682
+ def index(target_table_or_target_column_full_name, *args)
683
+ if args.size > 2
684
+ n_args = args.size + 1
685
+ raise ArgumentError, "wrong number of arguments (#{n_args} for 2 or 3)"
686
+ end
687
+ options = nil
688
+ options = args.pop if args.last.is_a?(::Hash)
689
+ if args.empty?
690
+ target_column_full_name = target_table_or_target_column_full_name
691
+ if target_column_full_name.is_a?(Groonga::Column)
692
+ target_column_full_name = target_column_full_name.name
693
+ end
694
+ target_table, target_column = target_column_full_name.split(/\./, 2)
695
+ else
696
+ target_table = target_table_or_target_column_full_name
697
+ target_column = args.pop
698
+ end
699
+ define_index(target_table, target_column, options || {})
700
+ end
701
+
702
+ # 名前が_name_の32bit符号付き整数のカラムを作成する。
703
+ #
704
+ # _options_に指定可能な値は
705
+ # Groonga::Schema::TableDefinition#columnを参照。
706
+ def integer32(name, options={})
707
+ column(name, "Int32", options)
708
+ end
709
+ alias_method :integer, :integer32
710
+ alias_method :int32, :integer32
711
+
712
+ # 名前が_name_の64bit符号付き整数のカラムを作成する。
713
+ #
714
+ # _options_に指定可能な値は
715
+ # Groonga::Schema::TableDefinition#columnを参照。
716
+ def integer64(name, options={})
717
+ column(name, "Int64", options)
718
+ end
719
+ alias_method :int64, :integer64
720
+
721
+ # 名前が_name_の32bit符号なし整数のカラムを作成する。
722
+ #
723
+ # _options_に指定可能な値は
724
+ # Groonga::Schema::TableDefinition#columnを参照。
725
+ def unsigned_integer32(name, options={})
726
+ column(name, "UInt32", options)
727
+ end
728
+ alias_method :unsigned_integer, :unsigned_integer32
729
+ alias_method :uint32, :unsigned_integer32
730
+
731
+ # 名前が_name_の64bit符号なし整数のカラムを作成する。
732
+ #
733
+ # _options_に指定可能な値は
734
+ # Groonga::Schema::TableDefinition#columnを参照。
735
+ def unsigned_integer64(name, options={})
736
+ column(name, "UInt64", options)
737
+ end
738
+ alias_method :uint64, :unsigned_integer64
739
+
740
+ # 名前が_name_のieee754形式の64bit浮動小数点数のカラム
741
+ # を作成する。
742
+ #
743
+ # _options_に指定可能な値は
744
+ # Groonga::Schema::TableDefinition#columnを参照。
745
+ def float(name, options={})
746
+ column(name, "Float", options)
747
+ end
748
+
749
+ # 名前が_name_の64bit符号付き整数で1970年1月1日0時0分
750
+ # 0秒からの経過マイクロ秒数を格納するカラムを作成する。
751
+ #
752
+ # _options_に指定可能な値は
753
+ # Groonga::Schema::TableDefinition#columnを参照。
754
+ def time(name, options={})
755
+ column(name, "Time", options)
756
+ end
757
+
758
+ # 名前が_name_の4Kbyte以下の文字列を格納できるカラムを
759
+ # 作成する。
760
+ #
761
+ # _options_に指定可能な値は
762
+ # Groonga::Schema::TableDefinition#columnを参照。
763
+ def short_text(name, options={})
764
+ column(name, "ShortText", options)
765
+ end
766
+ alias_method :string, :short_text
767
+
768
+ # 名前が_name_の64Kbyte以下の文字列を格納できるカラムを
769
+ # 作成する。
770
+ #
771
+ # _options_に指定可能な値は
772
+ # Groonga::Schema::TableDefinition#columnを参照。
773
+ def text(name, options={})
774
+ column(name, "Text", options)
775
+ end
776
+
777
+ # 名前が_name_の2Gbyte以下の文字列を格納できるカラムを
778
+ # 作成する。
779
+ #
780
+ # _options_に指定可能な値は
781
+ # Groonga::Schema::TableDefinition#columnを参照。
782
+ def long_text(name, options={})
783
+ column(name, "LongText", options)
784
+ end
785
+
786
+ # 名前が_name_で_table_のレコードIDを格納する参照カラ
787
+ # ムを作成する。
788
+ #
789
+ # _options_に指定可能な値は
790
+ # Groonga::Schema::TableDefinition#columnを参照。
791
+ def reference(name, table, options={})
792
+ column(name, table, options)
793
+ end
794
+
795
+ # 名前が_name_の真偽値を格納できるカラムを作成する。
796
+ #
797
+ # _options_に指定可能な値は
798
+ # Groonga::Schema::TableDefinition#columnを参照。
799
+ def boolean(name, options={})
800
+ column(name, "Bool", options)
801
+ end
802
+ alias_method :bool, :boolean
803
+
804
+ def [](name, definition_class=nil) # :nodoc:
805
+ @definitions.find do |definition|
806
+ definition.name.to_s == name.to_s and
807
+ (definition_class.nil? or definition.is_a?(definition_class))
808
+ end
809
+ end
810
+
811
+ def context # :nodoc:
812
+ @options[:context] || Groonga::Context.default
813
+ end
814
+
815
+ private
816
+ def update_definition(name, definition_class, definition) # :nodoc:
817
+ old_definition = self[name, definition_class]
818
+ if old_definition
819
+ index = @definitions.index(old_definition)
820
+ @definitions[index] = definition
821
+ else
822
+ @definitions << definition
823
+ end
824
+ end
825
+
826
+ AVAILABLE_OPTION_KEYS = [:context, :change, :force,
827
+ :type, :path, :persistent,
828
+ :key_type, :value_type, :sub_records,
829
+ :default_tokenizer,
830
+ :key_normalize, :key_with_sis] # :nodoc:
831
+ def validate_options(options) # :nodoc:
832
+ return if options.nil?
833
+ unknown_keys = options.keys - AVAILABLE_OPTION_KEYS
834
+ unless unknown_keys.empty?
835
+ message = "unknown keys are specified: #{unknown_keys.inspect}"
836
+ message << ": available keys: #{AVAILABLE_OPTION_KEYS.inspect}"
837
+ raise ArgumentError, message
838
+ end
839
+ end
840
+
841
+ def table_type # :nodoc:
842
+ type = @options[:type]
843
+ case type
844
+ when :array, nil
845
+ Groonga::Array
846
+ when :hash
847
+ Groonga::Hash
848
+ when :patricia_trie
849
+ Groonga::PatriciaTrie
850
+ else
851
+ raise ArgumentError, "unknown table type: #{type.inspect}"
852
+ end
853
+ end
854
+
855
+ def create_options # :nodoc:
856
+ common = {
857
+ :name => @name,
858
+ :path => @options[:path],
859
+ :persistent => persistent?,
860
+ :value_type => @options[:value_type],
861
+ :context => context,
862
+ :sub_records => @options[:sub_records],
863
+ }
864
+ key_support_table_common = {
865
+ :key_type => Schema.normalize_type(@options[:key_type]),
866
+ :default_tokenizer => @options[:default_tokenizer],
867
+ }
868
+
869
+ if @table_type == Groonga::Array
870
+ common
871
+ elsif @table_type == Groonga::Hash
872
+ common.merge(key_support_table_common)
873
+ elsif @table_type == Groonga::PatriciaTrie
874
+ options = {
875
+ :key_normalize => @options[:key_normalize],
876
+ :key_with_sis => @options[:key_with_sis],
877
+ }
878
+ common.merge(key_support_table_common).merge(options)
879
+ else
880
+ raise ArgumentError, "unknown table type: #{@table_type.inspect}"
881
+ end
882
+ end
883
+
884
+ def column_options # :nodoc:
885
+ {:persistent => persistent?}
886
+ end
887
+
888
+ def persistent? # :nodoc:
889
+ @options[:persistent].nil? ? true : @options[:persistent]
890
+ end
891
+
892
+ def define_index(target_table, target_column, options)
893
+ name = options.delete(:name)
894
+ name ||= "#{target_table}_#{target_column}".gsub(/\./, "_")
895
+
896
+ definition = self[name, IndexColumnDefinition]
897
+ if definition.nil?
898
+ definition = IndexColumnDefinition.new(name, options)
899
+ update_definition(name, IndexColumnDefinition, definition)
900
+ end
901
+ definition.target_table = target_table
902
+ definition.target_column = target_column
903
+ definition.options.merge!(column_options.merge(options))
904
+ self
905
+ end
906
+ end
907
+
908
+ class TableRemoveDefinition # :nodoc:
909
+ def initialize(name, options={})
910
+ @name = name
911
+ @options = options
912
+ end
913
+
914
+ def define
915
+ context = @options[:context] || Groonga::Context.default
916
+ context[@name].remove
917
+ end
918
+ end
919
+
920
+ # スキーマ定義時にGroonga::Schema.create_viewや
921
+ # Groonga::Schema#create_viewからブロックに渡されてくる
922
+ # オブジェクト
923
+ class ViewDefinition
924
+ # ビューの名前
925
+ attr_reader :name
926
+
927
+ def initialize(name, options) # :nodoc:
928
+ @name = name
929
+ @name = @name.to_s if @name.is_a?(Symbol)
930
+ @tables = []
931
+ validate_options(options)
932
+ @options = options
933
+ end
934
+
935
+ def define # :nodoc:
936
+ view = context[@name]
937
+ if @options[:change]
938
+ raise ArgumentError, "view doesn't exist: #{@name}" if view.nil?
939
+ else
940
+ if view and @options[:force]
941
+ view.remove
942
+ view = nil
943
+ end
944
+ view ||= Groonga::View.create(create_options)
945
+ end
946
+ _context = context
947
+ @tables.each do |table|
948
+ _table = table
949
+ table = context[table] unless table.is_a?(Groonga::Table)
950
+ raise ArgumentError, "table doesn't exist: #{_table}" if table.nil?
951
+ view.add_table(table)
952
+ end
953
+ view
954
+ end
955
+
956
+ # 名前が_table_のテーブルをビューに追加する。
957
+ def add(table)
958
+ table = table.to_s if table.is_a?(Symbol)
959
+ @tables << table
960
+ self
961
+ end
962
+
963
+ def context # :nodoc:
964
+ @options[:context] || Groonga::Context.default
965
+ end
966
+
967
+ private
968
+ AVAILABLE_OPTION_KEYS = [:context, :change, :force,
969
+ :path, :persistent] # :nodoc:
970
+ def validate_options(options) # :nodoc:
971
+ return if options.nil?
972
+ unknown_keys = options.keys - AVAILABLE_OPTION_KEYS
973
+ unless unknown_keys.empty?
974
+ message = "unknown keys are specified: #{unknown_keys.inspect}"
975
+ message << ": available keys: #{AVAILABLE_OPTION_KEYS.inspect}"
976
+ raise ArgumentError, message
977
+ end
978
+ end
979
+
980
+ def create_options # :nodoc:
981
+ {
982
+ :name => @name,
983
+ :path => @options[:path],
984
+ :persistent => persistent?,
985
+ :context => context,
986
+ }
987
+ end
988
+
989
+ def persistent? # :nodoc:
990
+ @options[:persistent].nil? ? true : @options[:persistent]
991
+ end
992
+ end
993
+
994
+ class ViewRemoveDefinition # :nodoc:
995
+ def initialize(name, options={})
996
+ @name = name
997
+ @options = options
998
+ end
999
+
1000
+ def define
1001
+ context = @options[:context] || Groonga::Context.default
1002
+ context[@name].remove
1003
+ end
1004
+ end
1005
+
1006
+ class ColumnDefinition # :nodoc:
1007
+ attr_accessor :name, :type
1008
+ attr_reader :options
1009
+
1010
+ def initialize(name, options={})
1011
+ @name = name
1012
+ @name = @name.to_s if @name.is_a?(Symbol)
1013
+ @options = (options || {}).dup
1014
+ @type = nil
1015
+ end
1016
+
1017
+ def define(table_definition, table)
1018
+ column = table.column(@name)
1019
+ if column
1020
+ return column if same_column?(table_definition, column)
1021
+ if @options.delete(:force)
1022
+ column.remove
1023
+ else
1024
+ raise ArgumentError,
1025
+ "the same name column with different type is " +
1026
+ "already defined: #{@type.inspect}(#{@options.inspect}): " +
1027
+ "#{column.inspect}"
1028
+ end
1029
+ end
1030
+ table.define_column(@name,
1031
+ Schema.normalize_type(@type),
1032
+ @options)
1033
+ end
1034
+
1035
+ private
1036
+ def same_column?(table_definition, column)
1037
+ context = table_definition.context
1038
+ # TODO: should check column type and other options.
1039
+ column.range == context[Schema.normalize_type(@type)]
1040
+ end
1041
+ end
1042
+
1043
+ class ColumnRemoveDefinition # :nodoc:
1044
+ attr_accessor :name
1045
+ attr_reader :options
1046
+
1047
+ def initialize(name, options={})
1048
+ @name = name
1049
+ @name = @name.to_s if @name.is_a?(Symbol)
1050
+ @options = (options || {}).dup
1051
+ end
1052
+
1053
+ def define(table_definition, table)
1054
+ table.column(@name).remove
1055
+ end
1056
+ end
1057
+
1058
+ class IndexColumnDefinition # :nodoc:
1059
+ attr_accessor :name, :target_table, :target_column
1060
+ attr_reader :options
1061
+
1062
+ def initialize(name, options={})
1063
+ @name = name
1064
+ @name = @name.to_s if @name.is_a?(Symbol)
1065
+ @options = (options || {}).dup
1066
+ @target_table = nil
1067
+ @target_column = nil
1068
+ end
1069
+
1070
+ def define(table_definition, table)
1071
+ target_name = "#{@target_table}.#{@target_column}"
1072
+ target_table = table_definition.context[@target_table]
1073
+ if target_table.nil? or
1074
+ !(@target_column == "_key" or
1075
+ target_table.have_column?(@target_column))
1076
+ raise ArgumentError, "Unknown index target: <#{target_name}>"
1077
+ end
1078
+ index = table.column(@name)
1079
+ if index
1080
+ return index if same_index?(table_definition, index)
1081
+ if @options.delete(:force)
1082
+ index.remove
1083
+ else
1084
+ raise ArgumentError,
1085
+ "the same name index column with " +
1086
+ "different target or options is " +
1087
+ "already defined: #{target_name.inspect}" +
1088
+ "(#{@options.inspect}): #{index.inspect}"
1089
+ end
1090
+ end
1091
+ index = table.define_index_column(@name,
1092
+ @target_table,
1093
+ @options)
1094
+ index.source = target_table.column(@target_column)
1095
+ index
1096
+ end
1097
+
1098
+ private
1099
+ def same_index?(table_definition, index)
1100
+ context = table_definition.context
1101
+ # TODO: should check column type and other options.
1102
+ return false if index.range.name != @target_table
1103
+ source_names = index.sources.collect do |source|
1104
+ if source.nil?
1105
+ "#{index.range.name}._key"
1106
+ else
1107
+ source.name
1108
+ end
1109
+ end
1110
+ source_names == ["#{@target_table}.#{@target_column}"]
1111
+ end
1112
+ end
1113
+
1114
+ class Dumper # :nodoc:
1115
+ def initialize(options={})
1116
+ @options = (options || {}).dup
1117
+ end
1118
+
1119
+ def dump
1120
+ context = @options[:context] || Groonga::Context.default
1121
+ database = context.database
1122
+ return nil if database.nil?
1123
+
1124
+ reference_columns = []
1125
+ definitions = []
1126
+ database.each do |object|
1127
+ next unless object.is_a?(Groonga::Table)
1128
+ schema = "create_table(#{object.name.inspect}) do |table|\n"
1129
+ object.columns.sort_by {|column| column.local_name}.each do |column|
1130
+ if column.range.is_a?(Groonga::Table)
1131
+ reference_columns << column
1132
+ else
1133
+ type = column_method(column)
1134
+ name = column.local_name
1135
+ schema << " table.#{type}(#{name.inspect})\n"
1136
+ end
1137
+ end
1138
+ schema << "end"
1139
+ definitions << schema
1140
+ end
1141
+
1142
+ reference_columns.group_by do |column|
1143
+ column.table
1144
+ end.each do |table, columns|
1145
+ schema = "change_table(#{table.name.inspect}) do |table|\n"
1146
+ columns.each do |column|
1147
+ name = column.local_name
1148
+ reference = column.range
1149
+ schema << " table.reference(#{name.inspect}, " +
1150
+ "#{reference.name.inspect})\n"
1151
+ end
1152
+ schema << "end"
1153
+ definitions << schema
1154
+ end
1155
+
1156
+ if definitions.empty?
1157
+ ""
1158
+ else
1159
+ definitions.join("\n\n") + "\n"
1160
+ end
1161
+ end
1162
+
1163
+ private
1164
+ def column_method(column)
1165
+ range = column.range
1166
+ case range.name
1167
+ when "Int32"
1168
+ "integer32"
1169
+ when "Int64"
1170
+ "integer64"
1171
+ when "UInt32"
1172
+ "unsigned_integer32"
1173
+ when "UInt64"
1174
+ "unsigned_integer64"
1175
+ when "Float"
1176
+ "float"
1177
+ when "Time"
1178
+ "time"
1179
+ when "ShortText"
1180
+ "short_text"
1181
+ when "Text"
1182
+ "text"
1183
+ when "LongText"
1184
+ "long_text"
1185
+ else
1186
+ raise ArgumentError, "unsupported column: #{column.inspect}"
1187
+ end
1188
+ end
1189
+ end
1190
+ end
1191
+ end