groonga-client 0.5.8 → 0.6.3

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,52 @@
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/base"
18
+ require "groonga/client/response/searchable"
19
+
20
+ module Groonga
21
+ class Client
22
+ module Response
23
+ class LogicalRangeFilter < Base
24
+ Response.register("logical_range_filter", self)
25
+
26
+ include Searchable
27
+
28
+ attr_accessor :records
29
+
30
+ def body=(body)
31
+ super(body)
32
+ parse_body(body)
33
+ end
34
+
35
+ private
36
+ def parse_body(body)
37
+ if body.is_a?(::Array)
38
+ @raw_columns, *@raw_records = body.first
39
+ @raw_records ||= []
40
+ @records = parse_records(raw_columns, raw_records)
41
+ else
42
+ @raw_columns = body["columns"]
43
+ @raw_records = body["records"] || []
44
+ end
45
+ @records = parse_records(@raw_columns, @raw_records)
46
+ body
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -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
@@ -100,25 +102,88 @@ module Groonga
100
102
 
101
103
  drilldowns
102
104
  end
105
+
106
+ def parse_tsv_body(tsv)
107
+ record_sets = []
108
+
109
+ n_hits = parse_tsv_n_hits(tsv.shift)
110
+ columns = parse_tsv_columns(tsv.shift)
111
+ records = []
112
+ loop do
113
+ row = tsv.shift
114
+ break if row.size == 1 and row[0] == "END"
115
+ if (row.size % 4).zero? and row[0] == "[" and row[-1] == "]"
116
+ next_n_hits_row = records.pop
117
+ record_sets << [
118
+ [n_hits],
119
+ columns,
120
+ *records,
121
+ ]
122
+ n_hits = parse_tsv_n_hits(next_n_hits_row)
123
+ columns = parse_tsv_columns(row)
124
+ records = []
125
+ next
126
+ end
127
+ records << parse_tsv_record(row)
128
+ end
129
+
130
+ record_sets << [
131
+ [n_hits],
132
+ columns,
133
+ *records,
134
+ ]
135
+ record_sets
136
+ end
137
+
138
+ def parse_tsv_n_hits(row)
139
+ Integer(row[0], 10)
140
+ end
141
+
142
+ def parse_tsv_columns(row)
143
+ columns = []
144
+ column = nil
145
+ row.each do |value|
146
+ case value
147
+ when "["
148
+ column = []
149
+ when "]"
150
+ columns << column
151
+ else
152
+ column << value
153
+ end
154
+ end
155
+ columns
156
+ end
157
+
158
+ def parse_tsv_record(row)
159
+ record = []
160
+ column_value = nil
161
+ row.each do |value|
162
+ case value
163
+ when "["
164
+ column_value = []
165
+ when "]"
166
+ record << column_value
167
+ else
168
+ if column_value
169
+ column_value << value
170
+ else
171
+ record << value
172
+ end
173
+ end
174
+ end
175
+ record
176
+ end
103
177
  end
104
178
 
105
- include Enumerable
179
+ include Drilldownable
180
+ include Searchable
106
181
 
107
182
  # @return [Integer] The number of records that match againt
108
183
  # a search condition.
109
184
  attr_accessor :n_hits
110
185
  # For Kaminari
111
186
  alias_method :total_count, :n_hits
112
- attr_accessor :records
113
-
114
- # @return [::Array<Groonga::Client::Response::Select::Drilldown>,
115
- # ::Hash<String, Groonga::Client::Response::Select::Drilldown>]
116
- # If labeled drilldowns are used or command version 3 or
117
- # later is used, `{"label1" => drilldown1, "label2" => drilldown2}`
118
- # is returned since 0.3.1.
119
- #
120
- # Otherwise, `[drilldown1, drilldown2]` is returned.
121
- attr_accessor :drilldowns
122
187
 
123
188
  # @return [::Hash<String, Groonga::Client::Response::Select::Slice>]
124
189
  #
@@ -130,29 +195,11 @@ module Groonga
130
195
  parse_body(body)
131
196
  end
132
197
 
133
- # For Kaminari
134
- def limit_value
135
- (@command[:limit] || 10).to_i
136
- end
137
-
138
- # For Kaminari
139
- def offset_value
140
- (@command[:offset] || 0).to_i
141
- end
142
-
143
- # For Kaminari
144
- def size
145
- records.size
146
- end
147
-
148
- def each(&block)
149
- records.each(&block)
150
- end
151
-
152
198
  private
153
199
  def parse_body(body)
154
200
  if body.is_a?(::Array)
155
- @n_hits, @records = parse_match_records_v1(body.first)
201
+ @n_hits, @raw_columns, @raw_records, @records =
202
+ parse_record_set_v1(body.first)
156
203
  if @command.slices.empty?
157
204
  raw_slices = nil
