groonga 0.0.7 → 0.9.0

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.
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でサポートされます。