groonga 0.0.7 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/NEWS.ja.rdoc +56 -0
  2. data/NEWS.rdoc +58 -0
  3. data/Rakefile +2 -3
  4. data/benchmark/read-write-many-small-items.rb +16 -32
  5. data/benchmark/write-many-small-items.rb +14 -28
  6. data/example/bookmark.rb +19 -17
  7. data/example/index-html.rb +11 -1
  8. data/example/search/config.ru +14 -9
  9. data/ext/rb-grn-array.c +6 -6
  10. data/ext/rb-grn-column.c +348 -18
  11. data/ext/rb-grn-context.c +8 -4
  12. data/ext/rb-grn-database.c +6 -7
  13. data/ext/rb-grn-exception.c +101 -5
  14. data/ext/rb-grn-expression.c +206 -23
  15. data/ext/rb-grn-fix-size-column.c +6 -39
  16. data/ext/rb-grn-hash.c +24 -24
  17. data/ext/rb-grn-index-column.c +74 -19
  18. data/ext/rb-grn-logger.c +48 -0
  19. data/ext/rb-grn-object.c +281 -67
  20. data/ext/rb-grn-operation.c +1 -1
  21. data/ext/rb-grn-patricia-trie-cursor.c +10 -1
  22. data/ext/rb-grn-patricia-trie.c +268 -7
  23. data/ext/rb-grn-query.c +52 -1
  24. data/ext/rb-grn-record.c +8 -2
  25. data/ext/rb-grn-snippet.c +63 -1
  26. data/ext/rb-grn-table-cursor-key-support.c +15 -1
  27. data/ext/rb-grn-table-cursor.c +57 -0
  28. data/ext/rb-grn-table-key-support.c +382 -46
  29. data/ext/rb-grn-table.c +729 -192
  30. data/ext/rb-grn-type.c +63 -12
  31. data/ext/rb-grn-utils.c +156 -158
  32. data/ext/rb-grn-variable.c +18 -0
  33. data/ext/rb-grn.h +85 -21
  34. data/ext/rb-groonga.c +13 -3
  35. data/extconf.rb +19 -4
  36. data/html/developer.html +1 -1
  37. data/html/header.html.erb +1 -1
  38. data/html/index.html +4 -4
  39. data/lib/groonga.rb +10 -0
  40. data/lib/groonga/expression-builder.rb +81 -42
  41. data/lib/groonga/patricia-trie.rb +13 -0
  42. data/lib/groonga/record.rb +158 -13
  43. data/lib/groonga/schema.rb +339 -33
  44. data/pkg-config.rb +6 -1
  45. data/test-unit/lib/test/unit.rb +23 -42
  46. data/test-unit/lib/test/unit/assertionfailederror.rb +11 -0
  47. data/test-unit/lib/test/unit/assertions.rb +87 -9
  48. data/test-unit/lib/test/unit/autorunner.rb +20 -11
  49. data/test-unit/lib/test/unit/collector.rb +1 -8
  50. data/test-unit/lib/test/unit/collector/load.rb +2 -3
  51. data/test-unit/lib/test/unit/color-scheme.rb +13 -1
  52. data/test-unit/lib/test/unit/diff.rb +223 -37
  53. data/test-unit/lib/test/unit/error.rb +4 -0
  54. data/test-unit/lib/test/unit/failure.rb +31 -5
  55. data/test-unit/lib/test/unit/notification.rb +8 -4
  56. data/test-unit/lib/test/unit/omission.rb +51 -3
  57. data/test-unit/lib/test/unit/pending.rb +4 -0
  58. data/test-unit/lib/test/unit/testcase.rb +55 -4
  59. data/test-unit/lib/test/unit/ui/console/testrunner.rb +190 -4
  60. data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +14 -0
  61. data/test-unit/lib/test/unit/ui/testrunner.rb +8 -0
  62. data/test-unit/lib/test/unit/version.rb +1 -1
  63. data/test-unit/sample/{tc_adder.rb → test_adder.rb} +3 -1
  64. data/test-unit/sample/{tc_subtracter.rb → test_subtracter.rb} +3 -1
  65. data/test-unit/sample/test_user.rb +1 -0
  66. data/test-unit/test/collector/test-descendant.rb +2 -4
  67. data/test-unit/test/collector/test_objectspace.rb +7 -5
  68. data/test-unit/test/run-test.rb +2 -0
  69. data/test-unit/test/test-color-scheme.rb +7 -0
  70. data/test-unit/test/test-diff.rb +48 -7
  71. data/test-unit/test/test-omission.rb +1 -1
  72. data/test-unit/test/test-testcase.rb +47 -0
  73. data/test-unit/test/test_assertions.rb +79 -10
  74. data/test/groonga-test-utils.rb +6 -1
  75. data/test/test-array.rb +29 -14
  76. data/test/test-column.rb +107 -55
  77. data/test/test-context.rb +5 -0
  78. data/test/test-database.rb +2 -37
  79. data/test/test-exception.rb +9 -1
  80. data/test/test-expression-builder.rb +23 -5
  81. data/test/test-expression.rb +44 -8
  82. data/test/test-fix-size-column.rb +16 -5
  83. data/test/test-gqtp.rb +70 -0
  84. data/test/test-hash.rb +142 -43
  85. data/test/test-index-column.rb +9 -9
  86. data/test/test-patricia-trie.rb +79 -20
  87. data/test/test-procedure.rb +4 -2
  88. data/test/test-record.rb +32 -20
  89. data/test/test-remote.rb +3 -2
  90. data/test/test-schema.rb +226 -92
  91. data/test/test-table-cursor.rb +103 -1
  92. data/test/test-table-offset-and-limit.rb +102 -0
  93. data/test/test-table-select-normalize.rb +4 -4
  94. data/test/test-table-select.rb +52 -8
  95. data/test/test-table.rb +235 -116
  96. data/test/test-type.rb +2 -2
  97. data/test/test-variable-size-column.rb +21 -5
  98. data/test/test-vector-column.rb +76 -0
  99. data/{TUTORIAL.ja.rdoc → text/TUTORIAL.ja.rdoc} +52 -52
  100. data/text/expression.rdoc +284 -0
  101. metadata +11 -7
  102. data/test-unit/sample/ts_examples.rb +0 -7