158
205
  raw_drilldowns = body[1..-1]
@@ -160,105 +207,74 @@ module Groonga
160
207
  raw_slices, *raw_drilldowns = body[1..-1]
161
208
  end
162
209
  @slices = parse_slices_v1(raw_slices)
163
- @drilldowns = parse_drilldowns_v1(raw_drilldowns)
164
- else
165
- @n_hits, @records = parse_match_records_v3(body)
166
- @drilldowns = parse_drilldowns_v3(body["drilldowns"])
167
- @slices = parse_slices_v3(body["slices"])
168
- end
169
- body
170
- end
171
-
172
- def parse_records(raw_columns, raw_records)
173
- column_names = {}
174
- columns = raw_columns.collect do |column|
175
- if column.is_a?(::Array)
176
- 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])
177
215
  else
178
- name = column["name"]
179
- type = column["type"]
180
- end
181
- base_column_name = name
182
- suffix = 2
183
- while column_names.key?(name)
184
- name = "#{base_column_name}#{suffix}"
185
- suffix += 1
186
- end
187
- column_names[name] = true
188
- [name, type]
189
- end
190
-
191
- (raw_records || []).collect do |raw_record|
192
- record = Record.new
193
- columns.each_with_index do |(name, type), i|
194
- record[name] = convert_value(raw_record[i], type)
195
- end
196
- record
197
- end
198
- end
199
-
200
- def convert_value(value, type)
201
- case value
202
- when ::Array
203
- value.collect do |element|
204
- convert_value(element, type)
216
+ @drilldowns = parse_drilldowns(drilldown_keys, raw_drilldowns)
205
217
  end
206
218
  else
207
- case type
208
- when "Time"
209
- Time.at(value)
210
- else
211
- 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
212
231
  end
232
+ @drilldowns = parse_labeled_drilldowns(labeled_drilldowns,
233
+ body["drilldowns"])
234
+ @slices = parse_slices_v3(body["slices"])
213
235
  end
236
+ body
214
237
  end
215
238
 
216
- 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] || []
217
243
  [
218
- raw_records.first.first,
219
- 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),
220
248
  ]
221
249
  end
222
250
 
223
- 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"] || []
224
255
  [
225
- raw_records["n_hits"],
226
- parse_records(raw_records["columns"], raw_records["records"]),
256
+ n_hits,
257
+ raw_columns,
258
+ raw_records,
259
+ parse_records(raw_columns, raw_records),
227
260
  ]
228
261
  end
229
262
 
230
- def parse_drilldowns_v1(raw_drilldowns)
231
- request_drilldowns = @command.drilldowns
232
- if request_drilldowns.empty? and !@command.labeled_drilldowns.empty?
233
- drilldowns = {}
234
- (raw_drilldowns[0] || {}).each do |label, raw_drilldown|
235
- n_hits, records = parse_match_records_v1(raw_drilldown)
236
- drilldowns[label] = Drilldown.new(label, n_hits, records)
237
- end
238
- drilldowns
239
- else
240
- (raw_drilldowns || []).collect.with_index do |raw_drilldown, i|
241
- key = request_drilldowns[i]
242
- n_hits, records = parse_match_records_v1(raw_drilldown)
243
- Drilldown.new(key, n_hits, records)
244
- end
245
- end
246
- end
247
-
248
- def parse_drilldowns_v3(raw_drilldowns)
249
- drilldowns = {}
250
- (raw_drilldowns || {}).each do |key, raw_drilldown|
251
- n_hits, records = parse_match_records_v3(raw_drilldown)
252
- drilldowns[key] = Drilldown.new(key, n_hits, records)
253
- end
254
- drilldowns
255
- end
256
-
257
263
  def parse_slices_v1(raw_slices)
258
264
  slices = {}
259
265
  (raw_slices || {}).each do |key, raw_slice|
260
- n_hits, records = parse_match_records_v1(raw_slice)
261
- 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)
262
278
  end
263
279
  slices
264
280
  end
@@ -266,22 +282,17 @@ module Groonga
266
282
  def parse_slices_v3(raw_slices)
267
283
  slices = {}
268
284
  (raw_slices || {}).each do |key, raw_slice|
269
- n_hits, records = parse_match_records_v3(raw_slice)
270
- 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)
271
291
  end
272
292
  slices
273
293
  end
274
294
 
275
- class Record < ::Hash
276
- include Hashie::Extensions::MethodAccess
277
- end
278
-
279
- class Drilldown < Struct.new(:key, :n_hits, :records)
280
- # @deprecated since 0.2.6. Use {#records} instead.
281
- alias_method :items, :records
282
- end
283
-
284
- class Slice < Struct.new(:key, :n_hits, :records)
295
+ class Slice < Struct.new(:key, :n_hits, :records, :drilldowns)
285
296
  end
286
297
  end
287
298
  end