groonga-client 0.6.0 → 0.6.5

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.
@@ -0,0 +1,28 @@
1
+ # Copyright (C) 2019 Sutou Kouhei <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/response/select"
18
+
19
+ module Groonga
20
+ class Client
21
+ module Response
22
+ class LogicalSelect < Select
23
+ Response.register("logical_select", self)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,97 @@
1
+ # Copyright (C) 2019 Sutou Kouhei <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
+ module Groonga
18
+ class Client
19
+ module Response
20
+ module Searchable
21
+ include Enumerable
22
+
23
+ attr_accessor :records
24
+ attr_accessor :raw_columns
25
+ attr_accessor :raw_records
26
+
27
+ # For Kaminari
28
+ def limit_value
29
+ (@command[:limit] || 10).to_i
30
+ end
31
+
32
+ # For Kaminari
33
+ def offset_value
34
+ (@command[:offset] || 0).to_i
35
+ end
36
+
37
+ # For Kaminari
38
+ def size
39
+ records.size
40
+ end
41
+
42
+ def each(&block)
43
+ records.each(&block)
44
+ end
45
+
46
+ private
47
+ def parse_records(raw_columns, raw_records)
48
+ column_names = {}
49
+ columns = raw_columns.collect do |column|
50
+ if column.is_a?(::Array)
51
+ name, type = column
52
+ else
53
+ name = column["name"]
54
+ type = column["type"]
55
+ end
56
+ base_column_name = name
57
+ suffix = 2
58
+ while column_names.key?(name)
59
+ name = "#{base_column_name}#{suffix}"
60
+ suffix += 1
61
+ end
62
+ column_names[name] = true
63
+ [name, type]
64
+ end
65
+
66
+ (raw_records || []).collect do |raw_record|
67
+ record = Record.new
68
+ columns.each_with_index do |(name, type), i|
69
+ record[name] = convert_value(raw_record[i], type)
70
+ end
71
+ record
72
+ end
73
+ end
74
+
75
+ def convert_value(value, type)
76
+ case value
77
+ when ::Array
78
+ value.collect do |element|
79
+ convert_value(element, type)
80
+ end
81
+ else
82
+ case type
83
+ when "Time"
84
+ Time.at(value)
85
+ else
86
+ value
87
+ end
88
+ end
89
+ end
90
+
91
+ class Record < ::Hash
92
+ include Hashie::Extensions::MethodAccess
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2013-2019 Sutou Kouhei <kou@clear-code.com>
2
2
  # Copyright (C) 2013 Kosuke Asami
3
3
  # Copyright (C) 2016 Masafumi Yokoyama <yokoyama@clear-code.com>
4
4
  #
@@ -17,6 +17,8 @@
17
17
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require "groonga/client/response/base"
20
+ require "groonga/client/response/drilldownable"
21
+ require "groonga/client/response/searchable"
20
22
 
21
23
  module Groonga
22
24
  class Client
@@ -174,23 +176,14 @@ module Groonga
174
176
  end
175
177
  end
176
178
 
177
- include Enumerable
179
+ include Drilldownable
180
+ include Searchable
178
181
 
179
182
  # @return [Integer] The number of records that match againt
180
183
  # a search condition.
181
184
  attr_accessor :n_hits
182
185
  # For Kaminari
183
186
  alias_method :total_count, :n_hits
184
- attr_accessor :records
185
-
186
- # @return [::Array<Groonga::Client::Response::Select::Drilldown>,
187
- # ::Hash<String, Groonga::Client::Response::Select::Drilldown>]
188
- # If labeled drilldowns are used or command version 3 or
189
- # later is used, `{"label1" => drilldown1, "label2" => drilldown2}`
190
- # is returned since 0.3.1.
191
- #
192
- # Otherwise, `[drilldown1, drilldown2]` is returned.
193
- attr_accessor :drilldowns
194
187
 
195
188
  # @return [::Hash<String, Groonga::Client::Response::Select::Slice>]
196
189
  #
@@ -202,29 +195,11 @@ module Groonga
202
195
  parse_body(body)
203
196
  end
204
197
 
