rroonga 0.9.2-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +5 -0
- data/NEWS.ja.rdoc +114 -0
- data/NEWS.rdoc +116 -0
- data/README.ja.rdoc +65 -0
- data/README.rdoc +66 -0
- data/Rakefile +206 -0
- data/benchmark/common.rb +49 -0
- data/benchmark/read-write-many-small-items.rb +144 -0
- data/benchmark/write-many-small-items.rb +135 -0
- data/example/bookmark.rb +161 -0
- data/example/index-html.rb +89 -0
- data/example/search/config.ru +230 -0
- data/example/search/public/css/groonga.css +122 -0
- data/ext/.gitignore +2 -0
- data/ext/groonga/extconf.rb +93 -0
- data/ext/groonga/rb-grn-accessor.c +52 -0
- data/ext/groonga/rb-grn-array-cursor.c +36 -0
- data/ext/groonga/rb-grn-array.c +210 -0
- data/ext/groonga/rb-grn-column.c +573 -0
- data/ext/groonga/rb-grn-context.c +662 -0
- data/ext/groonga/rb-grn-database.c +472 -0
- data/ext/groonga/rb-grn-encoding-support.c +64 -0
- data/ext/groonga/rb-grn-encoding.c +257 -0
- data/ext/groonga/rb-grn-exception.c +1110 -0
- data/ext/groonga/rb-grn-expression-builder.c +75 -0
- data/ext/groonga/rb-grn-expression.c +731 -0
- data/ext/groonga/rb-grn-fix-size-column.c +166 -0
- data/ext/groonga/rb-grn-hash-cursor.c +38 -0
- data/ext/groonga/rb-grn-hash.c +294 -0
- data/ext/groonga/rb-grn-index-column.c +488 -0
- data/ext/groonga/rb-grn-logger.c +504 -0
- data/ext/groonga/rb-grn-object.c +1369 -0
- data/ext/groonga/rb-grn-operation.c +198 -0
- data/ext/groonga/rb-grn-patricia-trie-cursor.c +39 -0
- data/ext/groonga/rb-grn-patricia-trie.c +488 -0
- data/ext/groonga/rb-grn-procedure.c +52 -0
- data/ext/groonga/rb-grn-query.c +260 -0
- data/ext/groonga/rb-grn-record.c +40 -0
- data/ext/groonga/rb-grn-snippet.c +334 -0
- data/ext/groonga/rb-grn-table-cursor-key-support.c +69 -0
- data/ext/groonga/rb-grn-table-cursor.c +247 -0
- data/ext/groonga/rb-grn-table-key-support.c +714 -0
- data/ext/groonga/rb-grn-table.c +1977 -0
- data/ext/groonga/rb-grn-type.c +181 -0
- data/ext/groonga/rb-grn-utils.c +769 -0
- data/ext/groonga/rb-grn-variable-size-column.c +36 -0
- data/ext/groonga/rb-grn-variable.c +108 -0
- data/ext/groonga/rb-grn-view-accessor.c +53 -0
- data/ext/groonga/rb-grn-view-cursor.c +35 -0
- data/ext/groonga/rb-grn-view-record.c +41 -0
- data/ext/groonga/rb-grn-view.c +421 -0
- data/ext/groonga/rb-grn.h +698 -0
- data/ext/groonga/rb-groonga.c +107 -0
- data/extconf.rb +130 -0
- data/html/bar.svg +153 -0
- data/html/developer.html +117 -0
- data/html/developer.svg +469 -0
- data/html/download.svg +253 -0
- data/html/favicon.ico +0 -0
- data/html/favicon.xcf +0 -0
- data/html/footer.html.erb +28 -0
- data/html/head.html.erb +4 -0
- data/html/header.html.erb +17 -0
- data/html/index.html +147 -0
- data/html/install.svg +636 -0
- data/html/logo.xcf +0 -0
- data/html/ranguba.css +250 -0
- data/html/tutorial.svg +559 -0
- data/lib/1.8/groonga.so +0 -0
- data/lib/1.9/groonga.so +0 -0
- data/lib/groonga.rb +90 -0
- data/lib/groonga/context.rb +184 -0
- data/lib/groonga/expression-builder.rb +324 -0
- data/lib/groonga/patricia-trie.rb +85 -0
- data/lib/groonga/record.rb +311 -0
- data/lib/groonga/schema.rb +1191 -0
- data/lib/groonga/view-record.rb +56 -0
- data/license/GPL +340 -0
- data/license/LGPL +504 -0
- data/license/RUBY +59 -0
- data/misc/grnop2ruby.rb +49 -0
- data/pkg-config.rb +333 -0
- data/rroonga-build.rb +57 -0
- data/test-unit/Rakefile +40 -0
- data/test-unit/TODO +5 -0
- data/test-unit/bin/testrb +5 -0
- data/test-unit/html/classic.html +15 -0
- data/test-unit/html/index.html +25 -0
- data/test-unit/html/index.html.ja +27 -0
- data/test-unit/lib/test/unit.rb +323 -0
- data/test-unit/lib/test/unit/assertionfailederror.rb +25 -0
- data/test-unit/lib/test/unit/assertions.rb +1230 -0
- data/test-unit/lib/test/unit/attribute.rb +125 -0
- data/test-unit/lib/test/unit/autorunner.rb +360 -0
- data/test-unit/lib/test/unit/collector.rb +36 -0
- data/test-unit/lib/test/unit/collector/descendant.rb +23 -0
- data/test-unit/lib/test/unit/collector/dir.rb +108 -0
- data/test-unit/lib/test/unit/collector/load.rb +144 -0
- data/test-unit/lib/test/unit/collector/objectspace.rb +34 -0
- data/test-unit/lib/test/unit/color-scheme.rb +102 -0
- data/test-unit/lib/test/unit/color.rb +96 -0
- data/test-unit/lib/test/unit/diff.rb +724 -0
- data/test-unit/lib/test/unit/error.rb +130 -0
- data/test-unit/lib/test/unit/exceptionhandler.rb +39 -0
- data/test-unit/lib/test/unit/failure.rb +136 -0
- data/test-unit/lib/test/unit/fixture.rb +176 -0
- data/test-unit/lib/test/unit/notification.rb +129 -0
- data/test-unit/lib/test/unit/omission.rb +191 -0
- data/test-unit/lib/test/unit/pending.rb +150 -0
- data/test-unit/lib/test/unit/priority.rb +180 -0
- data/test-unit/lib/test/unit/runner/console.rb +52 -0
- data/test-unit/lib/test/unit/runner/emacs.rb +8 -0
- data/test-unit/lib/test/unit/runner/tap.rb +8 -0
- data/test-unit/lib/test/unit/testcase.rb +476 -0
- data/test-unit/lib/test/unit/testresult.rb +89 -0
- data/test-unit/lib/test/unit/testsuite.rb +110 -0
- data/test-unit/lib/test/unit/ui/console/outputlevel.rb +14 -0
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +466 -0
- data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +63 -0
- data/test-unit/lib/test/unit/ui/tap/testrunner.rb +92 -0
- data/test-unit/lib/test/unit/ui/testrunner.rb +28 -0
- data/test-unit/lib/test/unit/ui/testrunnermediator.rb +77 -0
- data/test-unit/lib/test/unit/ui/testrunnerutilities.rb +41 -0
- data/test-unit/lib/test/unit/util/backtracefilter.rb +41 -0
- data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
- data/test-unit/lib/test/unit/util/observable.rb +90 -0
- data/test-unit/lib/test/unit/util/procwrapper.rb +48 -0
- data/test-unit/lib/test/unit/version.rb +7 -0
- data/test-unit/sample/adder.rb +13 -0
- data/test-unit/sample/subtracter.rb +12 -0
- data/test-unit/sample/test_adder.rb +20 -0
- data/test-unit/sample/test_subtracter.rb +20 -0
- data/test-unit/sample/test_user.rb +23 -0
- data/test-unit/test/collector/test-descendant.rb +133 -0
- data/test-unit/test/collector/test-load.rb +442 -0
- data/test-unit/test/collector/test_dir.rb +406 -0
- data/test-unit/test/collector/test_objectspace.rb +100 -0
- data/test-unit/test/run-test.rb +15 -0
- data/test-unit/test/test-attribute.rb +86 -0
- data/test-unit/test/test-color-scheme.rb +67 -0
- data/test-unit/test/test-color.rb +47 -0
- data/test-unit/test/test-diff.rb +518 -0
- data/test-unit/test/test-emacs-runner.rb +60 -0
- data/test-unit/test/test-fixture.rb +287 -0
- data/test-unit/test/test-notification.rb +33 -0
- data/test-unit/test/test-omission.rb +81 -0
- data/test-unit/test/test-pending.rb +70 -0
- data/test-unit/test/test-priority.rb +119 -0
- data/test-unit/test/test-testcase.rb +544 -0
- data/test-unit/test/test_assertions.rb +1151 -0
- data/test-unit/test/test_error.rb +26 -0
- data/test-unit/test/test_failure.rb +33 -0
- data/test-unit/test/test_testresult.rb +113 -0
- data/test-unit/test/test_testsuite.rb +129 -0
- data/test-unit/test/testunit-test-util.rb +14 -0
- data/test-unit/test/ui/test_testrunmediator.rb +20 -0
- data/test-unit/test/util/test-method-owner-finder.rb +38 -0
- data/test-unit/test/util/test_backtracefilter.rb +41 -0
- data/test-unit/test/util/test_observable.rb +102 -0
- data/test-unit/test/util/test_procwrapper.rb +36 -0
- data/test/.gitignore +1 -0
- data/test/groonga-test-utils.rb +134 -0
- data/test/run-test.rb +58 -0
- data/test/test-array.rb +90 -0
- data/test/test-column.rb +316 -0
- data/test/test-context-select.rb +93 -0
- data/test/test-context.rb +73 -0
- data/test/test-database.rb +113 -0
- data/test/test-encoding.rb +33 -0
- data/test/test-exception.rb +93 -0
- data/test/test-expression-builder.rb +217 -0
- data/test/test-expression.rb +134 -0
- data/test/test-fix-size-column.rb +65 -0
- data/test/test-gqtp.rb +72 -0
- data/test/test-hash.rb +305 -0
- data/test/test-index-column.rb +81 -0
- data/test/test-logger.rb +37 -0
- data/test/test-patricia-trie.rb +205 -0
- data/test/test-procedure.rb +37 -0
- data/test/test-query.rb +22 -0
- data/test/test-record.rb +243 -0
- data/test/test-remote.rb +54 -0
- data/test/test-schema-view.rb +90 -0
- data/test/test-schema.rb +459 -0
- data/test/test-snippet.rb +130 -0
- data/test/test-table-cursor.rb +153 -0
- data/test/test-table-offset-and-limit.rb +102 -0
- data/test/test-table-select-normalize.rb +53 -0
- data/test/test-table-select.rb +150 -0
- data/test/test-table.rb +594 -0
- data/test/test-type.rb +71 -0
- data/test/test-variable-size-column.rb +98 -0
- data/test/test-variable.rb +28 -0
- data/test/test-vector-column.rb +76 -0
- data/test/test-version.rb +31 -0
- data/test/test-view.rb +72 -0
- data/text/TUTORIAL.ja.rdoc +392 -0
- data/text/expression.rdoc +284 -0
- 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
|
+
# # " マッチしない <> " +
|
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
|