@@ -19,7 +19,7 @@ class TypeTest < Test::Unit::TestCase
19
19
  setup :setup_database
20
20
 
21
21
  def test_new
22
- type = Groonga::Type.new("user-id", :option => :integer)
22
+ type = Groonga::Type.new("user-id", :type => :integer)
23
23
  assert_equal("user-id", type.name)
24
24
  end
25
25
 
@@ -46,7 +46,7 @@ class TypeTest < Test::Unit::TestCase
46
46
  assert_equal("#<Groonga::Type id: <#{Groonga::Type::LONG_TEXT}>, " +
47
47
  "name: <LongText>, " +
48
48
  "path: (temporary), " +
49
- "domain: <nil>, " +
49
+ "domain: (nil), " +
50
50
  "range: <2147483648>, " +
51
51
  "flags: <>>",
52
52
  context["<longtext>"].inspect)
@@ -25,17 +25,23 @@ class VariableSizeColumnTest < Test::Unit::TestCase
25
25
 
26
26
  def setup_users_table
27
27
  @users_path = @tables_dir + "users"
28
- @users = Groonga::Array.create(:name => "users",
28
+ @users = Groonga::Array.create(:name => "Users",
29
29
  :path => @users_path.to_s)
30
30
 
31
31
  @users_name_column_path = @columns_dir + "name"
32
- @name = @users.define_column("name", "<shorttext>",
32
+ @name = @users.define_column("name", "ShortText",
33
33
  :path => @users_name_column_path.to_s)
34
34
 
35
35
  @users_friends_column_path = @columns_dir + "friends"
36
36
  @friends = @users.define_column("friends", @users,
37
37
  :type => :vector,
38
38
  :path => @users_friends_column_path.to_s)
39
+
40
+ @users_nick_names_column_path = @columns_dir + "nick_names"
41
+ @nick_names =
42
+ @users.define_column("nick_names", "ShortText",
43
+ :type => :vector,
44
+ :path => @users_nick_names_column_path.to_s)
39
45
  end
40
46
 
41
47
  def setup_users
@@ -47,10 +53,10 @@ class VariableSizeColumnTest < Test::Unit::TestCase
47
53
  def test_inspect
48
54
  assert_equal("#<Groonga::VariableSizeColumn " +
49
55
  "id: <#{@name.id}>, " +
50
- "name: <users.name>, " +
56
+ "name: <Users.name>, " +
51
57
  "path: <#{@users_name_column_path}>, " +
52
- "domain: <#{@users.inspect}>, " +
53
- "range: <#{context['<shorttext>'].inspect}>, " +
58
+ "domain: <Users>, " +
59
+ "range: <ShortText>, " +
54
60
  "flags: <>" +
55
61
  ">",
56
62
  @name.inspect)
@@ -79,4 +85,14 @@ class VariableSizeColumnTest < Test::Unit::TestCase
79
85
  @morita.prepend("friends", @gunyara_kun)
80
86
  assert_equal([@gunyara_kun, @yu], @morita["friends"])
81
87
  end
88
+
89
+ def test_string_vector
90
+ omit("append/prepend for non table domain column " +
91
+ "isn't supported by groonga.")
92
+ assert_equal([], @morita["nick_names"])
93
+ @morita.append("nick_names", "morita")
94
+ assert_equal(["morita"], @morita["nick_names"])
95
+ @morita.prepend("nick_names", "moritapo")
96
+ assert_equal(["moritapo", "morita"], @morita["nick_names"])
97
+ end
82
98
  end
@@ -0,0 +1,76 @@
1
+ # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ class VectorColumnTest < Test::Unit::TestCase
17
+ include GroongaTestUtils
18
+
19
+ def setup
20
+ setup_database
21
+ setup_tables
22
+ end
23
+
24
+ def setup_tables
25
+ Groonga::Schema.define do |schema|
26
+ schema.create_table("Users",
27
+ :type => :hash,
28
+ :key_type => "UInt32") do |table|
29
+ end
30
+
31
+ schema.create_table("Communities",
32
+ :type => :hash,
33
+ :key_type => "ShortText") do |table|
34
+ table.reference("users", "Users", :type => :vector)
35
+ end
36
+
37
+ schema.create_table("Areas",
38
+ :type => :hash,
39
+ :key_type => "ShortText") do |table|
40
+ table.reference("communities", "Communities", :type => :vector)
41
+ end
42
+ end
43
+
44
+ @users = Groonga::Context.default["Users"]
45
+ @communities = Groonga::Context.default["Communities"]
46
+ @areas = Groonga::Context.default["Areas"]
47
+ end
48
+
49
+ def test_set_records
50
+ groonga = @communities.add("groonga")
51
+ morita = @users.add(29)
52
+ groonga["users"] = [morita]
53
+
54
+ assert_equal([29], @users.collect {|record| record.key})
55
+ assert_equal([29], groonga["users"].collect {|record| record.key})
56
+ end
57
+
58
+ def test_set_nonexistent_keys
59
+ shinjuku = @areas.add("Shinjuku")
60
+ shinjuku["communities"] = ["groonga", "Senna"]
61
+
62
+ assert_equal(["groonga", "Senna"],
63
+ @communities.collect {|record| record.key})
64
+ assert_equal(["groonga", "Senna"],
65
+ shinjuku["communities"].collect {|record| record.key})
66
+ end
67
+
68
+ def test_set_nil
69
+ groonga = @communities.add("groonga")
70
+ assert_equal([], groonga["users"])
71
+ groonga["users"] = nil
72
+ assert_equal([], groonga["users"]) # should return nil?
73
+ # Can groonga tell us
74
+ # that the value is empty?
75
+ end
76
+ end
@@ -50,14 +50,14 @@ groongaには以下の3種類のテーブルがあります。
50
50
  配列。主キーの存在しないテーブルです。レコードはIDによって
51
51
  識別します。
52
52
 
53
- ここではハッシュテーブルを利用して、<tt><items></tt>という名前のテー
54
- ブルを作成します。
53
+ ここではハッシュテーブルを利用して、<tt>Items</tt>という名前のテー
54
+ ブルを作成します。キーは文字列とします。
55
55
 
56
- >> items = Groonga::Hash.create(:name => "<items>")
56
+ >> items = Groonga::Hash.create(:name => "Items", :key_type => "ShortText")
57
57
  => #<Groonga::Hash ...>
58
58
 
59
59
 
60
- これで<tt><items></tt>という名前のテーブルが作成できました。
60
+ これで<tt>Items</tt>という名前のテーブルが作成できました。
61
61
 
62
62
  テーブルはRubyのHashのように扱えます。
63
63
 
@@ -69,7 +69,7 @@ groongaには以下の3種類のテーブルがあります。
69
69
 
70
70
  == レコードを追加する
71
71
 
72
- <tt><items></tt>テーブルにレコードを追加します。
72
+ <tt>Items</tt>テーブルにレコードを追加します。
73
73
 
74
74
  >> items.add("http://ja.wikipedia.org/wiki/Ruby")
75
75
  => #<Groonga::Record ...>
@@ -91,26 +91,26 @@ groongaには以下の3種類のテーブルがあります。
91
91
  各itemのタイトル文字列を登録して、全文検索できるようにしてみ
92
92
  ましょう。
93
93
 
94
- まず<tt><items></tt>テーブルに+title+という名前のカラムを追加します。
94
+ まず<tt>Items</tt>テーブルに+title+という名前のカラムを追加します。
95
95
 
96
- >> title_column = items.define_column("title", "<text>")
96
+ >> title_column = items.define_column("title", "Text")
97
97
  => #<Groonga::VarSizeColumn ...>
98
98
 
99
99
  2番目の引数は、追加するカラムのデータ型を示しています。
100
- <tt><int></tt>、<tt><text></tt>、<tt><longtext></tt>等の型が基本型として用意されて
100
+ <tt><int></tt>、<tt>Text</tt>、<tt><longtext></tt>等の型が基本型として用意されて
101
101
  います。
102
102
 
103
103
  全文検索するためには、文字列を分解して得られる各単語を格納す
104
- るためのテーブルを別途しなければなりません。ここでは<terms>と
104
+ るためのテーブルを別途しなければなりません。ここではTermsと
105
105
  いう名前でテーブルを定義します。
106
106
 
107
- >> terms = Groonga::Hash.create(:name => "<terms>",
108
- :key_type => "<shorttext>",
109
- :default_tokenizer => "<token:bigram>")
107
+ >> terms = Groonga::Hash.create(:name => "Terms",
108
+ :key_type => "ShortText",
109
+ :default_tokenizer => "TokenBigram")
110
110
  => #<Groonga::Hash ...>
111
111
 
112
112
  ここでは、トークナイザとして<tt>:default_tokenzier =>
113
- "<token:bigram>"</tt> を指定しています。トークナイザとは文字
113
+ "TokenBigram"</tt> を指定しています。トークナイザとは文字
114
114
  列を単語に分解するオブジェクトのことです。デフォルトではトー
115
115
  クナイザは指定されていません。全文検索を利用するためにはトー
116
116
  クナイザを指定する必要があるので、ここではN-gramの一種である
@@ -121,22 +121,22 @@ N-gramを利用した全文検索では、分解したN文字とその出現位
121
121
  るかの文字数になります。groongaは1文字で分解するユニグラム、
122
122
  2文字のバイグラム、3文字のトリグラムをサポートしています。
123
123
 
124
- 単語格納用テーブルの準備ができたので、<tt><items></tt>テーブ
124
+ 単語格納用テーブルの準備ができたので、<tt>Items</tt>テーブ
125
125
  ルの+title+カラムに対するインデックスを定義します。
126
126
 
127
127
  >> title_index_column = terms.define_index_column("item_title", items,
128
- :source => "<items>.title")
128
+ :source => "Items.title")
129
129
  => #<Groonga::IndexColumn ...>
130
130
 
131
- 少し違和感を感じるかも知れませんが、<tt><items></tt>テーブル
132
- のカラムに対するインデックスは、<tt><terms></tt>テーブルのカ
131
+ 少し違和感を感じるかも知れませんが、<tt>Items</tt>テーブル
132
+ のカラムに対するインデックスは、<tt>Terms</tt>テーブルのカ
133
133
  ラムとして定義します。
134
134
 
135
- <tt><items></tt>にレコードが登録されると、その中に含まれる単
136
- 語に該当するレコードが<tt><terms></tt>に自動的に追加されるよ
135
+ <tt>Items</tt>にレコードが登録されると、その中に含まれる単
136
+ 語に該当するレコードが<tt>Terms</tt>に自動的に追加されるよ
137
137
  うになります。
138
138
 
139
- <tt><terms></tt>は、文書に含まれる語彙に相当する、やや特殊な
139
+ <tt>Terms</tt>は、文書に含まれる語彙に相当する、やや特殊な
140
140
  テーブルだと言えます。しかし、他のテーブルと同様に語彙テーブ
141
141
  ルには自由にカラムを追加し、単語毎の様々な属性を管理すること
142
142
  ができます。これはある種の検索処理を行う際には非常に便利に機
@@ -157,9 +157,9 @@ N-gramを利用した全文検索では、分解したN文字とその出現位
157
157
  ["http://ja.wikipedia.org/wiki/Ruby", "http://www.ruby-lang.org/"]
158
158
 
159
159
  検索結果はGroonga::Hashで返されます。ハッシュのキーに見つかっ
160
- た<tt><items></tt>のレコードが入っています。上の例では
161
- +record.key+で<tt><items></tt>のレコードを取得して、さらにそ
162
- のキーを指定して(+record.key.key+)で<tt><items></tt>のキー
160
+ た<tt>Items</tt>のレコードが入っています。上の例では
161
+ +record.key+で<tt>Items</tt>のレコードを取得して、さらにそ
162
+ のキーを指定して(+record.key.key+)で<tt>Items</tt>のキー
163
163
  を返しています。
164
164
 
165
165
 
@@ -174,33 +174,33 @@ N-gramを利用した全文検索では、分解したN文字とその出現位
174
174
 
175
175
  http://qwik.jp/senna/senna2.files/rect4605.png
176
176
 
177
- まず、<tt><users></tt>テーブルを追加します。
177
+ まず、<tt>Users</tt>テーブルを追加します。
178
178
 
179
- >> users = Groonga::Hash.create(:name => "<users>",
180
- :key_type => "<shorttext>")
179
+ >> users = Groonga::Hash.create(:name => "Users",
180
+ :key_type => "ShortText")
181
181
  => #<Groonga::Hash ...>
182
- >> users.define_column("name", "<text>")
182
+ >> users.define_column("name", "Text")
183
183
  => #<Groonga::VarSizeColumn ...>
184
184
 
185
185
 
186
- 次に、<tt><comments></tt>テーブルを追加します。
186
+ 次に、<tt>Comments</tt>テーブルを追加します。
187
187
 
188
- >> comments = Groonga::Array.create(:name => "<comments>")
188
+ >> comments = Groonga::Array.create(:name => "Comments")
189
189
  => #<Groonga::Array ...>
190
190
  >> comments.define_column("item", items)
191
191
  => #<Groonga::FixSizeColumn ..>
192
192
  >> comments.define_column("author", users)
193
193
  => #<Groonga::FixSizeColumn ..>
194
- >> comments.define_column("content", "<text>")
194
+ >> comments.define_column("content", "Text")
195
195
  => #<Groonga::VarSizeColumn ..>
196
- >> comments.define_column("issued", "<time>")
196
+ >> comments.define_column("issued", "Time")
197
197
  => #<Groonga::FixSizeColumn ..>
198
198
 
199
- <tt><comments></tt>テーブルの+content+カラムを全文検索できる
199
+ <tt>Comments</tt>テーブルの+content+カラムを全文検索できる
200
200
  ようにインデックスを定義します。
201
201
 
202
202
  >> terms.define_index_column("comment_content", comments,
203
- :source => "<comments>.content")
203
+ :source => "Comments.content")
204
204
  => #<Groonga::IndexColumn ...>
