groonga-client 0.3.3 → 0.3.4
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.
- checksums.yaml +4 -4
- data/doc/text/news.md +13 -0
- data/lib/groonga/client.rb +1 -1
- data/lib/groonga/client/request.rb +22 -0
- data/lib/groonga/client/request/base.rb +174 -0
- data/lib/groonga/client/request/error.rb +39 -0
- data/lib/groonga/client/request/select.rb +292 -0
- data/lib/groonga/client/response/schema.rb +25 -9
- data/lib/groonga/client/response/select.rb +64 -2
- data/lib/groonga/client/spec-helper.rb +37 -0
- data/lib/groonga/client/test-helper.rb +37 -0
- data/lib/groonga/client/test/fixture.rb +35 -0
- data/lib/groonga/client/test/groonga-server-runner.rb +158 -0
- data/lib/groonga/client/version.rb +1 -1
- data/test/request/select/test-filter-parameter.rb +97 -0
- data/test/request/select/test-output-columns-parameter.rb +75 -0
- data/test/request/select/test-sort-keys-columns-parameter.rb +74 -0
- data/test/request/select/test-values-parameter.rb +58 -0
- data/test/request/test-base.rb +50 -0
- data/test/request/test-select.rb +131 -0
- data/test/response/test-schema.rb +342 -0
- data/test/response/test-select-command-version1.rb +68 -0
- data/test/response/test-select-command-version3.rb +70 -0
- data/test/run-test.rb +1 -0
- metadata +36 -16
@@ -102,31 +102,31 @@ module Groonga
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
class Type < Hash
|
105
|
+
class Type < ::Hash
|
106
106
|
include Hashie::Extensions::MethodAccess
|
107
107
|
end
|
108
108
|
|
109
|
-
class Tokenizer < Hash
|
109
|
+
class Tokenizer < ::Hash
|
110
110
|
include Hashie::Extensions::MethodAccess
|
111
111
|
end
|
112
112
|
|
113
|
-
class Normalizer < Hash
|
113
|
+
class Normalizer < ::Hash
|
114
114
|
include Hashie::Extensions::MethodAccess
|
115
115
|
end
|
116
116
|
|
117
|
-
class TokenFilter < Hash
|
117
|
+
class TokenFilter < ::Hash
|
118
118
|
include Hashie::Extensions::MethodAccess
|
119
119
|
end
|
120
120
|
|
121
|
-
class KeyType < Hash
|
121
|
+
class KeyType < ::Hash
|
122
122
|
include Hashie::Extensions::MethodAccess
|
123
123
|
end
|
124
124
|
|
125
|
-
class ValueType < Hash
|
125
|
+
class ValueType < ::Hash
|
126
126
|
include Hashie::Extensions::MethodAccess
|
127
127
|
end
|
128
128
|
|
129
|
-
class Index < Hash
|
129
|
+
class Index < ::Hash
|
130
130
|
include Hashie::Extensions::MethodAccess
|
131
131
|
|
132
132
|
def initialize(schema, raw_index)
|
@@ -155,13 +155,17 @@ module Groonga
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
+
def full_text_searchable?
|
159
|
+
table.tokenizer and column.position
|
160
|
+
end
|
161
|
+
|
158
162
|
private
|
159
163
|
def coerce_table(table_name)
|
160
164
|
@schema.tables[table_name]
|
161
165
|
end
|
162
166
|
end
|
163
167
|
|
164
|
-
class Column < Hash
|
168
|
+
class Column < ::Hash
|
165
169
|
include Hashie::Extensions::MethodAccess
|
166
170
|
|
167
171
|
def initialize(schema, raw_column)
|
@@ -181,6 +185,12 @@ module Groonga
|
|
181
185
|
end
|
182
186
|
end
|
183
187
|
|
188
|
+
def have_full_text_search_index?
|
189
|
+
indexes.any? do |index|
|
190
|
+
index.full_text_searchable?
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
184
194
|
private
|
185
195
|
def coerce_indexes(raw_indexes)
|
186
196
|
raw_indexes.collect do |raw_index|
|
@@ -189,7 +199,7 @@ module Groonga
|
|
189
199
|
end
|
190
200
|
end
|
191
201
|
|
192
|
-
class Table < Hash
|
202
|
+
class Table < ::Hash
|
193
203
|
include Hashie::Extensions::MethodAccess
|
194
204
|
|
195
205
|
def initialize(schema)
|
@@ -214,6 +224,12 @@ module Groonga
|
|
214
224
|
end
|
215
225
|
end
|
216
226
|
|
227
|
+
def have_full_text_search_index?
|
228
|
+
indexes.any? do |index|
|
229
|
+
index.full_text_searchable?
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
217
233
|
private
|
218
234
|
def coerce_key_type(raw_key_type)
|
219
235
|
if raw_key_type.nil?
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Copyright (C) 2013-2016 Kouhei Sutou <kou@clear-code.com>
|
2
2
|
# Copyright (C) 2013 Kosuke Asami
|
3
|
+
# Copyright (C) 2016 Masafumi Yokoyama <yokoyama@clear-code.com>
|
3
4
|
#
|
4
5
|
# This library is free software; you can redistribute it and/or
|
5
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -23,9 +24,13 @@ module Groonga
|
|
23
24
|
class Select < Base
|
24
25
|
Response.register("select", self)
|
25
26
|
|
27
|
+
include Enumerable
|
28
|
+
|
26
29
|
# @return [Integer] The number of records that match againt
|
27
30
|
# a search condition.
|
28
31
|
attr_accessor :n_hits
|
32
|
+
# For Kaminari
|
33
|
+
alias_method :total_count, :n_hits
|
29
34
|
attr_accessor :records
|
30
35
|
|
31
36
|
# @return [::Array<Groonga::Client::Response::Select::Drilldown>,
|
@@ -37,19 +42,51 @@ module Groonga
|
|
37
42
|
# Otherwise, `[drilldown1, drilldown2]` is returned.
|
38
43
|
attr_accessor :drilldowns
|
39
44
|
|
45
|
+
# @return [::Hash<String, Groonga::Client::Response::Select::Slice>]
|
46
|
+
#
|
47
|
+
# @since 0.3.4
|
48
|
+
attr_accessor :slices
|
49
|
+
|
40
50
|
def body=(body)
|
41
51
|
super(body)
|
42
52
|
parse_body(body)
|
43
53
|
end
|
44
54
|
|
55
|
+
# For Kaminari
|
56
|
+
def limit_value
|
57
|
+
(@command[:limit] || 10).to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
# For Kaminari
|
61
|
+
def offset_value
|
62
|
+
(@command[:offset] || 0).to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
# For Kaminari
|
66
|
+
def size
|
67
|
+
records.size
|
68
|
+
end
|
69
|
+
|
70
|
+
def each(&block)
|
71
|
+
records.each(&block)
|
72
|
+
end
|
73
|
+
|
45
74
|
private
|
46
75
|
def parse_body(body)
|
47
76
|
if body.is_a?(::Array)
|
48
77
|
@n_hits, @records = parse_match_records_v1(body.first)
|
49
|
-
@
|
78
|
+
if @command.slices.empty?
|
79
|
+
raw_slices = nil
|
80
|
+
raw_drilldowns = body[1..-1]
|
81
|
+
else
|
82
|
+
raw_slices, *raw_drilldowns = body[1..-1]
|
83
|
+
end
|
84
|
+
@slices = parse_slices_v1(raw_slices)
|
85
|
+
@drilldowns = parse_drilldowns_v1(raw_drilldowns)
|
50
86
|
else
|
51
87
|
@n_hits, @records = parse_match_records_v3(body)
|
52
88
|
@drilldowns = parse_drilldowns_v3(body["drilldowns"])
|
89
|
+
@slices = parse_slices_v3(body["slices"])
|
53
90
|
end
|
54
91
|
body
|
55
92
|
end
|
@@ -74,7 +111,7 @@ module Groonga
|
|
74
111
|
end
|
75
112
|
|
76
113
|
(raw_records || []).collect do |raw_record|
|
77
|
-
record =
|
114
|
+
record = Record.new
|
78
115
|
columns.each_with_index do |(name, type), i|
|
79
116
|
record[name] = convert_value(raw_record[i], type)
|
80
117
|
end
|
@@ -139,10 +176,35 @@ module Groonga
|
|
139
176
|
drilldowns
|
140
177
|
end
|
141
178
|
|
179
|
+
def parse_slices_v1(raw_slices)
|
180
|
+
slices = {}
|
181
|
+
(raw_slices || {}).each do |key, raw_slice|
|
182
|
+
n_hits, records = parse_match_records_v1(raw_slice)
|
183
|
+
slices[key] = Slice.new(key, n_hits, records)
|
184
|
+
end
|
185
|
+
slices
|
186
|
+
end
|
187
|
+
|
188
|
+
def parse_slices_v3(raw_slices)
|
189
|
+
slices = {}
|
190
|
+
(raw_slices || {}).each do |key, raw_slice|
|
191
|
+
n_hits, records = parse_match_records_v3(raw_slice)
|
192
|
+
slices[key] = Slice.new(key, n_hits, records)
|
193
|
+
end
|
194
|
+
slices
|
195
|
+
end
|
196
|
+
|
197
|
+
class Record < ::Hash
|
198
|
+
include Hashie::Extensions::MethodAccess
|
199
|
+
end
|
200
|
+
|
142
201
|
class Drilldown < Struct.new(:key, :n_hits, :records)
|
143
202
|
# @deprecated since 0.2.6. Use {#records} instead.
|
144
203
|
alias_method :items, :records
|
145
204
|
end
|
205
|
+
|
206
|
+
class Slice < Struct.new(:key, :n_hits, :records)
|
207
|
+
end
|
146
208
|
end
|
147
209
|
end
|
148
210
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright (C) 2016 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 as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga/client/test/fixture"
|
18
|
+
|
19
|
+
module Groonga
|
20
|
+
class Client
|
21
|
+
module SpecHelper
|
22
|
+
include Test::Fixture
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def included(target)
|
26
|
+
target.before(:each) do
|
27
|
+
setup_groonga
|
28
|
+
end
|
29
|
+
|
30
|
+
target.after(:each) do
|
31
|
+
teardown_groonga
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright (C) 2016 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 as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga/client/test/fixture"
|
18
|
+
|
19
|
+
module Groonga
|
20
|
+
class Client
|
21
|
+
module TestHelper
|
22
|
+
include Test::Fixture
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def included(target)
|
26
|
+
target.setup do
|
27
|
+
setup_groonga
|
28
|
+
end
|
29
|
+
|
30
|
+
target.teardown do
|
31
|
+
teardown_groonga
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (C) 2016 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 as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga/client/test/groonga-server-runner"
|
18
|
+
|
19
|
+
module Groonga
|
20
|
+
class Client
|
21
|
+
module Test
|
22
|
+
module Fixture
|
23
|
+
def setup_groonga
|
24
|
+
@groonga_server_runner = GroongaServerRunner.new
|
25
|
+
@groonga_server_runner.run
|
26
|
+
end
|
27
|
+
|
28
|
+
def teardown_groonga
|
29
|
+
return if @groonga_server_runner.nil?
|
30
|
+
@groonga_server_runner.stop
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Copyright (C) 2016 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 as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "rbconfig"
|
18
|
+
require "fileutils"
|
19
|
+
|
20
|
+
module Groonga
|
21
|
+
class Client
|
22
|
+
module Test
|
23
|
+
class GroongaServerRunner
|
24
|
+
def initialize
|
25
|
+
@pid = nil
|
26
|
+
@using_running_server = false
|
27
|
+
@url = build_url
|
28
|
+
@groonga = find_groonga
|
29
|
+
@tmp_dir = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def run
|
33
|
+
if groonga_server_running?
|
34
|
+
@using_running_server = true
|
35
|
+
else
|
36
|
+
return if @groonga.nil?
|
37
|
+
@tmp_dir = create_tmp_dir
|
38
|
+
db_path = @tmp_dir + "db"
|
39
|
+
@pid = spawn(@groonga,
|
40
|
+
"--port", @url.port.to_s,
|
41
|
+
"--log-path", (@tmp_dir + "groonga.log").to_s,
|
42
|
+
"--query-log-path", (@tmp_dir + "query.log").to_s,
|
43
|
+
"--protocol", "http",
|
44
|
+
"-s",
|
45
|
+
"-n", db_path.to_s)
|
46
|
+
wait_groonga_ready
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def stop
|
51
|
+
if @using_running_server
|
52
|
+
Groonga::Client.open(url: @url) do |client|
|
53
|
+
schema = client.schema
|
54
|
+
schema.tables.each do |name, _|
|
55
|
+
client.delete(table: name,
|
56
|
+
filter: "true")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
else
|
60
|
+
if @pid
|
61
|
+
Groonga::Client.open(url: @url) do |client|
|
62
|
+
client.shutdown
|
63
|
+
end
|
64
|
+
wait_groonga_shutdown
|
65
|
+
end
|
66
|
+
if @tmp_dir
|
67
|
+
FileUtils.rm_rf(@tmp_dir)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def build_url
|
74
|
+
default_options = Groonga::Client.default_options
|
75
|
+
url = default_options[:url]
|
76
|
+
if url.nil?
|
77
|
+
host = default_options[:host] ||
|
78
|
+
default_options[:address] ||
|
79
|
+
"127.0.0.1"
|
80
|
+
port = default_options[:port] || 10041
|
81
|
+
path = default_options[:path]
|
82
|
+
url = URI("http://#{host}:#{port}#{path}")
|
83
|
+
end
|
84
|
+
url
|
85
|
+
end
|
86
|
+
|
87
|
+
def groonga_server_running?
|
88
|
+
begin
|
89
|
+
TCPSocket.open(@url.host, @url.port) do
|
90
|
+
end
|
91
|
+
rescue SystemCallError
|
92
|
+
false
|
93
|
+
else
|
94
|
+
true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_groonga
|
99
|
+
paths = ENV["PATH"].split(File::PATH_SEPARATOR)
|
100
|
+
exeext = RbConfig::CONFIG["EXEEXT"]
|
101
|
+
paths.each do |path|
|
102
|
+
groonga = File.join(path, "groonga#{exeext}")
|
103
|
+
return groonga if File.executable?(groonga)
|
104
|
+
end
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_tmp_dir
|
109
|
+
tmpfs_dir = "/dev/shm"
|
110
|
+
if File.directory?(tmpfs_dir)
|
111
|
+
base_tmp_dir = Pathname(tmpfs_dir)
|
112
|
+
else
|
113
|
+
base_tmp_dir = Pathname("tmp")
|
114
|
+
end
|
115
|
+
tmp_dir = base_tmp_dir + "groonga-client.#{Process.pid}"
|
116
|
+
FileUtils.rm_rf(tmp_dir)
|
117
|
+
FileUtils.mkdir_p(tmp_dir)
|
118
|
+
tmp_dir
|
119
|
+
end
|
120
|
+
|
121
|
+
def wait_groonga_ready
|
122
|
+
n_retried = 0
|
123
|
+
while n_retried <= 20
|
124
|
+
n_retried += 1
|
125
|
+
sleep(0.05)
|
126
|
+
if groonga_server_running?
|
127
|
+
break
|
128
|
+
else
|
129
|
+
begin
|
130
|
+
pid = Process.waitpid(@pid, Process::WNOHANG)
|
131
|
+
rescue SystemCallError
|
132
|
+
@pid = nil
|
133
|
+
break
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def wait_groonga_shutdown
|
140
|
+
# TODO: Remove me when Groonga 6.0.1 has been released.
|
141
|
+
# Workaround to shutdown as soon as possible.
|
142
|
+
groonga_server_running?
|
143
|
+
|
144
|
+
n_retried = 0
|
145
|
+
while n_retried <= 20
|
146
|
+
n_retried += 1
|
147
|
+
sleep(0.05)
|
148
|
+
pid = Process.waitpid(@pid, Process::WNOHANG)
|
149
|
+
return if pid
|
150
|
+
end
|
151
|
+
|
152
|
+
Process.kill(:KILL, @pid)
|
153
|
+
Process.waitpid(@pid)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|