groonga-client 0.5.9 → 0.6.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 +54 -0
- data/groonga-client.gemspec +3 -2
- data/lib/groonga/client/command-line/groonga-client.rb +160 -2
- data/lib/groonga/client/protocol/http/synchronous.rb +45 -14
- data/lib/groonga/client/response.rb +3 -1
- data/lib/groonga/client/response/base.rb +49 -4
- data/lib/groonga/client/response/drilldownable.rb +85 -0
- data/lib/groonga/client/response/logical-range-filter.rb +52 -0
- data/lib/groonga/client/response/logical-select.rb +28 -0
- data/lib/groonga/client/response/searchable.rb +97 -0
- data/lib/groonga/client/response/select.rb +64 -125
- data/lib/groonga/client/version.rb +2 -2
- data/test/request/test-select.rb +2 -2
- data/test/response/test-base.rb +14 -1
- data/test/response/test-select-command-version1.rb +58 -12
- data/test/response/test-select-command-version3.rb +54 -8
- data/test/response/test-select-tsv.rb +26 -4
- data/test/response/test-select-xml.rb +26 -4
- metadata +41 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78a494db54af1981f348843b06df72cfb4d2c09d1dda5095aa75d8b5b05a8cc9
|
4
|
+
data.tar.gz: 1551dc509c115d96a94d70816234b42baef30c7c19bcd74a01e62b595a0633ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 227087c01d247af0e6e4255fcafda882babc27aef9da37425ef1759b18dc4ec7839bf0d643ee2ac31844ab17f8d67a3643a516b296f04e8f78ca4fa347e00ae1
|
7
|
+
data.tar.gz: 3bd74b528c567aeb7679d59898232a35a622dc5a999df43cdbc6041721a29a4f625de74df70adf5fa0228bb459cd738c4d04a0b946d9bd2eff974b3807875cb4
|
data/doc/text/news.md
CHANGED
@@ -1,5 +1,59 @@
|
|
1
1
|
# NEWS
|
2
2
|
|
3
|
+
## 0.6.4 - 2021-02-24
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Added support for Ruby 3.0.
|
8
|
+
|
9
|
+
## 0.6.3 - 2020-06-08
|
10
|
+
|
11
|
+
### Improvements
|
12
|
+
|
13
|
+
* `groonga-client`:
|
14
|
+
|
15
|
+
* Added support for `-load-input-type=apache-arrow`.
|
16
|
+
|
17
|
+
* Added `--load-lock-table`.
|
18
|
+
|
19
|
+
* `http`: Added support for debugging by
|
20
|
+
`GROONGA_CLIENT_HTTP_DEBUG=yes` environment variable.
|
21
|
+
|
22
|
+
* Added support for response in Apache Arrow.
|
23
|
+
|
24
|
+
## 0.6.2 - 2019-09-02
|
25
|
+
|
26
|
+
### Improvements
|
27
|
+
|
28
|
+
* `Groonga::Client::Response::LogicalSelect`: Added.
|
29
|
+
|
30
|
+
* `Groonga::Client::Response::Select#raw_columns`: Added.
|
31
|
+
|
32
|
+
* `Groonga::Client::Response::Select#raw_records`: Added.
|
33
|
+
|
34
|
+
* `Groonga::Client::Response::LogicalRangeFilter`: Added.
|
35
|
+
|
36
|
+
## 0.6.1 - 2019-07-27
|
37
|
+
|
38
|
+
### Improvements
|
39
|
+
|
40
|
+
* `Groonga::Client::Request::Select`: Added support for drilldowns
|
41
|
+
in slice.
|
42
|
+
|
43
|
+
## 0.6.0 - 2018-08-30
|
44
|
+
|
45
|
+
### Improvements
|
46
|
+
|
47
|
+
* `groonga-client`:
|
48
|
+
|
49
|
+
* Added `--target-command` option.
|
50
|
+
|
51
|
+
* Added `--target-table` option.
|
52
|
+
|
53
|
+
* Added `--target-column` option.
|
54
|
+
|
55
|
+
* Added support for JSONP.
|
56
|
+
|
3
57
|
## 0.5.9 - 2018-07-13
|
4
58
|
|
5
59
|
### Improvements
|
data/groonga-client.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- mode: ruby -*-
|
2
2
|
#
|
3
3
|
# Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
|
4
|
-
# Copyright (C) 2014-
|
4
|
+
# Copyright (C) 2014-2021 Sutou Kouhei <kou@clear-code.com>
|
5
5
|
#
|
6
6
|
# This library is free software; you can redistribute it and/or
|
7
7
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -48,9 +48,10 @@ Gem::Specification.new do |spec|
|
|
48
48
|
end
|
49
49
|
|
50
50
|
spec.add_runtime_dependency("gqtp", ">= 1.0.4")
|
51
|
-
spec.add_runtime_dependency("groonga-command", ">= 1.
|
51
|
+
spec.add_runtime_dependency("groonga-command", ">= 1.4.7")
|
52
52
|
spec.add_runtime_dependency("groonga-command-parser", ">= 1.1.0")
|
53
53
|
spec.add_runtime_dependency("hashie")
|
54
|
+
spec.add_runtime_dependency("rexml")
|
54
55
|
|
55
56
|
spec.add_development_dependency("bundler")
|
56
57
|
spec.add_development_dependency("rake")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2015-
|
1
|
+
# Copyright (C) 2015-2020 Sutou Kouhei <kou@clear-code.com>
|
2
2
|
#
|
3
3
|
# This library is free software; you can redistribute it and/or
|
4
4
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -19,6 +19,11 @@ require "json"
|
|
19
19
|
require "pathname"
|
20
20
|
require "securerandom"
|
21
21
|
|
22
|
+
begin
|
23
|
+
require "arrow"
|
24
|
+
rescue LoadError
|
25
|
+
end
|
26
|
+
|
22
27
|
require "groonga/command/parser"
|
23
28
|
|
24
29
|
require "groonga/client"
|
@@ -30,10 +35,19 @@ module Groonga
|
|
30
35
|
class GroongaClient
|
31
36
|
def initialize
|
32
37
|
@chunk = false
|
38
|
+
@load_input_type = "json"
|
39
|
+
@available_load_input_types = ["json"]
|
40
|
+
if Object.const_defined?(:Arrow)
|
41
|
+
@available_load_input_types << "apache-arrow"
|
42
|
+
end
|
43
|
+
@load_lock_table = false
|
33
44
|
|
34
45
|
@runner_options = {
|
35
46
|
:split_load_chunk_size => 10000,
|
36
47
|
:generate_request_id => false,
|
48
|
+
:target_commands => [],
|
49
|
+
:target_tables => [],
|
50
|
+
:target_columns => [],
|
37
51
|
}
|
38
52
|
end
|
39
53
|
|
@@ -43,7 +57,9 @@ module Groonga
|
|
43
57
|
parse_command_line(option_parser)
|
44
58
|
end
|
45
59
|
|
46
|
-
parser.open_client(:chunk => @chunk
|
60
|
+
parser.open_client(:chunk => @chunk,
|
61
|
+
:load_input_type => @load_input_type,
|
62
|
+
:load_lock_table => @load_lock_table) do |client|
|
47
63
|
runner = Runner.new(client, @runner_options)
|
48
64
|
|
49
65
|
if command_file_paths.empty?
|
@@ -96,6 +112,20 @@ module Groonga
|
|
96
112
|
@runner_options[:split_load_chunk_size] = size
|
97
113
|
end
|
98
114
|
|
115
|
+
parser.on("--load-input-type=TYPE",
|
116
|
+
@available_load_input_types,
|
117
|
+
"Use TYPE as input type for load.",
|
118
|
+
"[#{@available_load_input_types.join(", ")}]",
|
119
|
+
"(#{@load_input_types})") do |type|
|
120
|
+
@load_input_type = type
|
121
|
+
end
|
122
|
+
|
123
|
+
parser.on("--[no-]load-lock-table",
|
124
|
+
"Use lock_table=yes for load.",
|
125
|
+
"(#{@load_lock_table})") do |boolean|
|
126
|
+
@load_lock_table = boolean
|
127
|
+
end
|
128
|
+
|
99
129
|
parser.on("--[no-]generate-request-id",
|
100
130
|
"Add auto generated request ID to all commands.",
|
101
131
|
"(#{@runner_options[:generate_request_id]})") do |boolean|
|
@@ -108,6 +138,39 @@ module Groonga
|
|
108
138
|
"(#{@chunk})") do |boolean|
|
109
139
|
@chunk = boolean
|
110
140
|
end
|
141
|
+
|
142
|
+
parser.on("--target-command=COMMAND",
|
143
|
+
"Add COMMAND as target commands",
|
144
|
+
"You can specify multiple times",
|
145
|
+
"If COMMAND is /.../,",
|
146
|
+
"it's treated as a regular expression") do |command|
|
147
|
+
add_target(@runner_options[:target_commands], command)
|
148
|
+
end
|
149
|
+
|
150
|
+
parser.on("--target-table=TABLE",
|
151
|
+
"Add TABLE as target tables",
|
152
|
+
"You can specify multiple times",
|
153
|
+
"If TABLE is /.../,",
|
154
|
+
"it's treated as a regular expression") do |table|
|
155
|
+
add_target(@runner_options[:target_tables], table)
|
156
|
+
end
|
157
|
+
|
158
|
+
parser.on("--target-column=COLUMN",
|
159
|
+
"Add COLUMN as target columns",
|
160
|
+
"You can specify multiple times",
|
161
|
+
"If COLUMN is /.../,",
|
162
|
+
"it's treated as a regular expression") do |column|
|
163
|
+
add_target(@runner_options[:target_columns], column)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def add_target(targets, target)
|
168
|
+
if /\A\\(.+?)\\(i)?\z/ =~ target
|
169
|
+
pattern = Regexp.new($1, $2 == "i")
|
170
|
+
targets << pattern
|
171
|
+
else
|
172
|
+
targets << target
|
173
|
+
end
|
111
174
|
end
|
112
175
|
|
113
176
|
class Runner
|
@@ -115,6 +178,9 @@ module Groonga
|
|
115
178
|
@client = client
|
116
179
|
@split_load_chunk_size = options[:split_load_chunk_size] || 10000
|
117
180
|
@generate_request_id = options[:generate_request_id]
|
181
|
+
@target_commands = options[:target_commands]
|
182
|
+
@target_tables = options[:target_tables]
|
183
|
+
@target_columns = options[:target_columns]
|
118
184
|
@load_values = []
|
119
185
|
@parser = create_command_parser
|
120
186
|
end
|
@@ -188,6 +254,12 @@ module Groonga
|
|
188
254
|
end
|
189
255
|
|
190
256
|
def run_command(command)
|
257
|
+
return unless target_command?(command)
|
258
|
+
return unless target_table?(command)
|
259
|
+
return unless target_column?(command)
|
260
|
+
|
261
|
+
command = Marshal.load(Marshal.dump(command))
|
262
|
+
apply_target_columns(command)
|
191
263
|
command[:request_id] ||= SecureRandom.uuid if @generate_request_id
|
192
264
|
response = @client.execute(command)
|
193
265
|
case command.output_type
|
@@ -199,6 +271,92 @@ module Groonga
|
|
199
271
|
puts(response.body)
|
200
272
|
end
|
201
273
|
end
|
274
|
+
|
275
|
+
def target_command?(command)
|
276
|
+
return true if @target_commands.empty?
|
277
|
+
|
278
|
+
@target_commands.any? do |name|
|
279
|
+
name === command.command_name
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def target_table?(command)
|
284
|
+
return true if @target_tables.empty?
|
285
|
+
|
286
|
+
target = nil
|
287
|
+
case command.command_name
|
288
|
+
when "load", "column_create", "select"
|
289
|
+
target = command.table
|
290
|
+
when "table_create", "table_remove"
|
291
|
+
target = command.name
|
292
|
+
end
|
293
|
+
return true if target.nil?
|
294
|
+
|
295
|
+
@target_tables.any? do |name|
|
296
|
+
name === target
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def target_column?(command)
|
301
|
+
return true if @target_columns.empty?
|
302
|
+
|
303
|
+
target = nil
|
304
|
+
case command.command_name
|
305
|
+
when "column_create"
|
306
|
+
target = command.name
|
307
|
+
end
|
308
|
+
return true if target.nil?
|
309
|
+
|
310
|
+
@target_columns.any? do |name|
|
311
|
+
name === target
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def apply_target_columns(command)
|
316
|
+
return if @target_columns.empty?
|
317
|
+
|
318
|
+
values = command[:values]
|
319
|
+
return if values.nil?
|
320
|
+
|
321
|
+
command = command.dup
|
322
|
+
|
323
|
+
values = JSON.parse(values)
|
324
|
+
columns = command[:columns]
|
325
|
+
if columns
|
326
|
+
columns = columns.split(/\s*,\s*/)
|
327
|
+
target_indexes = []
|
328
|
+
new_columns = []
|
329
|
+
columns.each_with_index do |column, i|
|
330
|
+
if load_target_column?(column)
|
331
|
+
target_indexes << i
|
332
|
+
new_columns << column
|
333
|
+
end
|
334
|
+
end
|
335
|
+
command[:columns] = new_columns.join(",")
|
336
|
+
new_values = values.collect do |value|
|
337
|
+
target_indexes.collect do |i|
|
338
|
+
value[i]
|
339
|
+
end
|
340
|
+
end
|
341
|
+
command[:values] = JSON.generate(new_values)
|
342
|
+
else
|
343
|
+
new_values = values.collect do |value|
|
344
|
+
new_value = {}
|
345
|
+
value.each do |key, value|
|
346
|
+
if load_target_column?(key)
|
347
|
+
new_value[key] = value
|
348
|
+
end
|
349
|
+
end
|
350
|
+
new_value
|
351
|
+
end
|
352
|
+
command[:values] = JSON.generate(new_values)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def load_target_column?(column)
|
357
|
+
column == "_key" or
|
358
|
+
@target_columns.any? {|name| name === column}
|
359
|
+
end
|
202
360
|
end
|
203
361
|
|
204
362
|
class BareREPL
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2020 Sutou Kouhei <kou@clear-code.com>
|
3
3
|
#
|
4
4
|
# This library is free software; you can redistribute it and/or
|
5
5
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -55,9 +55,15 @@ module Groonga
|
|
55
55
|
@options = options
|
56
56
|
end
|
57
57
|
|
58
|
+
DEBUG = (ENV["GROONGA_CLIENT_HTTP_DEBUG"] == "yes")
|
58
59
|
def send(command)
|
59
60
|
begin
|
60
|
-
HTTPClient.
|
61
|
+
http = HTTPClient.new(@url.host, @url.port)
|
62
|
+
http.set_debug_output($stderr) if DEBUG
|
63
|
+
start_options.each do |key, value|
|
64
|
+
http.__send__("#{key}=", value)
|
65
|
+
end
|
66
|
+
http.start do
|
61
67
|
http.read_timeout = read_timeout
|
62
68
|
response = send_request(http, command)
|
63
69
|
case response
|
@@ -141,18 +147,7 @@ module Groonga
|
|
141
147
|
|
142
148
|
def send_request(http, command)
|
143
149
|
if command.is_a?(Groonga::Command::Load)
|
144
|
-
|
145
|
-
command[:values] = nil
|
146
|
-
path = resolve_path(@url, command.to_uri_format)
|
147
|
-
command[:values] = raw_values
|
148
|
-
request = Net::HTTP::Post.new(path, headers)
|
149
|
-
request.content_type = "application/json"
|
150
|
-
if @options[:chunk]
|
151
|
-
request["Transfer-Encoding"] = "chunked"
|
152
|
-
else
|
153
|
-
request.content_length = raw_values.bytesize
|
154
|
-
end
|
155
|
-
request.body_stream = StringIO.new(raw_values)
|
150
|
+
request = prepare_load_request(command)
|
156
151
|
else
|
157
152
|
path = resolve_path(@url, command.to_uri_format)
|
158
153
|
request = Net::HTTP::Get.new(path, headers)
|
@@ -167,6 +162,42 @@ module Groonga
|
|
167
162
|
}
|
168
163
|
end
|
169
164
|
|
165
|
+
def prepare_load_request(command)
|
166
|
+
path_prefix = command.path_prefix
|
167
|
+
command = command.class.new(command.command_name,
|
168
|
+
command.arguments,
|
169
|
+
[])
|
170
|
+
command.path_prefix = path_prefix
|
171
|
+
case @options[:load_input_type]
|
172
|
+
when "apache-arrow"
|
173
|
+
command[:input_type] = "apache-arrow"
|
174
|
+
content_type = "application/x-apache-arrow-streaming"
|
175
|
+
arrow_table = command.build_arrow_table
|
176
|
+
if arrow_table
|
177
|
+
buffer = Arrow::ResizableBuffer.new(1024)
|
178
|
+
arrow_table.save(buffer, format: :stream)
|
179
|
+
body = buffer.data.to_s
|
180
|
+
else
|
181
|
+
body = ""
|
182
|
+
end
|
183
|
+
command.arguments.delete(:values)
|
184
|
+
else
|
185
|
+
content_type = "application/json"
|
186
|
+
body = command.arguments.delete(:values)
|
187
|
+
end
|
188
|
+
command[:lock_table] = "yes" if @options[:load_lock_table]
|
189
|
+
path = resolve_path(@url, command.to_uri_format)
|
190
|
+
request = Net::HTTP::Post.new(path, headers)
|
191
|
+
if @options[:chunk]
|
192
|
+
request["Transfer-Encoding"] = "chunked"
|
193
|
+
else
|
194
|
+
request.content_length = body.bytesize
|
195
|
+
end
|
196
|
+
request.content_type = content_type
|
197
|
+
request.body_stream = StringIO.new(body)
|
198
|
+
request
|
199
|
+
end
|
200
|
+
|
170
201
|
def setup_authentication(request)
|
171
202
|
userinfo = @url.userinfo
|
172
203
|
return if userinfo.nil?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014-
|
1
|
+
# Copyright (C) 2014-2019 Sutou Kouhei <kou@clear-code.com>
|
2
2
|
# Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
|
3
3
|
#
|
4
4
|
# This library is free software; you can redistribute it and/or
|
@@ -30,6 +30,8 @@ require "groonga/client/response/lock-clear"
|
|
30
30
|
require "groonga/client/response/log-level"
|
31
31
|
require "groonga/client/response/log-put"
|
32
32
|
require "groonga/client/response/log-reopen"
|
33
|
+
require "groonga/client/response/logical-range-filter"
|
34
|
+
require "groonga/client/response/logical-select"
|
33
35
|
require "groonga/client/response/quit"
|
34
36
|
require "groonga/client/response/register"
|
35
37
|
require "groonga/client/response/schema"
|
@@ -1,7 +1,5 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
1
|
# Copyright (C) 2013 Haruka Yoshihara <yoshihara@clear-code.com>
|
4
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2018 Kouhei Sutou <kou@clear-code.com>
|
5
3
|
#
|
6
4
|
# This library is free software; you can redistribute it and/or
|
7
5
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -21,6 +19,10 @@ require "csv"
|
|
21
19
|
require "rexml/document"
|
22
20
|
require "json"
|
23
21
|
|
22
|
+
begin
|
23
|
+
require "arrow"
|
24
|
+
rescue LoadError
|
25
|
+
end
|
24
26
|
require "hashie"
|
25
27
|
|
26
28
|
module Groonga
|
@@ -67,7 +69,13 @@ module Groonga
|
|
67
69
|
return_code = nil
|
68
70
|
case command.output_type
|
69
71
|
when :json
|
70
|
-
|
72
|
+
callback = command["callback"]
|
73
|
+
if callback and
|
74
|
+
/\A#{Regexp.escape(callback)}\((.+)\);\z/ =~ raw_response
|
75
|
+
response = JSON.parse($1)
|
76
|
+
else
|
77
|
+
response = JSON.parse(raw_response)
|
78
|
+
end
|
71
79
|
if response.is_a?(::Array)
|
72
80
|
header, body = response
|
73
81
|
return_code = header[0] if header
|
@@ -82,6 +90,9 @@ module Groonga
|
|
82
90
|
when :tsv
|
83
91
|
header, body = parse_tsv(raw_response)
|
84
92
|
return_code = header["return_code"] if header
|
93
|
+
when :arrow, :"apache-arrow"
|
94
|
+
header, body = parse_apache_arrow(raw_response)
|
95
|
+
return_code = header["return_code"] if header
|
85
96
|
else
|
86
97
|
header = nil
|
87
98
|
body = raw_response
|
@@ -173,6 +184,40 @@ module Groonga
|
|
173
184
|
end
|
174
185
|
body
|
175
186
|
end
|
187
|
+
|
188
|
+
def parse_apache_arrow(response)
|
189
|
+
header = nil
|
190
|
+
body = nil
|
191
|
+
buffer = Arrow::Buffer.new(response)
|
192
|
+
Arrow::BufferInputStream.open(buffer) do |input|
|
193
|
+
while input.tell < response.bytesize
|
194
|
+
reader = Arrow::RecordBatchStreamReader.new(input)
|
195
|
+
schema = reader.schema
|
196
|
+
record_batches = reader.to_a
|
197
|
+
if apache_arrow_metadata?(schema)
|
198
|
+
table = Arrow::Table.new(schema, record_batches)
|
199
|
+
header = table.each_record.first.to_h
|
200
|
+
else
|
201
|
+
body = {}
|
202
|
+
body["columns"] = schema.fields.collect do |field|
|
203
|
+
[field.name, field.data_type.to_s]
|
204
|
+
end
|
205
|
+
if record_batches.empty?
|
206
|
+
records = []
|
207
|
+
else
|
208
|
+
table = Arrow::Table.new(schema, record_batches)
|
209
|
+
records = table.raw_records
|
210
|
+
end
|
211
|
+
body["records"] = records
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
return header, body
|
216
|
+
end
|
217
|
+
|
218
|
+
def apache_arrow_metadata?(schema)
|
219
|
+
(schema.metadata || {})["GROONGA:data_type"] == "metadata"
|
220
|
+
end
|
176
221
|
end
|
177
222
|
|
178
223
|
# @return [Groonga::Command] The command for the request.
|