205
205
 
206
206
  これでテーブルが定義できました。
@@ -218,20 +218,20 @@ http://qwik.jp/senna/senna2.files/rect4605.png
218
218
  ユーザ+moritan+が、はてなダイアリーのとあるページをブックマーク
219
219
  したと想定します。
220
220
 
221
- まず対象のページが<tt><items></tt>テーブルに登録済かどうか調
221
+ まず対象のページが<tt>Items</tt>テーブルに登録済かどうか調
222
222
  べます。
223
223
 
224
224
  >> items.find("http://d.hatena.ne.jp/brazil/20050829/1125321936")
225
225
  => nil
226
226
 
227
- 未登録なのでまず当該ページを<tt><items></tt>に登録します。
227
+ 未登録なのでまず当該ページを<tt>Items</tt>に登録します。
228
228
 
229
229
  >> items.add("http://d.hatena.ne.jp/brazil/20050829/1125321936",
230
230
  :title => "[翻訳]JavaScript: 世界で最も誤解されたプログラミング言語")
231
231
  => #<Groonga::Record ...>
232
232
 
233
233
  次に、登録したitemを+item+カラムの値に指定して
234
- <tt><comments></tt>にレコードを登録します。
234
+ <tt>Comments</tt>にレコードを登録します。
235
235
 
