rroonga 1.0.1 → 1.0.2
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/NEWS.ja.rdoc +46 -0
- data/NEWS.rdoc +46 -0
- data/README.ja.rdoc +2 -2
- data/README.rdoc +2 -3
- data/Rakefile +148 -11
- data/example/bookmark.rb +94 -91
- data/ext/groonga/extconf.rb +25 -25
- data/ext/groonga/groonga.def +2 -0
- data/ext/groonga/mkmf.log +7 -7
- data/ext/groonga/rb-grn-column.c +66 -0
- data/ext/groonga/rb-grn-context.c +92 -5
- data/ext/groonga/rb-grn-database.c +24 -1
- data/ext/groonga/rb-grn-exception.c +15 -0
- data/ext/groonga/rb-grn-hash.c +2 -2
- data/ext/groonga/rb-grn-index-column.c +8 -4
- data/ext/groonga/rb-grn-object.c +4 -2
- data/ext/groonga/rb-grn-patricia-trie.c +2 -2
- data/ext/groonga/rb-grn-snippet.c +1 -0
- data/ext/groonga/rb-grn-table-key-support.c +22 -19
- data/ext/groonga/rb-grn-table.c +63 -5
- data/ext/groonga/rb-grn.h +1 -1
- data/ext/groonga/rb-groonga.c +1 -0
- data/extconf.rb +1 -1
- data/html/developer.html +8 -2
- data/html/favicon.ico +0 -0
- data/html/favicon.svg +591 -0
- data/html/index.html +71 -8
- data/html/logo.svg +612 -0
- data/html/ranguba.css +92 -7
- data/html/readme.svg +256 -0
- data/lib/groonga/expression-builder.rb +8 -4
- data/lib/groonga/record.rb +77 -7
- data/lib/groonga/schema.rb +429 -100
- data/rroonga-build.rb +1 -1
- data/test/run-test.rb +1 -1
- data/test/test-array.rb +4 -0
- data/test/test-context.rb +8 -0
- data/test/test-database.rb +8 -1
- data/test/test-expression-builder.rb +14 -0
- data/test/test-fix-size-column.rb +4 -0
- data/test/test-hash.rb +10 -4
- data/test/test-index-column.rb +11 -0
- data/test/test-patricia-trie.rb +7 -1
- data/test/test-record.rb +14 -0
- data/test/test-remote.rb +3 -2
- data/test/test-schema-create-table.rb +32 -2
- data/test/test-schema.rb +108 -11
- data/test/test-table-select-normalize.rb +7 -3
- data/test/test-table-select.rb +12 -0
- data/test/test-table.rb +7 -0
- data/test/test-variable-size-column.rb +4 -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/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/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/collector.rb +36 -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/lib/test/unit.rb +323 -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/text/{TUTORIAL.ja.rdoc → tutorial.ja.rdoc} +165 -126
- metadata +106 -16
- data/html/favicon.xcf +0 -0
- data/html/logo.xcf +0 -0
- data/license/GPL +0 -340
- data/license/RUBY +0 -59
- data/pkg-config.rb +0 -333
data/lib/groonga/schema.rb
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# License along with this library; if not, write to the Free Software
|
|
16
16
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
17
17
|
|
|
18
|
+
require 'fileutils'
|
|
18
19
|
|
|
19
20
|
module Groonga
|
|
20
21
|
|
|
@@ -80,12 +81,22 @@ module Groonga
|
|
|
80
81
|
end
|
|
81
82
|
end
|
|
82
83
|
|
|
84
|
+
# 未知のインデックス対象テーブルを指定したときに発生する。
|
|
85
|
+
class UnknownIndexTargetTable < Error
|
|
86
|
+
attr_reader :table
|
|
87
|
+
def initialize(table)
|
|
88
|
+
@table = table
|
|
89
|
+
super("unknown index target table: <#{@table.inspect}>")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
83
93
|
# 未知のインデックス対象を指定したときに発生する。
|
|
84
94
|
class UnknownIndexTarget < Error
|
|
85
|
-
attr_reader :
|
|
86
|
-
def initialize(
|
|
87
|
-
@
|
|
88
|
-
|
|
95
|
+
attr_reader :table, :targets
|
|
96
|
+
def initialize(table, targets)
|
|
97
|
+
@table = table
|
|
98
|
+
@targets = targets
|
|
99
|
+
super("unknown index target: <#{@table.inspect}>: <#{@targets.inspect}>")
|
|
89
100
|
end
|
|
90
101
|
end
|
|
91
102
|
|
|
@@ -96,7 +107,7 @@ module Groonga
|
|
|
96
107
|
@options = options
|
|
97
108
|
@unknown_keys = unknown_keys
|
|
98
109
|
@available_keys = available_keys
|
|
99
|
-
message = "unknown keys are specified: #{
|
|
110
|
+
message = "unknown keys are specified: #{@unknown_keys.inspect}"
|
|
100
111
|
message << ": available keys: #{@available_keys.inspect}"
|
|
101
112
|
message << ": options: #{@options.inspect}"
|
|
102
113
|
super(message)
|
|
@@ -114,6 +125,18 @@ module Groonga
|
|
|
114
125
|
end
|
|
115
126
|
end
|
|
116
127
|
|
|
128
|
+
# 参照先のテーブルを推測できないときに発生する。
|
|
129
|
+
class UnguessableReferenceTable < Error
|
|
130
|
+
attr_reader :name, :tried_table_names
|
|
131
|
+
def initialize(name, tried_table_names)
|
|
132
|
+
@name = name
|
|
133
|
+
@tried_table_names = tried_table_names
|
|
134
|
+
super("failed to guess referenced table name " +
|
|
135
|
+
"for reference column: #{@name.inspect}: " +
|
|
136
|
+
"tried table names: #{@tried_table_names.inspect}")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
117
140
|
class << self
|
|
118
141
|
|
|
119
142
|
# call-seq:
|
|
@@ -349,6 +372,20 @@ module Groonga
|
|
|
349
372
|
end
|
|
350
373
|
end
|
|
351
374
|
|
|
375
|
+
# call-seq:
|
|
376
|
+
# Groonga::Schema.remove_column(table_name, column_name)
|
|
377
|
+
#
|
|
378
|
+
# 以下と同様:
|
|
379
|
+
#
|
|
380
|
+
# Groonga::Schema.change_table(table_name) do |table|
|
|
381
|
+
# table.remove_column(column_name)
|
|
382
|
+
# end
|
|
383
|
+
def remove_column(table_name, column_name)
|
|
384
|
+
change_table(table_name) do |table|
|
|
385
|
+
table.remove_column(column_name)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
352
389
|
# スキーマの内容を文字列で返す。返された値は
|
|
353
390
|
# Groonga::Schema.restoreすることによりスキーマ内に組
|
|
354
391
|
# み込むことができる。
|
|
@@ -369,13 +406,15 @@ module Groonga
|
|
|
369
406
|
# スキーマ定義時に使用するGroonga::Contextを指定する。
|
|
370
407
|
# 省略した場合はGroonga::Context.defaultを使用する。
|
|
371
408
|
def dump(options={})
|
|
372
|
-
|
|
409
|
+
options = options.dup
|
|
410
|
+
schema = new(:context => options.delete(:context))
|
|
411
|
+
schema.dump
|
|
373
412
|
end
|
|
374
413
|
|
|
375
414
|
# Groonga::Schema.dumpで文字列化したスキーマを組み込む。
|
|
376
415
|
def restore(dumped_text, options={})
|
|
377
416
|
define(options) do |schema|
|
|
378
|
-
schema.
|
|
417
|
+
schema.restore(dumped_text)
|
|
379
418
|
end
|
|
380
419
|
end
|
|
381
420
|
|
|
@@ -414,6 +453,7 @@ module Groonga
|
|
|
414
453
|
# 省略した場合はGroonga::Context.defaultを使用する。
|
|
415
454
|
def initialize(options={})
|
|
416
455
|
@options = (options || {}).dup
|
|
456
|
+
@options[:context] ||= Groonga::Context.default
|
|
417
457
|
@definitions = []
|
|
418
458
|
end
|
|
419
459
|
|
|
@@ -424,13 +464,23 @@ module Groonga
|
|
|
424
464
|
end
|
|
425
465
|
end
|
|
426
466
|
|
|
427
|
-
# Groonga::Schema
|
|
467
|
+
# Groonga::Schema#dumpで返されたスキーマの内容を読み込む。
|
|
428
468
|
#
|
|
429
469
|
# 読み込まれた内容は#defineを呼び出すまでは実行されない
|
|
430
470
|
# ことに注意すること。
|
|
431
|
-
def
|
|
471
|
+
def restore(dumped_text)
|
|
432
472
|
instance_eval(dumped_text)
|
|
433
473
|
end
|
|
474
|
+
# for backward compatibility.
|
|
475
|
+
# TODO: remove this at the next major release.
|
|
476
|
+
alias_method :load, :restore
|
|
477
|
+
|
|
478
|
+
# スキーマの内容を文字列で返す。返された値は
|
|
479
|
+
# Groonga::Schema#restoreすることによりスキーマ内に組み込むことができる。
|
|
480
|
+
def dump
|
|
481
|
+
dumper = Dumper.new(:context => @options[:context])
|
|
482
|
+
dumper.dump
|
|
483
|
+
end
|
|
434
484
|
|
|
435
485
|
# call-seq:
|
|
436
486
|
# schema.create_table(name, options={}) {|table| ...}
|
|
@@ -516,7 +566,7 @@ module Groonga
|
|
|
516
566
|
# 登録される。
|
|
517
567
|
def create_table(name, options={})
|
|
518
568
|
definition = TableDefinition.new(name, @options.merge(options || {}))
|
|
519
|
-
yield(definition)
|
|
569
|
+
yield(definition) if block_given?
|
|
520
570
|
@definitions << definition
|
|
521
571
|
end
|
|
522
572
|
|
|
@@ -624,6 +674,24 @@ module Groonga
|
|
|
624
674
|
@definitions << definition
|
|
625
675
|
end
|
|
626
676
|
|
|
677
|
+
# call-seq:
|
|
678
|
+
# schema.remove_column(table_name, column_name)
|
|
679
|
+
#
|
|
680
|
+
# 以下と同様:
|
|
681
|
+
#
|
|
682
|
+
# schema.change_table(table_name) do |table|
|
|
683
|
+
# table.remove_column(column_name)
|
|
684
|
+
# end
|
|
685
|
+
def remove_column(table_name, column_name)
|
|
686
|
+
change_table(table_name) do |table|
|
|
687
|
+
table.remove_column(column_name)
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
def context # :nodoc:
|
|
692
|
+
@options[:context] || Groonga::Context.default
|
|
693
|
+
end
|
|
694
|
+
|
|
627
695
|
# スキーマ定義時にGroonga::Schema.create_tableや
|
|
628
696
|
# Groonga::Schema#create_tableからブロックに渡されてくる
|
|
629
697
|
# オブジェクト
|
|
@@ -708,9 +776,9 @@ module Groonga
|
|
|
708
776
|
self
|
|
709
777
|
end
|
|
710
778
|
|
|
711
|
-
# 名前が_name_
|
|
779
|
+
# 名前が_name_のカラムを削除します。
|
|
712
780
|
#
|
|
713
|
-
# _options_
|
|
781
|
+
# _options_に指定可能な値はありません(TODO _options_は不要?)。
|
|
714
782
|
#
|
|
715
783
|
def remove_column(name, options={})
|
|
716
784
|
definition = self[name, ColumnRemoveDefinition]
|
|
@@ -725,9 +793,11 @@ module Groonga
|
|
|
725
793
|
# call-seq:
|
|
726
794
|
# table.index(target_column_full_name, options={})
|
|
727
795
|
# table.index(target_table, target_column, options={})
|
|
796
|
+
# table.index(target_table, target_column1, target_column2, ..., options={})
|
|
728
797
|
#
|
|
729
|
-
# _target_table_の_target_column_
|
|
730
|
-
#
|
|
798
|
+
# _target_table_の_target_column_を対象とするインデック
|
|
799
|
+
# スカラムを作成します。複数のカラムを指定することもで
|
|
800
|
+
# きます。
|
|
731
801
|
#
|
|
732
802
|
# _target_column_full_name_で指定するときはテーブル名
|
|
733
803
|
# とカラム名を"."でつなげます。例えば、「Users」テーブ
|
|
@@ -753,31 +823,71 @@ module Groonga
|
|
|
753
823
|
# した場合は自動的にパスが付加される。
|
|
754
824
|
#
|
|
755
825
|
# [+:with_section+]
|
|
756
|
-
#
|
|
826
|
+
# +true+を指定すると転置索引にsection(段落情報)を合
|
|
827
|
+
# わせて格納する。未指定または+nil+を指定した場合、
|
|
828
|
+
# 複数のカラムを指定すると自動的に有効になる。
|
|
757
829
|
#
|
|
758
830
|
# [+:with_weight+]
|
|
759
|
-
#
|
|
831
|
+
# +true+を指定すると転置索引にweight情報を合わせて格
|
|
832
|
+
# 納する。
|
|
760
833
|
#
|
|
761
834
|
# [+:with_position+]
|
|
762
|
-
#
|
|
835
|
+
# +true+を指定すると転置索引に出現位置情報を合わせて
|
|
836
|
+
# 格納する。未指定または+nil+を指定した場合、テーブ
|
|
837
|
+
# ルがN-gram系のトークナイザーを利用している場合は自
|
|
838
|
+
# 動的に有効になる。
|
|
763
839
|
def index(target_table_or_target_column_full_name, *args)
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
840
|
+
key, target_table, target_columns, options =
|
|
841
|
+
parse_index_argument(target_table_or_target_column_full_name, *args)
|
|
842
|
+
|
|
843
|
+
name = options.delete(:name)
|
|
844
|
+
definition = self[key, IndexColumnDefinition]
|
|
845
|
+
if definition.nil?
|
|
846
|
+
definition = IndexColumnDefinition.new(name, options)
|
|
847
|
+
update_definition(key, IndexColumnDefinition, definition)
|
|
767
848
|
end
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
849
|
+
definition.target_table = target_table
|
|
850
|
+
definition.target_columns = target_columns
|
|
851
|
+
definition.options.merge!(column_options.merge(options))
|
|
852
|
+
self
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
# call-seq:
|
|
856
|
+
# table.remove_index(target_column_full_name, options={})
|
|
857
|
+
# table.remove_index(target_table, target_column, options={})
|
|
858
|
+
# table.remove_index(target_table, target_column1, target_column2, ..., options={})
|
|
859
|
+
#
|
|
860
|
+
# _target_table_の_target_column_を対象とするインデッ
|
|
861
|
+
# クスカラムを削除します。
|
|
862
|
+
#
|
|
863
|
+
# _target_column_full_name_で指定するときはテーブル名
|
|
864
|
+
# とカラム名を"."でつなげます。例えば、「Users」テーブ
|
|
865
|
+
# ルの「name」カラムのインデックスカラムを削除する場合
|
|
866
|
+
# はこうなります。
|
|
867
|
+
#
|
|
868
|
+
# table.remove_index("Users.name")
|
|
869
|
+
#
|
|
870
|
+
# _options_に指定可能な値は以下の通り。
|
|
871
|
+
#
|
|
872
|
+
# [+:name+]
|
|
873
|
+
# インデックスカラムのカラム名を任意に指定する。
|
|
874
|
+
def remove_index(target_table_or_target_column_full_name, *args)
|
|
875
|
+
key, target_table, target_columns, options =
|
|
876
|
+
parse_index_argument(target_table_or_target_column_full_name, *args)
|
|
877
|
+
|
|
878
|
+
name = options.delete(:name)
|
|
879
|
+
name ||= lambda do |context|
|
|
880
|
+
IndexColumnDefinition.column_name(context,
|
|
881
|
+
target_table,
|
|
882
|
+
target_columns)
|
|
883
|
+
end
|
|
884
|
+
definition = self[key, ColumnRemoveDefinition]
|
|
885
|
+
if definition.nil?
|
|
886
|
+
definition = ColumnRemoveDefinition.new(name, options)
|
|
887
|
+
update_definition(key, ColumnRemoveDefinition, definition)
|
|
779
888
|
end
|
|
780
|
-
|
|
889
|
+
definition.options.merge!(options)
|
|
890
|
+
self
|
|
781
891
|
end
|
|
782
892
|
|
|
783
893
|
# 名前が_name_の32bit符号付き整数のカラムを作成する。
|
|
@@ -836,6 +946,14 @@ module Groonga
|
|
|
836
946
|
column(name, "Time", options)
|
|
837
947
|
end
|
|
838
948
|
|
|
949
|
+
# 以下と同様:
|
|
950
|
+
# table.time("updated_at")
|
|
951
|
+
# table.time("created_at")
|
|
952
|
+
def timestamps(options={})
|
|
953
|
+
time("created_at", options)
|
|
954
|
+
time("updated_at", options)
|
|
955
|
+
end
|
|
956
|
+
|
|
839
957
|
# 名前が_name_の4Kbyte以下の文字列を格納できるカラムを
|
|
840
958
|
# 作成する。
|
|
841
959
|
#
|
|
@@ -867,9 +985,13 @@ module Groonga
|
|
|
867
985
|
# 名前が_name_で_table_のレコードIDを格納する参照カラ
|
|
868
986
|
# ムを作成する。
|
|
869
987
|
#
|
|
988
|
+
# _table_が省略された場合は_name_の複数形が使われる。
|
|
989
|
+
# 例えば、_name_が"user"な場合は_table_は"users"になる。
|
|
990
|
+
#
|
|
870
991
|
# _options_に指定可能な値は
|
|
871
992
|
# Groonga::Schema::TableDefinition#columnを参照。
|
|
872
|
-
def reference(name, table, options={})
|
|
993
|
+
def reference(name, table=nil, options={})
|
|
994
|
+
table ||= lambda {|context| guess_table_name(context, name)}
|
|
873
995
|
column(name, table, options)
|
|
874
996
|
end
|
|
875
997
|
|
|
@@ -894,8 +1016,8 @@ module Groonga
|
|
|
894
1016
|
end
|
|
895
1017
|
|
|
896
1018
|
private
|
|
897
|
-
def update_definition(
|
|
898
|
-
old_definition = self[
|
|
1019
|
+
def update_definition(key, definition_class, definition) # :nodoc:
|
|
1020
|
+
old_definition = self[key, definition_class]
|
|
899
1021
|
if old_definition
|
|
900
1022
|
index = @definitions.index(old_definition)
|
|
901
1023
|
@definitions[index] = definition
|
|
@@ -934,14 +1056,14 @@ module Groonga
|
|
|
934
1056
|
def create_options # :nodoc:
|
|
935
1057
|
common = {
|
|
936
1058
|
:name => @name,
|
|
937
|
-
:path =>
|
|
1059
|
+
:path => path,
|
|
938
1060
|
:persistent => persistent?,
|
|
939
1061
|
:value_type => @options[:value_type],
|
|
940
1062
|
:context => context,
|
|
941
1063
|
:sub_records => @options[:sub_records],
|
|
942
1064
|
}
|
|
943
1065
|
key_support_table_common = {
|
|
944
|
-
:key_type =>
|
|
1066
|
+
:key_type => normalize_key_type(@options[:key_type] || "ShortText"),
|
|
945
1067
|
:key_normalize => @options[:key_normalize],
|
|
946
1068
|
:default_tokenizer => @options[:default_tokenizer],
|
|
947
1069
|
}
|
|
@@ -958,6 +1080,14 @@ module Groonga
|
|
|
958
1080
|
end
|
|
959
1081
|
end
|
|
960
1082
|
|
|
1083
|
+
def path
|
|
1084
|
+
user_path = @options[:path]
|
|
1085
|
+
return user_path if user_path
|
|
1086
|
+
tables_dir = "#{context.database.path}.tables"
|
|
1087
|
+
FileUtils.mkdir_p(tables_dir)
|
|
1088
|
+
File.join(tables_dir, @name)
|
|
1089
|
+
end
|
|
1090
|
+
|
|
961
1091
|
def column_options # :nodoc:
|
|
962
1092
|
{:persistent => persistent?}
|
|
963
1093
|
end
|
|
@@ -966,19 +1096,26 @@ module Groonga
|
|
|
966
1096
|
@options[:persistent].nil? ? true : @options[:persistent]
|
|
967
1097
|
end
|
|
968
1098
|
|
|
969
|
-
def
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1099
|
+
def parse_index_argument(target_table_or_target_column_full_name, *args)
|
|
1100
|
+
options = nil
|
|
1101
|
+
options = args.pop if args.last.is_a?(::Hash)
|
|
1102
|
+
if args.empty?
|
|
1103
|
+
target_column_full_name = target_table_or_target_column_full_name
|
|
1104
|
+
if target_column_full_name.is_a?(Groonga::Column)
|
|
1105
|
+
target_column_full_name = target_column_full_name.name
|
|
1106
|
+
end
|
|
1107
|
+
target_table, target_column = target_column_full_name.split(/\./, 2)
|
|
1108
|
+
target_columns = [target_column]
|
|
1109
|
+
key = [target_table, target_columns]
|
|
1110
|
+
else
|
|
1111
|
+
target_table_name = target_table_or_target_column_full_name
|
|
1112
|
+
target_table = lambda do |context|
|
|
1113
|
+
guess_table_name(context, target_table_name)
|
|
1114
|
+
end
|
|
1115
|
+
target_columns = args
|
|
1116
|
+
key = [target_table_name, target_columns]
|
|
977
1117
|
end
|
|
978
|
-
|
|
979
|
-
definition.target_column = target_column
|
|
980
|
-
definition.options.merge!(column_options.merge(options))
|
|
981
|
-
self
|
|
1118
|
+
[key, target_table, target_columns, options || {}]
|
|
982
1119
|
end
|
|
983
1120
|
|
|
984
1121
|
def same_table?(table, options)
|
|
@@ -987,12 +1124,15 @@ module Groonga
|
|
|
987
1124
|
sub_records = options[:sub_records]
|
|
988
1125
|
sub_records = false if sub_records.nil?
|
|
989
1126
|
return false unless table.support_sub_records? == sub_records
|
|
1127
|
+
path = options[:path]
|
|
1128
|
+
return false if path and table.path != path
|
|
990
1129
|
|
|
991
1130
|
case table
|
|
992
1131
|
when Groonga::Array
|
|
993
1132
|
true
|
|
994
1133
|
when Groonga::Hash, Groonga::PatriciaTrie
|
|
995
|
-
|
|
1134
|
+
key_type = normalize_key_type(options[:key_type])
|
|
1135
|
+
return false unless table.domain == resolve_name(key_type)
|
|
996
1136
|
default_tokenizer = resolve_name(options[:default_tokenizer])
|
|
997
1137
|
return false unless table.default_tokenizer == default_tokenizer
|
|
998
1138
|
key_normalize = options[:key_normalize]
|
|
@@ -1009,15 +1149,40 @@ module Groonga
|
|
|
1009
1149
|
end
|
|
1010
1150
|
end
|
|
1011
1151
|
|
|
1152
|
+
def normalize_key_type(key_type)
|
|
1153
|
+
Schema.normalize_type(key_type || "ShortText")
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1012
1156
|
def resolve_name(type)
|
|
1013
|
-
if type.
|
|
1014
|
-
nil
|
|
1015
|
-
elsif type.is_a?(String)
|
|
1157
|
+
if type.is_a?(String)
|
|
1016
1158
|
context[type]
|
|
1017
1159
|
else
|
|
1018
1160
|
type
|
|
1019
1161
|
end
|
|
1020
1162
|
end
|
|
1163
|
+
|
|
1164
|
+
def guess_table_name(context, name)
|
|
1165
|
+
original_name = name
|
|
1166
|
+
name = name.to_s
|
|
1167
|
+
candidate_names = [name]
|
|
1168
|
+
if name.respond_to?(:pluralize)
|
|
1169
|
+
pluralized_name = name.pluralize
|
|
1170
|
+
else
|
|
1171
|
+
pluralized_name = "#{name}s"
|
|
1172
|
+
end
|
|
1173
|
+
candidate_names << pluralized_name
|
|
1174
|
+
if pluralized_name.respond_to?(:camelize)
|
|
1175
|
+
candidate_names << pluralized_name.camelize
|
|
1176
|
+
else
|
|
1177
|
+
candidate_names << pluralized_name.split(/_/).collect do |word|
|
|
1178
|
+
word.capitalize
|
|
1179
|
+
end.join
|
|
1180
|
+
end
|
|
1181
|
+
candidate_names.each do |table_name|
|
|
1182
|
+
return table_name if context[table_name]
|
|
1183
|
+
end
|
|
1184
|
+
raise UnguessableReferenceTable.new(original_name, candidate_names)
|
|
1185
|
+
end
|
|
1021
1186
|
end
|
|
1022
1187
|
|
|
1023
1188
|
class TableRemoveDefinition # :nodoc:
|
|
@@ -1027,7 +1192,7 @@ module Groonga
|
|
|
1027
1192
|
end
|
|
1028
1193
|
|
|
1029
1194
|
def define
|
|
1030
|
-
context = @options[:context]
|
|
1195
|
+
context = @options[:context]
|
|
1031
1196
|
context[@name].remove
|
|
1032
1197
|
end
|
|
1033
1198
|
end
|
|
@@ -1130,26 +1295,51 @@ module Groonga
|
|
|
1130
1295
|
end
|
|
1131
1296
|
|
|
1132
1297
|
def define(table_definition, table)
|
|
1298
|
+
context = table_definition.context
|
|
1133
1299
|
column = table.column(@name)
|
|
1300
|
+
options = define_options(context, table)
|
|
1134
1301
|
if column
|
|
1135
|
-
return column if same_column?(
|
|
1136
|
-
if @options
|
|
1302
|
+
return column if same_column?(context, column)
|
|
1303
|
+
if @options[:force]
|
|
1137
1304
|
column.remove
|
|
1138
1305
|
else
|
|
1139
|
-
options = @options.merge(:type => @type)
|
|
1140
1306
|
raise ColumnCreationWithDifferentOptions.new(column, options)
|
|
1141
1307
|
end
|
|
1142
1308
|
end
|
|
1143
1309
|
table.define_column(@name,
|
|
1144
|
-
|
|
1145
|
-
|
|
1310
|
+
normalize_type(context),
|
|
1311
|
+
options)
|
|
1146
1312
|
end
|
|
1147
1313
|
|
|
1148
1314
|
private
|
|
1149
|
-
def
|
|
1150
|
-
|
|
1315
|
+
def normalize_type(context)
|
|
1316
|
+
if @type.respond_to?(:call)
|
|
1317
|
+
resolved_type = @type.call(context)
|
|
1318
|
+
else
|
|
1319
|
+
resolved_type = @type
|
|
1320
|
+
end
|
|
1321
|
+
Schema.normalize_type(resolved_type)
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1324
|
+
def same_column?(context, column)
|
|
1151
1325
|
# TODO: should check column type and other options.
|
|
1152
|
-
column.range == context[
|
|
1326
|
+
column.range == context[normalize_type(context)]
|
|
1327
|
+
end
|
|
1328
|
+
|
|
1329
|
+
def define_options(context, table)
|
|
1330
|
+
{
|
|
1331
|
+
:path => path(context, table),
|
|
1332
|
+
:type => @options[:type],
|
|
1333
|
+
:compress => @options[:compress],
|
|
1334
|
+
}
|
|
1335
|
+
end
|
|
1336
|
+
|
|
1337
|
+
def path(context, table)
|
|
1338
|
+
user_path = @options[:path]
|
|
1339
|
+
return user_path if user_path
|
|
1340
|
+
columns_dir = "#{table.path}.columns"
|
|
1341
|
+
FileUtils.mkdir_p(columns_dir)
|
|
1342
|
+
File.join(columns_dir, @name)
|
|
1153
1343
|
end
|
|
1154
1344
|
end
|
|
1155
1345
|
|
|
@@ -1164,12 +1354,33 @@ module Groonga
|
|
|
1164
1354
|
end
|
|
1165
1355
|
|
|
1166
1356
|
def define(table_definition, table)
|
|
1167
|
-
|
|
1357
|
+
if @name.respond_to?(:call)
|
|
1358
|
+
name = @name.call(table_definition.context)
|
|
1359
|
+
else
|
|
1360
|
+
name = @name
|
|
1361
|
+
end
|
|
1362
|
+
table.column(name).remove
|
|
1168
1363
|
end
|
|
1169
1364
|
end
|
|
1170
1365
|
|
|
1171
1366
|
class IndexColumnDefinition # :nodoc:
|
|
1172
|
-
|
|
1367
|
+
class << self
|
|
1368
|
+
def column_name(context, target_table, target_columns)
|
|
1369
|
+
target_table = resolve(context, target_table)
|
|
1370
|
+
"#{target_table.name}_#{target_columns.join('_')}"
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
def resolve(context, object)
|
|
1374
|
+
return object if object.is_a?(Groonga::Object)
|
|
1375
|
+
if object.respond_to?(:call)
|
|
1376
|
+
object = object.call(context)
|
|
1377
|
+
end
|
|
1378
|
+
return nil if object.nil?
|
|
1379
|
+
context[object]
|
|
1380
|
+
end
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1383
|
+
attr_accessor :name, :target_table, :target_columns
|
|
1173
1384
|
attr_reader :options
|
|
1174
1385
|
|
|
1175
1386
|
def initialize(name, options={})
|
|
@@ -1177,48 +1388,83 @@ module Groonga
|
|
|
1177
1388
|
@name = @name.to_s if @name.is_a?(Symbol)
|
|
1178
1389
|
@options = (options || {}).dup
|
|
1179
1390
|
@target_table = nil
|
|
1180
|
-
@
|
|
1391
|
+
@target_columns = nil
|
|
1181
1392
|
end
|
|
1182
1393
|
|
|
1183
1394
|
def define(table_definition, table)
|
|
1184
|
-
|
|
1185
|
-
target_table =
|
|
1186
|
-
if target_table.nil?
|
|
1187
|
-
|
|
1188
|
-
target_table.have_column?(@target_column))
|
|
1189
|
-
raise UnknownIndexTarget.new(target_name)
|
|
1395
|
+
context = table_definition.context
|
|
1396
|
+
target_table = resolve_target_table(context)
|
|
1397
|
+
if target_table.nil?
|
|
1398
|
+
raise UnknownIndexTargetTable.new(@target_table)
|
|
1190
1399
|
end
|
|
1191
|
-
|
|
1400
|
+
nonexistent_columns = nonexistent_columns(target_table)
|
|
1401
|
+
unless nonexistent_columns.empty?
|
|
1402
|
+
raise UnknownIndexTarget.new(target_table, nonexistent_columns)
|
|
1403
|
+
end
|
|
1404
|
+
name = @name || self.class.column_name(context,
|
|
1405
|
+
target_table,
|
|
1406
|
+
@target_columns)
|
|
1407
|
+
index = table.column(name)
|
|
1192
1408
|
if index
|
|
1193
|
-
return index if same_index?(
|
|
1194
|
-
if @options
|
|
1409
|
+
return index if same_index?(context, index, target_table)
|
|
1410
|
+
if @options[:force]
|
|
1195
1411
|
index.remove
|
|
1196
1412
|
else
|
|
1197
1413
|
options = @options.merge(:type => :index,
|
|
1198
|
-
:
|
|
1414
|
+
:target_table => target_table,
|
|
1415
|
+
:target_columns => @target_columns)
|
|
1199
1416
|
raise ColumnCreationWithDifferentOptions.new(index, options)
|
|
1200
1417
|
end
|
|
1201
1418
|
end
|
|
1202
|
-
index = table.define_index_column(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
index.
|
|
1419
|
+
index = table.define_index_column(name,
|
|
1420
|
+
target_table,
|
|
1421
|
+
define_options(context, table, name))
|
|
1422
|
+
index.sources = @target_columns.collect do |column|
|
|
1423
|
+
target_table.column(column)
|
|
1424
|
+
end
|
|
1206
1425
|
index
|
|
1207
1426
|
end
|
|
1208
1427
|
|
|
1209
1428
|
private
|
|
1210
|
-
def same_index?(
|
|
1211
|
-
context = table_definition.context
|
|
1429
|
+
def same_index?(context, index, target_table)
|
|
1212
1430
|
# TODO: should check column type and other options.
|
|
1213
|
-
|
|
1431
|
+
range = index.range
|
|
1432
|
+
return false if range != target_table
|
|
1214
1433
|
source_names = index.sources.collect do |source|
|
|
1215
|
-
if source ==
|
|
1216
|
-
"
|
|
1434
|
+
if source == range
|
|
1435
|
+
"_key"
|
|
1217
1436
|
else
|
|
1218
|
-
source.
|
|
1437
|
+
source.local_name
|
|
1219
1438
|
end
|
|
1220
1439
|
end
|
|
1221
|
-
source_names ==
|
|
1440
|
+
source_names.sort == @target_columns.sort
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
def nonexistent_columns(target_table)
|
|
1444
|
+
@target_columns.reject do |column|
|
|
1445
|
+
column == "_key" or target_table.have_column?(column)
|
|
1446
|
+
end
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
def resolve_target_table(context)
|
|
1450
|
+
self.class.resolve(context, @target_table)
|
|
1451
|
+
end
|
|
1452
|
+
|
|
1453
|
+
def define_options(context, table, name)
|
|
1454
|
+
{
|
|
1455
|
+
:path => path(context, table, name),
|
|
1456
|
+
:with_section => @options[:with_section],
|
|
1457
|
+
:with_weight => @options[:with_weight],
|
|
1458
|
+
:with_position => @options[:with_position],
|
|
1459
|
+
}
|
|
1460
|
+
end
|
|
1461
|
+
|
|
1462
|
+
def path(context, table, name)
|
|
1463
|
+
user_path = @options[:path]
|
|
1464
|
+
return user_path if user_path
|
|
1465
|
+
columns_dir = "#{table.path}.columns"
|
|
1466
|
+
FileUtils.mkdir_p(columns_dir)
|
|
1467
|
+
File.join(columns_dir, name)
|
|
1222
1468
|
end
|
|
1223
1469
|
end
|
|
1224
1470
|
|
|
@@ -1232,35 +1478,51 @@ module Groonga
|
|
|
1232
1478
|
database = context.database
|
|
1233
1479
|
return nil if database.nil?
|
|
1234
1480
|
|
|
1481
|
+
header + dump_schema(database) + footer
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
def dump_schema(database)
|
|
1485
|
+
index_columns = []
|
|
1235
1486
|
reference_columns = []
|
|
1236
1487
|
definitions = []
|
|
1237
1488
|
database.each do |object|
|
|
1238
1489
|
next unless object.is_a?(Groonga::Table)
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1490
|
+
table = object
|
|
1491
|
+
schema = create_table_header(table)
|
|
1492
|
+
table.columns.sort_by {|column| column.local_name}.each do |column|
|
|
1493
|
+
if column.is_a?(Groonga::IndexColumn)
|
|
1494
|
+
index_columns << column
|
|
1243
1495
|
else
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1496
|
+
if column.range.is_a?(Groonga::Table)
|
|
1497
|
+
reference_columns << column
|
|
1498
|
+
else
|
|
1499
|
+
schema << define_column(table, column)
|
|
1500
|
+
end
|
|
1247
1501
|
end
|
|
1248
1502
|
end
|
|
1249
|
-
schema <<
|
|
1503
|
+
schema << create_table_footer(table)
|
|
1250
1504
|
definitions << schema
|
|
1251
1505
|
end
|
|
1252
1506
|
|
|
1253
1507
|
reference_columns.group_by do |column|
|
|
1254
1508
|
column.table
|
|
1255
1509
|
end.each do |table, columns|
|
|
1256
|
-
schema =
|
|
1510
|
+
schema = change_table_header(table)
|
|
1257
1511
|
columns.each do |column|
|
|
1258
|
-
|
|
1259
|
-
reference = column.range
|
|
1260
|
-
schema << " table.reference(#{name.inspect}, " +
|
|
1261
|
-
"#{reference.name.inspect})\n"
|
|
1512
|
+
schema << define_reference_column(table, column)
|
|
1262
1513
|
end
|
|
1263
|
-
schema <<
|
|
1514
|
+
schema << change_table_footer(table)
|
|
1515
|
+
definitions << schema
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1518
|
+
index_columns.group_by do |column|
|
|
1519
|
+
column.table
|
|
1520
|
+
end.each do |table, columns|
|
|
1521
|
+
schema = change_table_header(table)
|
|
1522
|
+
columns.each do |column|
|
|
1523
|
+
schema << define_index_column(table, column)
|
|
1524
|
+
end
|
|
1525
|
+
schema << change_table_footer(table)
|
|
1264
1526
|
definitions << schema
|
|
1265
1527
|
end
|
|
1266
1528
|
|
|
@@ -1272,6 +1534,73 @@ module Groonga
|
|
|
1272
1534
|
end
|
|
1273
1535
|
|
|
1274
1536
|
private
|
|
1537
|
+
def header
|
|
1538
|
+
""
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1541
|
+
def footer
|
|
1542
|
+
""
|
|
1543
|
+
end
|
|
1544
|
+
|
|
1545
|
+
def create_table_header(table)
|
|
1546
|
+
parameters = []
|
|
1547
|
+
unless table.is_a?(Groonga::Array)
|
|
1548
|
+
case table
|
|
1549
|
+
when Groonga::Hash
|
|
1550
|
+
parameters << ":type => :hash"
|
|
1551
|
+
when Groonga::PatriciaTrie
|
|
1552
|
+
parameters << ":type => :patricia_trie"
|
|
1553
|
+
end
|
|
1554
|
+
if table.domain
|
|
1555
|
+
parameters << ":key_type => #{table.domain.name.dump}"
|
|
1556
|
+
end
|
|
1557
|
+
end
|
|
1558
|
+
parameters << ":force => true"
|
|
1559
|
+
parameters.unshift("")
|
|
1560
|
+
parameters = parameters.join(",\n ")
|
|
1561
|
+
"create_table(#{table.name.dump}#{parameters}) do |table|\n"
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
def create_table_footer(table)
|
|
1565
|
+
"end"
|
|
1566
|
+
end
|
|
1567
|
+
|
|
1568
|
+
def change_table_header(table)
|
|
1569
|
+
"change_table(#{table.name.inspect}) do |table|\n"
|
|
1570
|
+
end
|
|
1571
|
+
|
|
1572
|
+
def change_table_footer(table)
|
|
1573
|
+
"end"
|
|
1574
|
+
end
|
|
1575
|
+
|
|
1576
|
+
def define_column(table, column)
|
|
1577
|
+
type = column_method(column)
|
|
1578
|
+
name = column.local_name
|
|
1579
|
+
" table.#{type}(#{name.inspect})\n"
|
|
1580
|
+
end
|
|
1581
|
+
|
|
1582
|
+
def define_reference_column(table, column)
|
|
1583
|
+
name = column.local_name
|
|
1584
|
+
reference = column.range
|
|
1585
|
+
" table.reference(#{name.dump}, #{reference.name.dump})\n"
|
|
1586
|
+
end
|
|
1587
|
+
|
|
1588
|
+
def define_index_column(table, column)
|
|
1589
|
+
target_table_name = column.range.name
|
|
1590
|
+
sources = column.sources
|
|
1591
|
+
source_names = sources.collect do |source|
|
|
1592
|
+
if source.is_a?(table.class)
|
|
1593
|
+
"_key".dump
|
|
1594
|
+
else
|
|
1595
|
+
source.local_name.dump
|
|
1596
|
+
end
|
|
1597
|
+
end.join(", ")
|
|
1598
|
+
arguments = [target_table_name.dump,
|
|
1599
|
+
sources.size == 1 ? source_names : "[#{source_names}]",
|
|
1600
|
+
":name => #{column.local_name.dump}"]
|
|
1601
|
+
" table.index(#{arguments.join(', ')})\n"
|
|
1602
|
+
end
|
|
1603
|
+
|
|
1275
1604
|
def column_method(column)
|
|
1276
1605
|
range = column.range
|
|
1277
1606
|
case range.name
|