205
- # For Kaminari
206
- def limit_value
207
- (@command[:limit] || 10).to_i
208
- end
209
-
210
- # For Kaminari
211
- def offset_value
212
- (@command[:offset] || 0).to_i
213
- end
214
-
215
- # For Kaminari
216
- def size
217
- records.size
218
- end
219
-
220
- def each(&block)
221
- records.each(&block)
222
- end
223
-
224
198
  private
225
199
  def parse_body(body)
226
200
  if body.is_a?(::Array)
227
- @n_hits, @records = parse_match_records_v1(body.first)
201
+ @n_hits, @raw_columns, @raw_records, @records =
202
+ parse_record_set_v1(body.first)
228
203
  if @command.slices.empty?
229
204
  raw_slices = nil
230
205
  raw_drilldowns = body[1..-1]
@@ -232,105 +207,74 @@ module Groonga
232
207
  raw_slices, *raw_drilldowns = body[1..-1]
233
208
  end
234
209
  @slices = parse_slices_v1(raw_slices)
235
- @drilldowns = parse_drilldowns_v1(raw_drilldowns)
236
- else
237
- @n_hits, @records = parse_match_records_v3(body)
238
- @drilldowns = parse_drilldowns_v3(body["drilldowns"])
239
- @slices = parse_slices_v3(body["slices"])
240
- end
241
- body
242
- end
243
-
244
- def parse_records(raw_columns, raw_records)
245
- column_names = {}
246
- columns = raw_columns.collect do |column|
247
- if column.is_a?(::Array)
248
- name, type = column
210
+ drilldown_keys = @command.drilldowns
211
+ labeled_drilldowns = @command.labeled_drilldowns
212
+ if drilldown_keys.empty? and !labeled_drilldowns.empty?
213
+ @drilldowns = parse_labeled_drilldowns(labeled_drilldowns,
214
+ raw_drilldowns[0])
249
215
  else
250
- name = column["name"]
251
- type = column["type"]
252
- end
253
- base_column_name = name
254
- suffix = 2
255
- while column_names.key?(name)
256
- name = "#{base_column_name}#{suffix}"
257
- suffix += 1
258
- end
259
- column_names[name] = true
260
- [name, type]
261
- end
262
-
263
- (raw_records || []).collect do |raw_record|
264
- record = Record.new
265
- columns.each_with_index do |(name, type), i|
266
- record[name] = convert_value(raw_record[i], type)
267
- end
268
- record
269
- end
270
- end
271
-
272
- def convert_value(value, type)
273
- case value
274
- when ::Array
275
- value.collect do |element|
276
- convert_value(element, type)
216
+ @drilldowns = parse_drilldowns(drilldown_keys, raw_drilldowns)
277
217
  end
278
218
  else
279
- case type
280
- when "Time"
281
- Time.at(value)
282
- else
283
- value
219
+ @n_hits, @raw_columns, @raw_records, @records =
220
+ parse_record_set_v3(body)
221
+ drilldown_keys = @command.drilldowns
222
+ labeled_drilldowns = @command.labeled_drilldowns
223
+ if labeled_drilldowns.empty?
224
+ drilldown_keys.each do |key|
225
+ labeled_drilldown =
226
+ Groonga::Command::Drilldownable::Drilldown.new
227
+ labeled_drilldown.label = key
228
+ labeled_drilldown.keys = [key]
229
+ labeled_drilldowns[key] = labeled_drilldown
230
+ end
284
231
  end
232
+ @drilldowns = parse_labeled_drilldowns(labeled_drilldowns,
233
+ body["drilldowns"])
234
+ @slices = parse_slices_v3(body["slices"])
285
235
  end
236
+ body
286
237
  end
287
238
 
288
- def parse_match_records_v1(raw_records)
239
+ def parse_record_set_v1(raw_record_set)
240
+ n_hits = raw_record_set.first.first
241
+ raw_columns = raw_record_set[1]
242
+ raw_records = raw_record_set[2..-1] || []
289
243
  [
290
- raw_records.first.first,
291
- parse_records(raw_records[1], raw_records[2..-1]),
244
+ n_hits,
245
+ raw_columns,
246
+ raw_records,
247
+ parse_records(raw_columns, raw_records),
292
248
  ]
293
249
  end
294
250
 
