groonga-client 0.5.8 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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