236
236
  >> comments.add(:item => "http://d.hatena.ne.jp/brazil/20050829/1125321936",
237
237
  :author => "moritan",
@@ -258,9 +258,9 @@ http://qwik.jp/senna/senna2.files/rect4605.png
258
258
 
259
259
  +add_bookmark+は以下のような手順を実行しています。
260
260
 
261
- * <tt><items></tt>テーブルに該当ページのレコードがあるかどうか調べる。
261
+ * <tt>Items</tt>テーブルに該当ページのレコードがあるかどうか調べる。
262
262
  * レコードがなければ追加する。
263
- * <tt><comments></tt>テーブルにレコードを登録する。
263
+ * <tt>Comments</tt>テーブルにレコードを登録する。
264
264
 
265
265
  作成したメソッドを呼び出していくつかブックマークを登録してみ
266
266
  ましょう。
@@ -298,8 +298,8 @@ http://qwik.jp/senna/senna2.files/rect4605.png
298
298
 
299
299
  カラムへのアクセスは、カラム名を+.+で繋いで複合データ型の要素
300
300
  を再帰的に辿ることができます。(同様の出力を普通のRDBで実現す
301
- るためには、<tt><items></tt>テーブル、<tt><comments></tt>テー
302
- ブル、<tt><users></tt>テーブルのJOIN操作が必要になります。)
301
+ るためには、<tt>Items</tt>テーブル、<tt>Comments</tt>テー
302
+ ブル、<tt>Users</tt>テーブルのJOIN操作が必要になります。)
303
303
 