295
- def parse_match_records_v3(raw_records)
251
+ def parse_record_set_v3(raw_record_set)
252
+ n_hits = raw_record_set["n_hits"]
253
+ raw_columns = raw_record_set["columns"]
254
+ raw_records = raw_record_set["records"] || []
296
255
  [
297
- raw_records["n_hits"],
298
- parse_records(raw_records["columns"], raw_records["records"]),
256
+ n_hits,
257
+ raw_columns,
258
+ raw_records,
259
+ parse_records(raw_columns, raw_records),
299
260
  ]
300
261
  end
301
262
 
302
- def parse_drilldowns_v1(raw_drilldowns)
303
- request_drilldowns = @command.drilldowns
304
- if request_drilldowns.empty? and !@command.labeled_drilldowns.empty?
305
- drilldowns = {}
306
- (raw_drilldowns[0] || {}).each do |label, raw_drilldown|
307
- n_hits, records = parse_match_records_v1(raw_drilldown)
308
- drilldowns[label] = Drilldown.new(label, n_hits, records)
309
- end
310
- drilldowns
311
- else
312
- (raw_drilldowns || []).collect.with_index do |raw_drilldown, i|
313
- key = request_drilldowns[i]
314
- n_hits, records = parse_match_records_v1(raw_drilldown)
315
- Drilldown.new(key, n_hits, records)
316
- end
317
- end
318
- end
319
-
320
- def parse_drilldowns_v3(raw_drilldowns)
321
- drilldowns = {}
322
- (raw_drilldowns || {}).each do |key, raw_drilldown|
323
- n_hits, records = parse_match_records_v3(raw_drilldown)
324
- drilldowns[key] = Drilldown.new(key, n_hits, records)
325
- end
326
- drilldowns
327
- end
328
-
329
263
  def parse_slices_v1(raw_slices)
330
264
  slices = {}
331
265
  (raw_slices || {}).each do |key, raw_slice|
332
- n_hits, records = parse_match_records_v1(raw_slice)
333
- slices[key] = Slice.new(key, n_hits, records)
266
+ requested_slice = @command.slices[key]
267
+ if raw_slice.last.is_a?(::Hash)
268
+ raw_drilldowns = raw_slice.last
269
+ raw_slice = raw_slice[0..-2]
270
+ drilldowns =
271
+ parse_labeled_drilldowns(requested_slice.labeled_drilldowns,
272
+ raw_drilldowns)
273
+ else
274
+ drilldowns = {}
275
+ end
276
+ n_hits, _, _, records = parse_record_set_v1(raw_slice)
277
+ slices[key] = Slice.new(key, n_hits, records, drilldowns)
334
278
  end
335
279
  slices
336
280
  end
@@ -338,22 +282,17 @@ module Groonga
338
282
  def parse_slices_v3(raw_slices)
339
283
  slices = {}
340
284
  (raw_slices || {}).each do |key, raw_slice|
341
- n_hits, records = parse_match_records_v3(raw_slice)
342
- slices[key] = Slice.new(key, n_hits, records)
285
+ requested_slice = @command.slices[key]
286
+ n_hits, _, _, records = parse_record_set_v3(raw_slice)
287
+ drilldowns =
288
+ parse_labeled_drilldowns(requested_slice.labeled_drilldowns,
289
+ raw_slice["drilldowns"])
290
+ slices[key] = Slice.new(key, n_hits, records, drilldowns)
343
291
  end
344
292
  slices
345
293
  end
346
294
 
347
- class Record < ::Hash
348
- include Hashie::Extensions::MethodAccess
349
- end
350
-
351
- class Drilldown < Struct.new(:key, :n_hits, :records)
352
- # @deprecated since 0.2.6. Use {#records} instead.
353
- alias_method :items, :records
354
- end
355
-
356
- class Slice < Struct.new(:key, :n_hits, :records)
295
+ class Slice < Struct.new(:key, :n_hits, :records, :drilldowns)
357
296
  end
358
297
  end
359
298
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2018 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2013-2021 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
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Groonga
18
18
  class Client
19
- VERSION = "0.6.0"
19
+ VERSION = "0.6.5"
20
20
  end
21
21
  end
