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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16493f7abb6c08faa1f10060a64bedca44071acc4772ed06ac400a91a4b96282
4
- data.tar.gz: 0c94271fb51d35d2758e423a7d52cacc46488ae1c6afbc9ab947b3093dedcfee
3
+ metadata.gz: 78a494db54af1981f348843b06df72cfb4d2c09d1dda5095aa75d8b5b05a8cc9
4
+ data.tar.gz: 1551dc509c115d96a94d70816234b42baef30c7c19bcd74a01e62b595a0633ea
5
5
  SHA512:
6
- metadata.gz: 8df38010cd75c8e29bd0461243ed0ce07a6cb7efe84450e2eae1e5a8612cd3237be4f00c37bda7a10bbd7016bf5be88507961016337664c988a27ee5c66487b5
7
- data.tar.gz: 3a23a2605d9711c4e46582b5a06021a434763d7021a4bd1263be077e605ec1bdcfdfb7fd8d858e2166bd0213ec2feb24961317699a486d33c817182214c3106b
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
@@ -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-2016 Kouhei Sutou <kou@clear-code.com>
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.2.8")
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-2018 Kouhei Sutou <kou@clear-code.com>
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) do |client|
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-2016 Kouhei Sutou <kou@clear-code.com>
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.start(@url.host, @url.port, start_options) do |http|
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
- raw_values = command[:values]
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-2016 Kouhei Sutou <kou@clear-code.com>
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-2014 Kouhei Sutou <kou@clear-code.com>
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
- response = JSON.parse(raw_response)
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.