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.
- data/NEWS.ja.rdoc +56 -0
- data/NEWS.rdoc +58 -0
- data/Rakefile +2 -3
- data/benchmark/read-write-many-small-items.rb +16 -32
- data/benchmark/write-many-small-items.rb +14 -28
- data/example/bookmark.rb +19 -17
- data/example/index-html.rb +11 -1
- data/example/search/config.ru +14 -9
- data/ext/rb-grn-array.c +6 -6
- data/ext/rb-grn-column.c +348 -18
- data/ext/rb-grn-context.c +8 -4
- data/ext/rb-grn-database.c +6 -7
- data/ext/rb-grn-exception.c +101 -5
- data/ext/rb-grn-expression.c +206 -23
- data/ext/rb-grn-fix-size-column.c +6 -39
- data/ext/rb-grn-hash.c +24 -24
- data/ext/rb-grn-index-column.c +74 -19
- data/ext/rb-grn-logger.c +48 -0
- data/ext/rb-grn-object.c +281 -67
- data/ext/rb-grn-operation.c +1 -1
- data/ext/rb-grn-patricia-trie-cursor.c +10 -1
- data/ext/rb-grn-patricia-trie.c +268 -7
- data/ext/rb-grn-query.c +52 -1
- data/ext/rb-grn-record.c +8 -2
- data/ext/rb-grn-snippet.c +63 -1
- data/ext/rb-grn-table-cursor-key-support.c +15 -1
- data/ext/rb-grn-table-cursor.c +57 -0
- data/ext/rb-grn-table-key-support.c +382 -46
- data/ext/rb-grn-table.c +729 -192
- data/ext/rb-grn-type.c +63 -12
- data/ext/rb-grn-utils.c +156 -158
- data/ext/rb-grn-variable.c +18 -0
- data/ext/rb-grn.h +85 -21
- data/ext/rb-groonga.c +13 -3
- data/extconf.rb +19 -4
- data/html/developer.html +1 -1
- data/html/header.html.erb +1 -1
- data/html/index.html +4 -4
- data/lib/groonga.rb +10 -0
- data/lib/groonga/expression-builder.rb +81 -42
- data/lib/groonga/patricia-trie.rb +13 -0
- data/lib/groonga/record.rb +158 -13
- data/lib/groonga/schema.rb +339 -33
- data/pkg-config.rb +6 -1
- data/test-unit/lib/test/unit.rb +23 -42
- data/test-unit/lib/test/unit/assertionfailederror.rb +11 -0
- data/test-unit/lib/test/unit/assertions.rb +87 -9
- data/test-unit/lib/test/unit/autorunner.rb +20 -11
- data/test-unit/lib/test/unit/collector.rb +1 -8
- data/test-unit/lib/test/unit/collector/load.rb +2 -3
- data/test-unit/lib/test/unit/color-scheme.rb +13 -1
- data/test-unit/lib/test/unit/diff.rb +223 -37
- data/test-unit/lib/test/unit/error.rb +4 -0
- data/test-unit/lib/test/unit/failure.rb +31 -5
- data/test-unit/lib/test/unit/notification.rb +8 -4
- data/test-unit/lib/test/unit/omission.rb +51 -3
- data/test-unit/lib/test/unit/pending.rb +4 -0
- data/test-unit/lib/test/unit/testcase.rb +55 -4
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +190 -4
- data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +14 -0
- data/test-unit/lib/test/unit/ui/testrunner.rb +8 -0
- data/test-unit/lib/test/unit/version.rb +1 -1
- data/test-unit/sample/{tc_adder.rb → test_adder.rb} +3 -1
- data/test-unit/sample/{tc_subtracter.rb → test_subtracter.rb} +3 -1
- data/test-unit/sample/test_user.rb +1 -0
- data/test-unit/test/collector/test-descendant.rb +2 -4
- data/test-unit/test/collector/test_objectspace.rb +7 -5
- data/test-unit/test/run-test.rb +2 -0
- data/test-unit/test/test-color-scheme.rb +7 -0
- data/test-unit/test/test-diff.rb +48 -7
- data/test-unit/test/test-omission.rb +1 -1
- data/test-unit/test/test-testcase.rb +47 -0
- data/test-unit/test/test_assertions.rb +79 -10
- data/test/groonga-test-utils.rb +6 -1
- data/test/test-array.rb +29 -14
- data/test/test-column.rb +107 -55
- data/test/test-context.rb +5 -0
- data/test/test-database.rb +2 -37
- data/test/test-exception.rb +9 -1
- data/test/test-expression-builder.rb +23 -5
- data/test/test-expression.rb +44 -8
- data/test/test-fix-size-column.rb +16 -5
- data/test/test-gqtp.rb +70 -0
- data/test/test-hash.rb +142 -43
- data/test/test-index-column.rb +9 -9
- data/test/test-patricia-trie.rb +79 -20
- data/test/test-procedure.rb +4 -2
- data/test/test-record.rb +32 -20
- data/test/test-remote.rb +3 -2
- data/test/test-schema.rb +226 -92
- data/test/test-table-cursor.rb +103 -1
- data/test/test-table-offset-and-limit.rb +102 -0
- data/test/test-table-select-normalize.rb +4 -4
- data/test/test-table-select.rb +52 -8
- data/test/test-table.rb +235 -116
- data/test/test-type.rb +2 -2
- data/test/test-variable-size-column.rb +21 -5
- data/test/test-vector-column.rb +76 -0
- data/{TUTORIAL.ja.rdoc → text/TUTORIAL.ja.rdoc} +52 -52
- data/text/expression.rdoc +284 -0
- metadata +11 -7
- data/test-unit/sample/ts_examples.rb +0 -7
data/test/test-type.rb
CHANGED
|
@@ -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", :
|
|
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:
|
|
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 => "
|
|
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", "
|
|
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: <
|
|
56
|
+
"name: <Users.name>, " +
|
|
51
57
|
"path: <#{@users_name_column_path}>, " +
|
|
52
|
-
"domain:
|
|
53
|
-
"range:
|
|
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
|
|
54
|
-
|
|
53
|
+
ここではハッシュテーブルを利用して、<tt>Items</tt>という名前のテー
|
|
54
|
+
ブルを作成します。キーは文字列とします。
|
|
55
55
|
|
|
56
|
-
>> items = Groonga::Hash.create(:name => "
|
|
56
|
+
>> items = Groonga::Hash.create(:name => "Items", :key_type => "ShortText")
|
|
57
57
|
=> #<Groonga::Hash ...>
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
これで<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
|
|
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
|
|
94
|
+
まず<tt>Items</tt>テーブルに+title+という名前のカラムを追加します。
|
|
95
95
|
|
|
96
|
-
>> title_column = items.define_column("title", "
|
|
96
|
+
>> title_column = items.define_column("title", "Text")
|
|
97
97
|
=> #<Groonga::VarSizeColumn ...>
|
|
98
98
|
|
|
99
99
|
2番目の引数は、追加するカラムのデータ型を示しています。
|
|
100
|
-
<tt><int></tt>、<tt
|
|
100
|
+
<tt><int></tt>、<tt>Text</tt>、<tt><longtext></tt>等の型が基本型として用意されて
|
|
101
101
|
います。
|
|
102
102
|
|
|
103
103
|
全文検索するためには、文字列を分解して得られる各単語を格納す
|
|
104
|
-
|
|
104
|
+
るためのテーブルを別途しなければなりません。ここではTermsと
|
|
105
105
|
いう名前でテーブルを定義します。
|
|
106
106
|
|
|
107
|
-
>> terms = Groonga::Hash.create(:name => "
|
|
108
|
-
:key_type => "
|
|
109
|
-
:default_tokenizer => "
|
|
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
|
-
"
|
|
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
|
|
124
|
+
単語格納用テーブルの準備ができたので、<tt>Items</tt>テーブ
|
|
125
125
|
ルの+title+カラムに対するインデックスを定義します。
|
|
126
126
|
|
|
127
127
|
>> title_index_column = terms.define_index_column("item_title", items,
|
|
128
|
-
:source => "
|
|
128
|
+
:source => "Items.title")
|
|
129
129
|
=> #<Groonga::IndexColumn ...>
|
|
130
130
|
|
|
131
|
-
少し違和感を感じるかも知れませんが、<tt
|
|
132
|
-
のカラムに対するインデックスは、<tt
|
|
131
|
+
少し違和感を感じるかも知れませんが、<tt>Items</tt>テーブル
|
|
132
|
+
のカラムに対するインデックスは、<tt>Terms</tt>テーブルのカ
|
|
133
133
|
ラムとして定義します。
|
|
134
134
|
|
|
135
|
-
<tt
|
|
136
|
-
語に該当するレコードが<tt
|
|
135
|
+
<tt>Items</tt>にレコードが登録されると、その中に含まれる単
|
|
136
|
+
語に該当するレコードが<tt>Terms</tt>に自動的に追加されるよ
|
|
137
137
|
うになります。
|
|
138
138
|
|
|
139
|
-
<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
|
|
161
|
-
+record.key+で<tt
|
|
162
|
-
のキーを指定して(+record.key.key+)で<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
|
|
177
|
+
まず、<tt>Users</tt>テーブルを追加します。
|
|
178
178
|
|
|
179
|
-
>> users = Groonga::Hash.create(:name => "
|
|
180
|
-
:key_type => "
|
|
179
|
+
>> users = Groonga::Hash.create(:name => "Users",
|
|
180
|
+
:key_type => "ShortText")
|
|
181
181
|
=> #<Groonga::Hash ...>
|
|
182
|
-
>> users.define_column("name", "
|
|
182
|
+
>> users.define_column("name", "Text")
|
|
183
183
|
=> #<Groonga::VarSizeColumn ...>
|
|
184
184
|
|
|
185
185
|
|
|
186
|
-
次に、<tt
|
|
186
|
+
次に、<tt>Comments</tt>テーブルを追加します。
|
|
187
187
|
|
|
188
|
-
>> comments = Groonga::Array.create(:name => "
|
|
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", "
|
|
194
|
+
>> comments.define_column("content", "Text")
|
|
195
195
|
=> #<Groonga::VarSizeColumn ..>
|
|
196
|
-
>> comments.define_column("issued", "
|
|
196
|
+
>> comments.define_column("issued", "Time")
|
|
197
197
|
=> #<Groonga::FixSizeColumn ..>
|
|
198
198
|
|
|
199
|
-
<tt
|
|
199
|
+
<tt>Comments</tt>テーブルの+content+カラムを全文検索できる
|
|
200
200
|
ようにインデックスを定義します。
|
|
201
201
|
|
|
202
202
|
>> terms.define_index_column("comment_content", comments,
|
|
203
|
-
:source => "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
261
|
+
* <tt>Items</tt>テーブルに該当ページのレコードがあるかどうか調べる。
|
|
262
262
|
* レコードがなければ追加する。
|
|
263
|
-
* <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
|
|
302
|
-
ブル、<tt
|
|
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
|
|
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
|
-
|
|
342
|
-
|
|
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
|
|
355
|
-
と<tt
|
|
354
|
+
今のところ検索対象として利用できるのは<tt>Items.title</tt>
|
|
355
|
+
と<tt>Comments.content</tt>ですが、<tt>Items.title</tt>は
|
|
356
356
|
元ページから得られるやや信頼できる情報なのに対して、
|
|
357
|
-
<tt
|
|
357
|
+
<tt>Comments.content</tt>はブックマークユーザが任意に設定で
|
|
358
358
|
きる情報で、やや信憑性に乏しいと言えます。しかし、再現率を確
|
|
359
359
|
保するためにはユーザのコメントも是非対象に含めたいところです。
|
|
360
360
|
|
|
361
361
|
そこで、以下のようなポリシーで検索を行うことにします。
|
|
362
362
|
|
|
363
|
-
* <tt
|
|
363
|
+
* <tt>Items.title</tt>か<tt>Comments.content</tt>のいずれ
|
|
364
364
|
かにマッチするitemを検索する。
|
|
365
|
-
* ただし、<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
|
|
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 => "
|
|
385
|
-
>> p [record["
|
|
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でサポートされます。
|