@@ -186,8 +186,8 @@ class TestRequestSelect < Test::Unit::TestCase
186
186
  end
187
187
 
188
188
  sub_test_case("#paginate") do
189
- def paginate(*args)
190
- @request.paginate(*args).to_parameters
189
+ def paginate(*args, **kwargs)
190
+ @request.paginate(*args, **kwargs).to_parameters
191
191
  end
192
192
 
193
193
  sub_test_case("page") do
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2013-2020 Sutou Kouhei <kou@clear-code.com>
2
2
  # Copyright (C) 2013 Kosuke Asami
3
3
  # Copyright (C) 2016 Masafumi Yokoyama <yokoyama@clear-code.com>
4
4
  #
@@ -320,12 +320,14 @@ class TestResponseSelectCommandVersion1 < Test::Unit::TestCase
320
320
  class TestSlices < self
321
321
  def setup
322
322
  pair_arguments = {
323
- "slices[groonga].filter" => 'tag @ "groonga"',
323
+ "slices[groonga].filter" => "tag @ 'groonga'",
324
+ "slices[groonga].limit" => "2",
325
+ "slices[groonga].drilldowns[author].keys" => "author",
324
326
  }
325
327
  @command = Groonga::Command::Select.new("select", pair_arguments)
326
328
  @body = [
327
329
  [
328
- [3],
330
+ [4],
329
331
  [
330
332
  [
331
333
  "_id",
@@ -339,10 +341,11 @@ class TestResponseSelectCommandVersion1 < Test::Unit::TestCase
339
341
  [1, "groonga"],
340
342
  [2, "rroonga"],
341
343
  [3, "groonga"],
344
+ [4, "groonga"],
342
345
  ],
343
346
  {
344
347
  "groonga" => [
345
- [2],
348
+ [3],
346
349
  [
347
350
  [
348
351
  "_id",
@@ -355,19 +358,55 @@ class TestResponseSelectCommandVersion1 < Test::Unit::TestCase
355
358
  ],
356
359
  [1, "groonga"],
357
360
  [3, "groonga"],
358
- ]
359
- }
361
+ {
362
+ "author" => [
363
+ [3],
364
+ [
365
+ [
366
+ "_key",
367
+ "ShortText",
368
+ ],
369
+ [
370
+ "_nsubrecs",
371
+ "Int32",
372
+ ],
373
+ ],
374
+ [
375
+ "Alice",
376
+ 1,
377
+ ],
378
+ [
379
+ "Bob",
380
+ 1,
381
+ ],
382
+ [
383
+ "Chris",
384
+ 1,
385
+ ],
386
+ ],
387
+ },
388
+ ],
389
+ },
360
390
  ]
361
391
  end
362
392
 
363
393
  def test_slices
364
394
  assert_equal({
365
- "groonga" => [
366
- {"_id" => 1, "tag" => "groonga"},
367
- {"_id" => 3, "tag" => "groonga"},
368
- ]
395
+ "groonga" => {
396
+ records: [
397
+ {"_id" => 1, "tag" => "groonga"},
398
+ {"_id" => 3, "tag" => "groonga"},
399
+ ],
400
+ drilldowns: {
401
+ "author" => [
402
+ {"_key" => "Alice", "_nsubrecs" => 1},
403
+ {"_key" => "Bob", "_nsubrecs" => 1},
404
+ {"_key" => "Chris", "_nsubrecs" => 1},
405
+ ],
406
+ },
407
+ },
369
408
  },
370
- collect_values(@body, &:records))
409
+ collect_values(@body))
371
410
  end
372
411
 
373
412
  private
@@ -378,7 +417,14 @@ class TestResponseSelectCommandVersion1 < Test::Unit::TestCase
378
417
  def collect_values(body)
379
418
  values = {}
380
419
  slices(body).each do |label, slice|
381
- values[label] = yield(slice)
420
+ drilldowns = {}
421
+ slice.drilldowns.each do |drilldown_label, drilldown|
422
+ drilldowns[drilldown_label] = drilldown.records
423
+ end
424
+ values[label] = {
425
+ records: slice.records,
426
+ drilldowns: drilldowns,
427
+ }
382
428
  end
383
429
  values
384
430
  end