304
304
  上の式の中で、肝心の検索処理は、第一引数の式を評価する時点で
305
305
  完了していて、レコードセットオブジェクトとしてメモリに蓄積さ
@@ -330,7 +330,7 @@ http://qwik.jp/senna/senna2.files/rect4605.png
330
330
 
331
331
  >> records.group([".item"]).each do |record|
332
332
  >> item = record.key
333
- >> p [record[".:nsubrecs"],
333
+ >> p [record.n_sub_records,
334
334
  >> item.key,
335
335
  >> item[".title"]]
336
336
  >> end
@@ -338,9 +338,9 @@ http://qwik.jp/senna/senna2.files/rect4605.png
338
338
  [2, "http://practical-scheme.net/docs/cont-j.html", "なんでも継続"]
339
339
  [1, "http://d.hatena.ne.jp/higepon/20070815/1187192864", "末尾再帰"]
340
340
 
341
- +.:nsubrecs+というのはグループ化した単位に含まれるレコードの
342
- 件数を示します。SQLで言えば、GROUP BY句を含むクエリのcount
343
- 数のような働きです。
341
+ +n_sub_records+というのはグループ化した単位に含まれるレコード
342
+ の件数を示します。SQLで言えば、GROUP BY句を含むクエリのcount
343
+ 関数のような働きです。
344
344
 
345
345
  == 少し複雑な検索
346
346
 
@@ -351,18 +351,18 @@ http://qwik.jp/senna/senna2.files/rect4605.png
351
351
  ブックマークが大量に蓄積されるに従って、より的確に適合度を算
352
352
  出する必要性に迫られます。
353
353
 
354
- 今のところ検索対象として利用できるのは<tt><items>.title</tt>
355
- と<tt><comments>.content</tt>ですが、<tt><items>.title</tt>は
354
+ 今のところ検索対象として利用できるのは<tt>Items.title</tt>
355
+ と<tt>Comments.content</tt>ですが、<tt>Items.title</tt>は
356
356
  元ページから得られるやや信頼できる情報なのに対して、
357
- <tt><comments>.content</tt>はブックマークユーザが任意に設定で
357
+ <tt>Comments.content</tt>はブックマークユーザが任意に設定で
358
358
  きる情報で、やや信憑性に乏しいと言えます。しかし、再現率を確
359
359
  保するためにはユーザのコメントも是非対象に含めたいところです。
360
360
 
361
361
  そこで、以下のようなポリシーで検索を行うことにします。
362
362
 
363
- * <tt><items>.title</tt>か<tt><comments>.content</tt>のいずれ
363
+ * <tt>Items.title</tt>か<tt>Comments.content</tt>のいずれ
364
364
  かにマッチするitemを検索する。
365
- * ただし、<tt><items>.title</tt>にマッチしたレコードはスコア
365
+ * ただし、<tt>Items.title</tt>にマッチしたレコードはスコア
366
366
  を10倍重み付けする。
367
367
  * 同一のitemに対して、キーワードにマッチする<tt>comment</tt>
368
368
  が複数存在した場合は、それぞれの<tt>comment</tt>のスコアの
@@ -373,7 +373,7 @@ http://qwik.jp/senna/senna2.files/rect4605.png
373
373
 
374
374
  >> ruby_comments = @comments.select {|record| record["content"] =~ "Ruby"}
375
375
  #<Groonga::Hash ..., size: <2>>
376
- >> ruby_items = @items.select("*W1:50 title:%Ruby")
376
+ >> ruby_items = @items.select("*W1:50 title:@Ruby")
377
377
  #<Groonga::Hash ..., size: <2>>
378
378
 
379
379
  _ruby_comments_の結果をitem毎にグループ化し、_ruby_items_と
@@ -381,8 +381,8 @@ unionして出力します。
381
381
 
382
382
  >> ruby_items = ruby_comments.group([".item"]).union!(ruby_items)
383
383
  #<Groonga::Hash ..., size: <4>>
384
- >> ruby_items.sort([{:key => ".:score", :order => "descendant"}]).each do |record|
385
- >> p [record[".:score"], record[".title"]]
384
+ >> ruby_items.sort([{:key => "._score", :order => "descendant"}]).each do |record|
385
+ >> p [record["._score"], record[".title"]]
386
386
  >> end
387
387
  [1, "るびま"]
388
388
  [1, "オブジェクトスクリプト言語Ruby"]
@@ -0,0 +1,284 @@
1
+
2
+ = grn式 (grn_expr)
3
+
4
+
5
+ == 名前
6
+
7
+ grn式 - 検索条件やデータベースへの操作を表現する文字列の形式。(読み方:"ぐるんしき")
8
+
9
+
10
+ == 書式
11
+
12
+ grn式はquery形式とscript形式のいずれかによって表現することができます。
13
+
14
+ query形式
15
+
16
+ [条件式]
17
+ 以下の条件式が使用できます。
18
+
19
+ [文字列]
20
+ 全文検索条件(デフォルト検索対象カラムの値が指定された文字列を含んでいる)
21
+
22
+ ["文字列"]
23
+ フレーズ検索条件(デフォルト検索対象カラムの値が指定されたフレーズを含んでいる)
24
+
25
+ [カラム名:値]
26
+ 一致条件(カラム値 == 値)
27
+
28
+ [カラム名:!値]
29
+ 不一致条件(カラム値 != 値)
30
+
31
+ [カラム名:<値]
32
+ 比較条件(カラム値 < 値)
33
+
34
+ [カラム名:>値]
35
+ 比較条件(カラム値 > 値)
36
+
37
+ [カラム名:<=値]
38
+ 比較条件(カラム値 <= 値)
39
+
40
+ [カラム名:>=値]
41
+ 比較条件(カラム値 >= 値)
42
+
43
+ [カラム名:@文字列]
44
+ 全文検索条件(カラム値が指定された文字列を含んでいる)
45
+
46
+ [補助演算子 [1]]
47
+ 全文検索条件の挙動を制御する以下の演算子が指定できます。
48
+
49
+ [~文字列]
50
+ 文字列を含んでいた場合は、そのレコードのスコアを下げます。
51
+
52
+ [<文字列]
53
+ 文字列を含んでいた場合に加算されるスコアの値を小さくします。
54
+
55
+ [>文字列]
56
+ 文字列を含んでいた場合に加算されるスコアの値を大きくします。
57
+
58
+ [文字列*]
59
+ 文字列に前方一致する条件を示します。
60
+
61
+ [*S[数値]"文字列"]
62
+ 文字列と関連する文書を検索します。文字列から抽出する特徴語の数を数値に指定します。
63
+
64
+ [*N[数値]"文字列"]
65
+ 文字列に含まれる複数の語が、近傍に含まれる文書を検索します。近傍の範囲の上限とな
66
+ る語数を数値に指定します。N-gramの場合は、文字数を指定します。
67
+
68
+ [結合演算子]
69
+ 複数の条件式を結合するために以下の演算子が使用できる。演算子を伴わずに複数の条件式 が空白('
70
+ ')区切りで指定された場合は、デフォルトの結合演算子が指定されたものとみな される。
71
+
72
+ [a OR b]
73
+ 論理和(aとbといずれかの条件がマッチする)
74
+
75
+ [a + b]
76
+ 論理積(aとbの両方がマッチする)
77
+
78
+ [a - b]
79
+ aにマッチし、bにはマッチしない
80
+
81
+ [( )]
82
+ 複数の条件をまとめる
83
+
84
+ [プラグマ [2]]
85
+ query形式文字列の先頭に、処理方法を指定するプラグマを埋め込むことができます。
86
+
87
+ プラグマは必ずクエリ文字列の冒頭に存在しなければなりません。(先頭に空白を入れては いけません)
88
+
89
+ 一つのクエリに複数のプラグマを指定することができます。
90
+
91
+ 複数のプラグマを指定する場合は、間に空白を入れてはいけません。
92
+
93
+ [*E数値1[,数値2]]
94
+ 検索結果の数が数値1よりも小さい場合、完全一致→非わかち書き→部分一致の順に自動
95
+ 的に検索処理方法を切り替えます。完全一致でヒットした文書と比べて非わかち書き一致、
96
+ 部分一致でヒットした文書には数値2分だけ小さいスコアを付与します。数値2を省略した
97
+ 場合は既定値(=2)と解釈されます。数値1に負の数を指定した場合は以下のように処理し ます。
98
+
99
+ -1
100
+
101
+ 完全一致検索のみを行う
102
+
103
+ -2
104
+
105
+ 非わかち書き検索のみを行う
106
+
107
+ -3
108
+
109
+ 完全一致検索と非わかち書き検索のみを行う
110
+
111
+ -4
112
+
113
+ 部分一致検索のみを行う
114
+
115
+ -5
116
+
117
+ 完全一致検索と部分一致検索のみを行う
118
+
119
+ -6
120
+
121
+ 非わかち書き検索と部分一致検索のみを行う
122
+
123
+ -7
124
+
125
+ 完全一致検索,非わかち書き検索,部分一致検索の全てを行う
126
+
127
+ 例:
128
+
129
+ *E10,3
130
+
131
+ 検索結果数が10件以下だった場合に検索処理方法を順次切り替え、スコアを3ずつ小さ くします。
132
+
133
+ [*D演算子]
134
+ 結合演算子の既定値(演算子を省略した場合にどの演算を行うか)を指定します。指定できる演 算子は、OR, +, - のいずれかです。
135
+
136
+ 例1:
137
+
138
+ *D+ abc def
139
+
140
+ abcとdefを両方含む文書を検索します。
141
+
142
+ 例2:
143
+
144
+ *DOR abc def
145
+
146
+ abcとdefのいずれかを含む文書を検索します。
147
+
148
+ [*W[数値[:重み][,数値[:重み]]...]
149
+ 数値で指定されたセクション番号のみを対象に検索します。セクションごとに検索スコア
150
+ の倍数を指定することができます。重みは、省略された場合1となります。負の重みも指 定することができます。
151
+
152
+ script形式 [3]
153
+
154
+ ECMAScript風の構文で検索条件やレコードへの操作を記述します。
155
+
156
+ 式中のIDENTIFIER(識別子)は、以下のいずれかを指します。
157
+
158
+ [引数名]
159
+ grn式が受け取る引数の名前
160
+
161
+ [カラム名]
162
+ 操作対象としているレコードのカラム名
163
+
164
+ [型名・関数名・テーブル名]
165
+ データベースに定義された型・テーブル・関数の名前
166
+
167
+
168
+ == 説明
169
+
170
+ grn式は、検索条件やデータベースへの操作を表現するために使用される文字列の形式です。
171
+
172
+ selectやloadなどのいくつかの組込コマンドや、API関数grn_table_select()などで使用されます。grn式はquery
173
+ 形式とscript形式という2種類の方式で記述することができます。query形式は、多くのweb検索エンジンなどで検索フォームにユーザが指定
174
+ 可能なクエリ文字列の書式に合わせた形式です。script形式は、ECMAScriptの構文から式(expression)以下の構文要素を抜粋
175
+ した形式になっており、文(statement)や制御構造などは表現できません。
176
+
177
+ query形式のgrn式もscript形式のgrn式も、共通の中間形式に翻訳された上で処理されますので、処理速度や効率には差違はありません。
178
+ 記述できる処理の範囲はscript形式の方がquery形式より広くなっています。たとえば更新系の操作はscript形式のみで記述できます。
179
+
180
+ DB-APIレイヤでは、異なる形式で記述された複数のgrn式を結合することも可能です。
181
+
182
+
183
+ == 例
184
+
185
+ query形式でcolumn1の値が'hoge'に等しいという条件
186
+
187
+ column1:hoge
188
+
189
+ script形式でcolumn1の値が'hoge'に等しいという条件
190
+
191
+ column1 == "hoge"
192
+
193
+
194
+ == 構文
195
+
196
+ query形式のgrn式で有効な式の構文を拡張BNF記法で示します。
197
+
198
+ query ::= query_element
199
+ | ( query query_element )
200
+ | ( query "+" query_element )
201
+ | ( query "-" query_element )
202
+ | ( query "OR" query_element )
203
+ query_element ::= STRING
204
+ | ( "(" query ")" )
205
+ | ( IDENTIFIER relative_operator query_element )
206
+ relative_operator ::= ( ":" | ":!" | ":<" | ":>" | ":<=" | ":>=" | ":@" )
207
+
208
+ script形式のgrn式で有効な式の構文を拡張BNF記法で示します。
209
+
210
+ expression ::= assignment_expression
211
+ expression ::= ( expression "," assignment_expression )
212
+ assignment_expression ::= conditional_expression
213
+ | ( lefthand_side_expression assign_operator assignment_expression )
214
+ assign_operator ::= ( "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | ">>>=" | "&=" | "^=" | "|=" )
215
+ conditional_expression ::= logical_or_expression
216
+ | ( logical_or_expression "?" assignment_expression ":" assignment_expression )
217
+ logical_or_expression ::= logical_and_expression
218
+ | ( logical_or_expression "||" logical_and_expression )
219
+ logical_and_expression ::= bitwise_or_expression
220
+ | ( logical_and_expression logical_and_operator bitwise_or_expression )
221
+ logical_and_operator ::= ( "&&" | "&!" )
222
+ bitwise_or_expression ::= bitwise_xor_expression
223
+ | ( bitwise_or_expression "|" bitwise_xor_expression )
224
+ bitwise_xor_expression ::= bitwise_and_expression
225
+ | ( bitwise_xor_expression "^" bitwise_and_expression )
226
+ bitwise_and_expression ::= equality_expression
227
+ | bitwise_and_expression "&" equality_expression )
228
+ equality_expression ::= relational_expression
229
+ | ( equality_expression equality_operator relational_expression )
230
+ equality_operator ::= ( "==" | "!=" )
231
+ relational_expression ::= shift_expression
232
+ | ( relational_expression relational_operator shift_expression )
233
+ relational_operator ::= ( "<" | ">" | "<=" | ">=" | "in" | "@" )
234
+ shift_expression ::= additive_expression
235
+ | ( shift_expression shift_operator additive_expression )
236
+ shift_operator ::= ( "<<" | ">>" | ">>>" )
237
+ additive_expression ::= multiplicative_expression
238
+ | ( additive_expression additive_operator multiplicative_expression )
239
+ additive_operator ::= ( "+" | "-" )
240
+ multiplicative_expression ::= unary_expression
241
+ | ( multiplicative_expression multiplicative_operator unary_expression )
242
+ multiplicative_operator ::= ( "*" | "/" | "%" )
243
+ unary_expression ::= postfix_expression
244
+ | ( unary_operator unary_expression )
245
+ unary_operator ::= ( "delete" : "++" : "--" : "+" : "-" : "!" : "~" )
246
+ postfix_expression ::= lefthand_side_expression
247
+ | ( lefthand_side_expression postfix_operator )
248
+ postfix_operator ::= ( "++" | "--" )
249
+ lefthand_side_expression ::= (call_expression | member_expression)
250
+ call_expression ::= member_expression arguments
251
+ member_expression ::= primary_expression
252
+ member_expression ::= member_expression member_expression_part
253
+ primary_expression ::= object_literal
254
+ | ( "(" expression ")" )
255
+ | IDENTIFIER
256
+ | array_literal
257
+ | DECIMAL
258
+ | HEX_INTEGER
259
+ | STRING
260
+ | "true"
261
+ | "false
262
+ | "null"
263
+ array_literal ::= ( "[" elision "]" )
264
+ | ( "[" `element_list elision` "]" )
265
+ | ( "[" element_list "]" )
266
+ elision ::= "," | ( elision "," )
267
+ element_list ::= assignment_expression
268
+ | ( elision assignment_expression )
269
+ | ( element_list elision assignment_expression )
270
+ object_literal ::= ( "{" property_name_and_value_list "}" )
271
+ property_name_and_value_list ::= ( property_name_and_value_list "," property_name_and_value )
272
+ property_name_and_value ::= ( property_name ":" assignment_expression )
273
+ property_name ::= IDENTIFIER | STRING | DECIMAL
274
+ member_expression_part ::= "[" expression "]" | ( "." IDENTIFIER )
275
+ arguments ::= ( "(" argument_list ")" )
276
+ argument_list ::= assignment_expression | ( argument_list "," assignment_expression )
277
+
278
+ -[ 脚注 ]-
279
+
280
+ [1] 補助演算子はv1.0でサポートされます。
281
+
282
+ [2] プラグマはv1.0でサポートされます。
283
+
284
+ [3] script形式のgrn式はv1.0でサポートされます。