fluent-plugin-groonga 1.1.8 → 1.2.0

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.
@@ -1,6 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2018 Yasuhiro Horimoto <horimoto@clear-code.com>
4
1
  # Copyright (C) 2012-2017 Kouhei Sutou <kou@clear-code.com>
5
2
  #
6
3
  # This library is free software; you can redistribute it and/or
@@ -22,195 +19,141 @@ require "yajl"
22
19
 
23
20
  require "groonga/client"
24
21
 
22
+ require "fluent/plugin/output"
23
+
25
24
  module Fluent
26
- class GroongaOutput < BufferedOutput
27
- Plugin.register_output("groonga", self)
25
+ module Plugin
26
+ class GroongaOutput < Output
27
+ Plugin.register_output("groonga", self)
28
+ helpers :compat_parameters
28
29
 
29
- def initialize
30
- super
31
- end
30
+ def initialize
31
+ super
32
+ end
33
+
34
+ config_param :protocol, :enum, :list => [:http, :gqtp, :command], :default => :http
32
35
 
33
- config_param :protocol, :enum, :list => [:http, :gqtp, :command], :default => :http
34
- # alias is just for backward compatibility
35
- config_param :store_table, :string, :default => nil, :alias => :table
36
-
37
- config_section :table,
38
- :param_name => "tables",
39
- :required => false,
40
- :multi => true do
41
- config_param :name, :string
42
- config_param :flags, :string, :default => nil
43
- config_param :key_type, :string, :default => nil
44
- config_param :default_tokenizer, :string, :default => nil
45
- config_param :token_filters, :string, :default => nil
46
- config_param :normalizer, :string, :default => nil
47
- config_section :index,
48
- :param_name => "indexes",
36
+ # alias is just for backward compatibility
37
+ config_param :store_table, :string, :default => nil, :alias => :table
38
+
39
+ config_section :table,
40
+ :param_name => "tables",
49
41
  :required => false,
50
42
  :multi => true do
51
43
  config_param :name, :string
52
- config_param :source_table, :string
53
- config_param :source_columns, :string
54
- end
55
- end
56
-
57
- config_section :mapping,
58
- :param_name => "mappings",
59
- :required => false,
60
- :multi => true do
61
- config_param :name, :string
62
- config_param :type, :string, :default => nil
63
- config_section :index,
64
- :param_name => "indexes",
44
+ config_param :flags, :string, :default => nil
45
+ config_param :key_type, :string, :default => nil
46
+ config_param :default_tokenizer, :string, :default => nil
47
+ config_param :token_filters, :string, :default => nil
48
+ config_param :normalizer, :string, :default => nil
49
+ config_section :index,
50
+ :param_name => "indexes",
51
+ :required => false,
52
+ :multi => true do
53
+ config_param :name, :string
54
+ config_param :source_table, :string
55
+ config_param :source_columns, :string
56
+ end
57
+ end
58
+
59
+ config_section :mapping,
60
+ :param_name => "mappings",
65
61
  :required => false,
66
62
  :multi => true do
67
- config_param :table, :string
68
63
  config_param :name, :string
69
- config_param :flags, :string, :default => nil
64
+ config_param :type, :string, :default => nil
65
+ config_section :index,
66
+ :param_name => "indexes",
67
+ :required => false,
68
+ :multi => true do
69
+ config_param :table, :string
70
+ config_param :name, :string
71
+ config_param :flags, :string, :default => nil
72
+ end
70
73
  end
71
- end
72
-
73
- def configure(conf)
74
- super
75
- @client = create_client(@protocol)
76
- @client.configure(conf)
77
74
 
78
- @schema = Schema.new(@client, @store_table, @mappings)
79
- @emitter = Emitter.new(@client, @store_table, @schema)
80
-
81
- @tables = @tables.collect do |table|
82
- TableDefinition.new(table)
75
+ config_section :buffer do
76
+ config_set_default :@type, "memory"
77
+ config_set_default :chunk_keys, ['tag']
83
78
  end
84
- end
85
-
86
- def start
87
- super
88
- @client.start
89
- @emitter.start
90
- tables_creator = TablesCreator.new(@client, @tables)
91
- tables_creator.create
92
- end
93
-
94
- def shutdown
95
- super
96
- @emitter.shutdown
97
- @client.shutdown
98
- end
99
-
100
- def format(tag, time, record)
101
- [tag, time, record].to_msgpack
102
- end
103
79
 
104
- def write(chunk)
105
- @emitter.emit(chunk)
106
- end
107
-
108
- private
109
- def create_client(protocol)
110
- case protocol
111
- when :http, :gqtp
112
- NetworkClient.new(protocol)
113
- when :command
114
- CommandClient.new
115
- end
116
- end
80
+ def configure(conf)
81
+ compat_parameters_convert(conf, :buffer)
82
+ super
83
+ @client = create_client(@protocol)
84
+ @client.configure(conf)
117
85
 
118
- module DefinitionParseMethods
119
- private
120
- def parse_flags(flags)
121
- if flags.is_a?(Array)
122
- flags
123
- else
124
- flags.strip.split(/\s*\|\s*/)
125
- end
126
- end
86
+ @schema = Schema.new(@client, @store_table, @mappings)
87
+ @emitter = Emitter.new(@client, @store_table, @schema)
127
88
 
128
- def parse_items(items)
129
- if items.is_a?(Array)
130
- items
131
- else
132
- items.strip.split(/\s*,\s*/)
89
+ @tables = @tables.collect do |table|
90
+ TableDefinition.new(table)
133
91
  end
134
92
  end
135
- end
136
-
137
- class TableDefinition
138
- include DefinitionParseMethods
139
-
140
- def initialize(raw)
141
- @raw = raw
142
- end
143
93
 
144
- def name
145
- @raw[:name]
94
+ def start
95
+ super
96
+ @client.start
97
+ @emitter.start
98
+ tables_creator = TablesCreator.new(@client, @tables)
99
+ tables_creator.create
146
100
  end
147
101
 
148
- def flags
149
- parse_flags(@raw[:flags] || "TABLE_NO_KEY")
102
+ def shutdown
103
+ super
104
+ @emitter.shutdown
105
+ @client.shutdown
150
106
  end
151
107
 
152
- def key_type
153
- @raw[:key_type]
108
+ def multi_workers_ready?
109
+ true
154
110
  end
155
111
 
156
- def default_tokenizer
157
- @raw[:default_tokenizer]
112
+ def format(tag, time, record)
113
+ [tag, time, record].to_msgpack
158
114
  end
159
115
 
160
- def token_filters
161
- parse_items(@raw[:token_filters] || "")
116
+ def formatted_to_msgpack_binary
117
+ true
162
118
  end
163
119
 
164
- def normalizer
165
- @raw[:normalizer]
120
+ def write(chunk)
121
+ @emitter.emit(chunk)
166
122
  end
167
123
 
168
- def indexes
169
- (@raw[:indexes] || []).collect do |raw|
170
- IndexDefinition.new(self, raw)
124
+ private
125
+ def create_client(protocol)
126
+ case protocol
127
+ when :http, :gqtp
128
+ NetworkClient.new(protocol, self)
129
+ when :command
130
+ CommandClient.new(self)
171
131
  end
172
132
  end
173
133
 
174
- def have_difference?(table)
175
- return true if table.name != name
176
-
177
- table_flags = (parse_flags(table.flags) - ["PERSISTENT"])
178
- return true if table_flags.sort != flags.sort
179
-
180
- return true if table.domain != key_type
181
-
182
- return true if table.default_tokenizer != default_tokenizer
183
-
184
- # TODO
185
- # return true if table.token_filters.sort != token_filters.sort
186
-
187
- return true if table.normalizer != normalizer
188
-
189
- false
190
- end
134
+ module DefinitionParseMethods
135
+ private
136
+ def parse_flags(flags)
137
+ if flags.is_a?(Array)
138
+ flags
139
+ else
140
+ flags.strip.split(/\s*\|\s*/)
141
+ end
142
+ end
191
143
 
192
- def to_create_arguments
193
- arguments = {
194
- "name" => name,
195
- "flags" => flags.join("|"),
196
- "key_type" => key_type,
197
- "default_tokenizer" => default_tokenizer,
198
- # TODO
199
- # "token_filters" => token_filters.join("|"),
200
- "normalizer" => normalizer,
201
- }
202
- arguments.keys.each do |key|
203
- value = arguments[key]
204
- arguments.delete(key) if value.nil? or value.empty?
205
- end
206
- arguments
144
+ def parse_items(items)
145
+ if items.is_a?(Array)
146
+ items
147
+ else
148
+ items.strip.split(/\s*,\s*/)
149
+ end
150
+ end
207
151
  end
208
152
 
209
- class IndexDefinition
153
+ class TableDefinition
210
154
  include DefinitionParseMethods
211
155
 
212
- def initialize(table, raw)
213
- @table = table
156
+ def initialize(raw)
214
157
  @raw = raw
215
158
  end
216
159
 
@@ -218,547 +161,619 @@ module Fluent
218
161
  @raw[:name]
219
162
  end
220
163
 
221
- def source_table
222
- @raw[:source_table]
164
+ def flags
165
+ parse_flags(@raw[:flags] || "TABLE_NO_KEY")
223
166
  end
224
167
 
225
- def source_columns
226
- parse_items(@raw[:source_columns])
168
+ def key_type
169
+ @raw[:key_type]
227
170
  end
228
171
 
229
- def flags
230
- _flags = ["COLUMN_INDEX"]
231
- _flags << "WITH_POSITION" if @table.default_tokenizer
232
- _flags << "WITH_SECTION" if source_columns.size >= 2
233
- _flags
172
+ def default_tokenizer
173
+ @raw[:default_tokenizer]
174
+ end
175
+
176
+ def token_filters
177
+ parse_items(@raw[:token_filters] || "")
178
+ end
179
+
180
+ def normalizer
181
+ @raw[:normalizer]
182
+ end
183
+
184
+ def indexes
185
+ (@raw[:indexes] || []).collect do |raw|
186
+ IndexDefinition.new(self, raw)
187
+ end
188
+ end
189
+
190
+ def have_difference?(table)
191
+ return true if table.name != name
192
+
193
+ table_flags = (parse_flags(table.flags) - ["PERSISTENT"])
194
+ return true if table_flags.sort != flags.sort
195
+
196
+ return true if table.domain != key_type
197
+
198
+ return true if table.default_tokenizer != default_tokenizer
199
+
200
+ # TODO
201
+ # return true if table.token_filters.sort != token_filters.sort
202
+
203
+ return true if table.normalizer != normalizer
204
+
205
+ false
234
206
  end
235
207
 
236
208
  def to_create_arguments
237
- {
238
- "table" => @table.name,
239
- "name" => name,
240
- "flags" => flags.join("|"),
241
- "type" => source_table,
242
- "source" => source_columns.join(","),
209
+ arguments = {
210
+ "name" => name,
211
+ "flags" => flags.join("|"),
212
+ "key_type" => key_type,
213
+ "default_tokenizer" => default_tokenizer,
214
+ # TODO
215
+ # "token_filters" => token_filters.join("|"),
216
+ "normalizer" => normalizer,
243
217
  }
218
+ arguments.keys.each do |key|
219
+ value = arguments[key]
220
+ arguments.delete(key) if value.nil? or value.empty?
221
+ end
222
+ arguments
244
223
  end
245
- end
246
- end
247
224
 
248
- class TablesCreator
249
- def initialize(client, definitions)
250
- @client = client
251
- @definitions = definitions
252
- end
225
+ class IndexDefinition
226
+ include DefinitionParseMethods
253
227
 
254
- def create
255
- return if @definitions.empty?
228
+ def initialize(table, raw)
229
+ @table = table
230
+ @raw = raw
231
+ end
256
232
 
257
- table_list = @client.execute("table_list")
258
- @definitions.each do |definition|
259
- existing_table = table_list.find do |table|
260
- table.name == definition.name
233
+ def name
234
+ @raw[:name]
261
235
  end
262
- if existing_table
263
- next unless definition.have_difference?(existing_table)
264
- # TODO: Is it OK?
265
- @client.execute("table_remove", "name" => definition.name)
236
+
237
+ def source_table
238
+ @raw[:source_table]
266
239
  end
267
240
 
268
- @client.execute("table_create", definition.to_create_arguments)
269
- definition.indexes.each do |index|
270
- @client.execute("column_create", index.to_create_arguments)
241
+ def source_columns
242
+ parse_items(@raw[:source_columns])
271
243
  end
272
- end
273
- end
274
- end
275
244
 
276
- class Schema
277
- def initialize(client, table_name, mappings)
278
- @client = client
279
- @table_name = table_name
280
- @mappings = mappings
281
- @taget_table = nil
282
- @columns = nil
283
- end
245
+ def flags
246
+ _flags = ["COLUMN_INDEX"]
247
+ _flags << "WITH_POSITION" if @table.default_tokenizer
248
+ _flags << "WITH_SECTION" if source_columns.size >= 2
249
+ _flags
250
+ end
284
251
 
285
- def update(records)
286
- ensure_table
287
- ensure_columns
288
-
289
- nonexistent_columns = {}
290
- records.each do |record|
291
- record.each do |key, value|
292
- next if pseudo_column_name?(key)
293
- column = @columns[key]
294
- if column.nil?
295
- nonexistent_columns[key] ||= []
296
- nonexistent_columns[key] << value
297
- end
252
+ def to_create_arguments
253
+ {
254
+ "table" => @table.name,
255
+ "name" => name,
256
+ "flags" => flags.join("|"),
257
+ "type" => source_table,
258
+ "source" => source_columns.join(","),
259
+ }
298
260
  end
299
261
  end
262
+ end
300
263
 
301
- nonexistent_columns.each do |name, values|
302
- @columns[name] = create_column(name, values)
264
+ class TablesCreator
265
+ def initialize(client, definitions)
266
+ @client = client
267
+ @definitions = definitions
303
268
  end
304
- end
305
269
 
306
- private
307
- def ensure_table
308
- return if @target_table
270
+ def create
271
+ return if @definitions.empty?
309
272
 
310
- @tables = {}
311
- @client.execute("table_list").collect do |table|
312
- name = table.name
313
- options = {
314
- :default_tokenizer => table.default_tokenizer,
315
- }
316
- @tables[name] = Table.new(table.name, options)
317
- end
273
+ table_list = @client.execute("table_list")
274
+ @definitions.each do |definition|
275
+ existing_table = table_list.find do |table|
276
+ table.name == definition.name
277
+ end
278
+ if existing_table
279
+ next unless definition.have_difference?(existing_table)
280
+ # TODO: Is it OK?
281
+ @client.execute("table_remove", "name" => definition.name)
282
+ end
318
283
 
319
- @target_table = @tables[@table_name]
320
- unless @target_table
321
- @client.execute("table_create",
322
- "name" => @table_name,
323
- "flags" => "TABLE_NO_KEY")
324
- @target_table = Table.new(@table_name)
325
- @tables[@table_name] = @target_table
284
+ @client.execute("table_create", definition.to_create_arguments)
285
+ definition.indexes.each do |index|
286
+ @client.execute("column_create", index.to_create_arguments)
287
+ end
288
+ end
326
289
  end
327
290
  end
328
291
 
329
- def ensure_columns
330
- return if @columns
331
-
332
- column_list = @client.execute("column_list", "table" => @table_name)
333
- @columns = {}
334
- column_list.each do |column|
335
- name = column.name
336
- @columns[name] = Column.new(name, column.range, column.vector?)
337
- ensure_column_indexes(name)
292
+ class Schema
293
+ def initialize(client, table_name, mappings)
294
+ @client = client
295
+ @table_name = table_name
296
+ @mappings = mappings
297
+ @taget_table = nil
298
+ @columns = nil
338
299
  end
339
- end
340
300
 
341
- def pseudo_column_name?(name)
342
- name.start_with?("_")
343
- end
301
+ def update(records)
302
+ ensure_table
303
+ ensure_columns
344
304
 
345
- def create_column(name, sample_values)
346
- mapping = @mappings.find do |mapping|
347
- mapping.name == name
348
- end
349
- if mapping
350
- value_type = mapping[:type]
351
- end
352
- guesser = TypeGuesser.new(sample_values)
353
- value_type ||= guesser.guess
354
- vector_p = guesser.vector?
355
- if vector_p
356
- flags = "COLUMN_VECTOR"
357
- else
358
- flags = "COLUMN_SCALAR"
359
- end
360
- @client.execute("column_create",
361
- "table" => @table_name,
362
- "name" => name,
363
- "flags" => flags,
364
- "type" => value_type)
365
- ensure_column_indexes(name)
366
-
367
- Column.new(name, value_type, vector_p)
368
- end
305
+ nonexistent_columns = {}
306
+ records.each do |record|
307
+ record.each do |key, value|
308
+ next if pseudo_column_name?(key)
309
+ column = @columns[key]
310
+ if column.nil?
311
+ nonexistent_columns[key] ||= []
312
+ nonexistent_columns[key] << value
313
+ end
314
+ end
315
+ end
369
316
 
370
- def ensure_column_indexes(name)
371
- mapping = @mappings.find do |_mapping|
372
- _mapping.name == name
317
+ nonexistent_columns.each do |name, values|
318
+ @columns[name] = create_column(name, values)
319
+ end
373
320
  end
374
- return if mapping.nil?
375
321
 
376
- mapping.indexes.each do |index|
377
- table = @tables[index[:table]]
378
- if table
379
- column_list = @client.execute("column_list", "table" => table.name)
380
- exist = column_list.any? {|column| column.name == index[:name]}
381
- next if exist
322
+ private
323
+ def ensure_table
324
+ return if @target_table
325
+
326
+ @tables = {}
327
+ @client.execute("table_list").collect do |table|
328
+ name = table.name
329
+ options = {
330
+ :default_tokenizer => table.default_tokenizer,
331
+ }
332
+ @tables[name] = Table.new(table.name, options)
382
333
  end
383
334
 
384
- index_flags = ["COLUMN_INDEX"]
385
- index_flags << "WITH_POSITION" if table and table.default_tokenizer
386
- index_flags << index[:flags] if index[:flags]
335
+ @target_table = @tables[@table_name]
336
+ unless @target_table
337
+ @client.execute("table_create",
338
+ "name" => @table_name,
339
+ "flags" => "TABLE_NO_KEY")
340
+ @target_table = Table.new(@table_name)
341
+ @tables[@table_name] = @target_table
342
+ end
343
+ end
387
344
 
388
- @client.execute("column_create",
389
- "table" => index[:table],
390
- "name" => index[:name],
391
- "flags" => index_flags.join("|"),
392
- "type" => @table_name,
393
- "source" => name)
345
+ def ensure_columns
346
+ return if @columns
347
+
348
+ column_list = @client.execute("column_list", "table" => @table_name)
349
+ @columns = {}
350
+ column_list.each do |column|
351
+ name = column.name
352
+ vector_p = column.flags.split("|").include?("COLUMN_VECTOR")
353
+ @columns[name] = Column.new(name, column.range, vector_p)
354
+ ensure_column_indexes(name)
355
+ end
394
356
  end
395
- end
396
357
 
397
- class TypeGuesser
398
- def initialize(sample_values)
399
- @sample_values = sample_values
358
+ def pseudo_column_name?(name)
359
+ name.start_with?("_")
400
360
  end
401
361
 
402
- def guess
403
- return "Bool" if bool_values?
404
- return "Time" if time_values?
405
- return "Int32" if int32_values?
406
- return "Int64" if int64_values?
407
- return "Float" if float_values?
408
- return "WGS84GeoPoint" if geo_point_values?
409
- return "LongText" if long_text_values?
410
- return "Text" if text_values?
362
+ def create_column(name, sample_values)
363
+ mapping = @mappings.find do |mapping|
364
+ mapping.name == name
365
+ end
366
+ if mapping
367
+ value_type = mapping[:type]
368
+ end
369
+ guesser = TypeGuesser.new(sample_values)
370
+ value_type ||= guesser.guess
371
+ vector_p = guesser.vector?
372
+ if vector_p
373
+ flags = "COLUMN_VECTOR"
374
+ else
375
+ flags = "COLUMN_SCALAR"
376
+ end
377
+ @client.execute("column_create",
378
+ "table" => @table_name,
379
+ "name" => name,
380
+ "flags" => flags,
381
+ "type" => value_type)
382
+ ensure_column_indexes(name)
411
383
 
412
- "ShortText"
384
+ Column.new(name, value_type, vector_p)
413
385
  end
414
386
 
415
- def vector?
416
- @sample_values.any? do |sample_value|
417
- sample_value.is_a?(Array)
387
+ def ensure_column_indexes(name)
388
+ mapping = @mappings.find do |_mapping|
389
+ _mapping.name == name
390
+ end
391
+ return if mapping.nil?
392
+
393
+ mapping.indexes.each do |index|
394
+ table = @tables[index[:table]]
395
+ if table
396
+ column_list = @client.execute("column_list", "table" => table.name)
397
+ exist = column_list.any? {|column| column.name == index[:name]}
398
+ next if exist
399
+ end
400
+
401
+ index_flags = ["COLUMN_INDEX"]
402
+ index_flags << "WITH_POSITION" if table and table.default_tokenizer
403
+ index_flags << index[:flags] if index[:flags]
404
+
405
+ @client.execute("column_create",
406
+ "table" => index[:table],
407
+ "name" => index[:name],
408
+ "flags" => index_flags.join("|"),
409
+ "type" => @table_name,
410
+ "source" => name)
418
411
  end
419
412
  end
420
413
 
421
- private
422
- def integer_value?(value)
423
- case value
424
- when String
425
- begin
426
- Integer(value)
414
+ class TypeGuesser
415
+ def initialize(sample_values)
416
+ @sample_values = sample_values
417
+ end
418
+
419
+ def guess
420
+ return "Bool" if bool_values?
421
+ return "Time" if time_values?
422
+ return "Int32" if int32_values?
423
+ return "Int64" if int64_values?
424
+ return "Float" if float_values?
425
+ return "WGS84GeoPoint" if geo_point_values?
426
+ return "LongText" if long_text_values?
427
+ return "Text" if text_values?
428
+
429
+ "ShortText"
430
+ end
431
+
432
+ def vector?
433
+ @sample_values.any? do |sample_value|
434
+ sample_value.is_a?(Array)
435
+ end
436
+ end
437
+
438
+ private
439
+ def integer_value?(value)
440
+ case value
441
+ when String
442
+ begin
443
+ Integer(value)
444
+ true
445
+ rescue ArgumentError
446
+ false
447
+ end
448
+ when Integer
427
449
  true
428
- rescue ArgumentError
450
+ else
429
451
  false
430
452
  end
431
- when Integer
432
- true
433
- else
434
- false
435
453
  end
436
- end
437
454
 
438
- BOOL_VALUES = [
439
- true,
440
- false,
441
- "true",
442
- "false",
443
- ]
444
- def bool_values?
445
- @sample_values.all? do |sample_value|
446
- BOOL_VALUES.include?(sample_value)
455
+ BOOL_VALUES = [
456
+ true,
457
+ false,
458
+ "true",
459
+ "false",
460
+ ]
461
+ def bool_values?
462
+ @sample_values.all? do |sample_value|
463
+ BOOL_VALUES.include?(sample_value)
464
+ end
447
465
  end
448
- end
449
466
 
450
- def time_values?
451
- now = Time.now.to_i
452
- year_in_seconds = 365 * 24 * 60 * 60
453
- window = 10 * year_in_seconds
454
- new = now + window
455
- old = now - window
456
- recent_range = old..new
457
- @sample_values.all? do |sample_value|
458
- integer_value?(sample_value) and
459
- recent_range.cover?(Integer(sample_value))
467
+ def time_values?
468
+ now = Time.now.to_i
469
+ year_in_seconds = 365 * 24 * 60 * 60
470
+ window = 10 * year_in_seconds
471
+ new = now + window
472
+ old = now - window
473
+ recent_range = old..new
474
+ @sample_values.all? do |sample_value|
475
+ integer_value?(sample_value) and
476
+ recent_range.cover?(Integer(sample_value))
477
+ end
460
478
  end
461
- end
462
479
 
463
- def int32_values?
464
- int32_min = -(2 ** 31)
465
- int32_max = 2 ** 31 - 1
466
- range = int32_min..int32_max
467
- @sample_values.all? do |sample_value|
468
- integer_value?(sample_value) and
469
- range.cover?(Integer(sample_value))
480
+ def int32_values?
481
+ int32_min = -(2 ** 31)
482
+ int32_max = 2 ** 31 - 1
483
+ range = int32_min..int32_max
484
+ @sample_values.all? do |sample_value|
485
+ integer_value?(sample_value) and
486
+ range.cover?(Integer(sample_value))
487
+ end
470
488
  end
471
- end
472
489
 
473
- def int64_values?
474
- @sample_values.all? do |sample_value|
475
- integer_value?(sample_value)
490
+ def int64_values?
491
+ @sample_values.all? do |sample_value|
492
+ integer_value?(sample_value)
493
+ end
476
494
  end
477
- end
478
495
 
479
- def float_value?(value)
480
- case value
481
- when String
482
- begin
483
- Float(value)
496
+ def float_value?(value)
497
+ case value
498
+ when String
499
+ begin
500
+ Float(value)
501
+ true
502
+ rescue ArgumentError
503
+ false
504
+ end
505
+ when Float
484
506
  true
485
- rescue ArgumentError
507
+ else
486
508
  false
487
509
  end
488
- when Float
489
- true
490
- else
491
- false
492
510
  end
493
- end
494
511
 
495
- def float_values?
496
- @sample_values.all? do |sample_value|
497
- float_value?(sample_value)
512
+ def float_values?
513
+ @sample_values.all? do |sample_value|
514
+ float_value?(sample_value)
515
+ end
498
516
  end
499
- end
500
517
 
501
- def geo_point_values?
502
- @sample_values.all? do |sample_value|
503
- sample_value.is_a?(String) and
504
- /\A-?\d+(?:\.\d+)[,x]-?\d+(?:\.\d+)\z/ =~ sample_value
518
+ def geo_point_values?
519
+ @sample_values.all? do |sample_value|
520
+ sample_value.is_a?(String) and
521
+ /\A-?\d+(?:\.\d+)[,x]-?\d+(?:\.\d+)\z/ =~ sample_value
522
+ end
505
523
  end
506
- end
507
524
 
508
- MAX_SHORT_TEXT_SIZE = 2 ** 12
509
- MAX_TEXT_SIZE = 2 ** 16
510
- def text_values?
511
- @sample_values.any? do |sample_value|
512
- sample_value.is_a?(String) and
513
- sample_value.bytesize > MAX_SHORT_TEXT_SIZE
525
+ MAX_SHORT_TEXT_SIZE = 2 ** 12
526
+ MAX_TEXT_SIZE = 2 ** 16
527
+ def text_values?
528
+ @sample_values.any? do |sample_value|
529
+ sample_value.is_a?(String) and
530
+ sample_value.bytesize > MAX_SHORT_TEXT_SIZE
531
+ end
514
532
  end
515
- end
516
533
 
517
- def long_text_values?
518
- @sample_values.any? do |sample_value|
519
- sample_value.is_a?(String) and
520
- sample_value.bytesize > MAX_TEXT_SIZE
534
+ def long_text_values?
535
+ @sample_values.any? do |sample_value|
536
+ sample_value.is_a?(String) and
537
+ sample_value.bytesize > MAX_TEXT_SIZE
538
+ end
521
539
  end
522
540
  end
523
- end
524
541
 
525
- class Table
526
- attr_reader :name
527
- attr_reader :flags
528
- attr_reader :domain
529
- attr_reader :range
530
- attr_reader :default_tokenizer
531
- attr_reader :normalizer
532
- attr_reader :token_filters
533
- def initialize(name, options={})
534
- @name = name
535
- @flags = options[:flags]
536
- @domain = options[:domain]
537
- @range = options[:range]
538
- @default_tokenizer = options[:default_tokenizer]
539
- @normalizer = options[:normalizer]
540
- @token_filters = options[:token_filters]
542
+ class Table
543
+ attr_reader :name
544
+ attr_reader :flags
545
+ attr_reader :domain
546
+ attr_reader :range
547
+ attr_reader :default_tokenizer
548
+ attr_reader :normalizer
549
+ attr_reader :token_filters
550
+ def initialize(name, options={})
551
+ @name = name
552
+ @flags = options[:flags]
553
+ @domain = options[:domain]
554
+ @range = options[:range]
555
+ @default_tokenizer = options[:default_tokenizer]
556
+ @normalizer = options[:normalizer]
557
+ @token_filters = options[:token_filters]
558
+ end
541
559
  end
542
- end
543
560
 
544
- class Column
545
- def initialize(name, value_type, vector_p)
546
- @name = name
547
- @value_type = value_type
548
- @vector_p = vector_p
561
+ class Column
562
+ def initialize(name, value_type, vector_p)
563
+ @name = name
564
+ @value_type = value_type
565
+ @vector_p = vector_p
566
+ end
549
567
  end
550
568
  end
551
- end
552
569
 
553
- class Emitter
554
- def initialize(client, table, schema)
555
- @client = client
556
- @table = table
557
- @schema = schema
558
- end
570
+ class Emitter
571
+ def initialize(client, table, schema)
572
+ @client = client
573
+ @table = table
574
+ @schema = schema
575
+ end
559
576
 
560
- def start
561
- end
577
+ def start
578
+ end
562
579
 
563
- def shutdown
564
- end
580
+ def shutdown
581
+ end
565
582
 
566
- def emit(chunk)
567
- records = []
568
- chunk.msgpack_each do |message|
569
- tag, _, record = message
570
- case tag
571
- when /\Agroonga\.command\./
572
- name = $POSTMATCH
573
- unless records.empty?
574
- store_records(records)
575
- records.clear
583
+ def emit(chunk)
584
+ records = []
585
+ chunk.msgpack_each do |message|
586
+ tag, _, record = message
587
+ if /\Agroonga\.command\./ =~ tag
588
+ name = $POSTMATCH
589
+ unless records.empty?
590
+ store_records(records)
591
+ records.clear
592
+ end
593
+ @client.execute(name, record)
594
+ else
595
+ records << record
576
596
  end
577
- @client.execute(name, record)
578
- when "groonga.command"
579
- name = record["name"]
580
- arguments = record["arguments"]
581
- @client.execute(name, arguments)
582
- else
583
- records << record
584
597
  end
598
+ store_records(records) unless records.empty?
585
599
  end
586
- store_records(records) unless records.empty?
587
- end
588
600
 
589
- private
590
- def store_records(records)
591
- return if @table.nil?
601
+ private
602
+ def store_records(records)
603
+ return if @table.nil?
592
604
 
593
- @schema.update(records)
605
+ @schema.update(records)
594
606
 
595
- arguments = {
596
- "table" => @table,
597
- "values" => Yajl::Encoder.encode(records),
598
- }
599
- @client.execute("load", arguments)
607
+ arguments = {
608
+ "table" => @table,
609
+ "values" => Yajl::Encoder.encode(records),
610
+ }
611
+ @client.execute("load", arguments)
612
+ end
600
613
  end
601
- end
602
614
 
603
- class BaseClient
604
- private
605
- def build_command(name, arguments={})
606
- command_class = Groonga::Command.find(name)
607
- command_class.new(name, arguments)
615
+ class BaseClient
616
+ private
617
+ def build_command(name, arguments={})
618
+ command_class = Groonga::Command.find(name)
619
+ command_class.new(name, arguments)
620
+ end
608
621
  end
609
- end
610
622
 
611
- class NetworkClient < BaseClient
612
- include Configurable
623
+ class NetworkClient < BaseClient
624
+ include Configurable
613
625
 
614
- config_param :host, :string, :default => nil
615
- config_param :port, :integer, :default => nil
626
+ config_param :host, :string, :default => nil
627
+ config_param :port, :integer, :default => nil
616
628
 
617
- def initialize(protocol)
618
- super()
619
- @protocol = protocol
620
- end
629
+ def initialize(protocol, output_plugin)
630
+ super()
631
+ @protocol = protocol
632
+ @output_plugin = output_plugin
633
+ end
621
634
 
622
- def start
623
- @client = nil
624
- end
635
+ def start
636
+ @client = nil
637
+ end
625
638
 
626
- def shutdown
627
- return if @client.nil?
628
- @client.close
629
- end
639
+ def shutdown
640
+ return if @client.nil?
641
+ @client.close
642
+ end
630
643
 
631
- def execute(name, arguments={})
632
- command = build_command(name, arguments)
633
- @client ||= Groonga::Client.new(:protocol => @protocol,
634
- :host => @host,
635
- :port => @port,
636
- :backend => :synchronous)
637
- response = nil
638
- begin
639
- response = @client.execute(command)
640
- rescue Groonga::Client::Error
641
- $log.error("[output][groonga][error]",
642
- :protocol => @protocol,
643
- :host => @host,
644
- :port => @port,
645
- :command_name => name)
646
- raise
647
- end
648
- unless response.success?
649
- $log.error("[output][groonga][error]",
650
- :status_code => response.status_code,
651
- :message => response.message)
652
- end
653
- response
644
+ def execute(name, arguments={})
645
+ command = build_command(name, arguments)
646
+ @client ||= Groonga::Client.new(:protocol => @protocol,
647
+ :host => @host,
648
+ :port => @port,
649
+ :backend => :synchronous)
650
+ response = nil
651
+ begin
652
+ response = @client.execute(command)
653
+ rescue Groonga::Client::Error
654
+ @output_plugin.log.error("[output][groonga][error]",
655
+ :protocol => @protocol,
656
+ :host => @host,
657
+ :port => @port,
658
+ :command_name => name)
659
+ raise
660
+ end
661
+ unless response.success?
662
+ @output_plugin.log.error("[output][groonga][error]",
663
+ :status_code => response.status_code,
664
+ :message => response.message)
665
+ end
666
+ response
667
+ end
654
668
  end
655
- end
656
-
657
- class CommandClient < BaseClient
658
- include Configurable
659
669
 
660
- config_param :groonga, :string, :default => "groonga"
661
- config_param :database, :string
662
- config_param :arguments, :default => [] do |value|
663
- Shellwords.split(value)
664
- end
670
+ class CommandClient < BaseClient
671
+ include Configurable
665
672
 
666
- def initialize
667
- super
668
- end
673
+ config_param :groonga, :string, :default => "groonga"
674
+ config_param :database, :string
675
+ config_param :arguments, :default => [] do |value|
676
+ Shellwords.split(value)
677
+ end
669
678
 
670
- def configure(conf)
671
- super
672
- end
679
+ def initialize(output_plugin)
680
+ super()
681
+ @output_plugin = output_plugin
682
+ end
673
683
 
674
- def start
675
- run_groonga
676
- end
684
+ def configure(conf)
685
+ super
686
+ end
677
687
 
678
- def shutdown
679
- @input.close
680
- read_output("shutdown")
681
- @output.close
682
- @error.close
683
- Process.waitpid(@pid)
684
- end
688
+ def start
689
+ run_groonga
690
+ end
685
691
 
686
- def execute(name, arguments={})
687
- command = build_command(name, arguments)
688
- body = nil
689
- if command.name == "load"
690
- body = command.arguments.delete(:values)
692
+ def shutdown
693
+ @input.close
694
+ read_output("shutdown")
695
+ @output.close
696
+ @error.close
697
+ Process.waitpid(@pid)
691
698
  end
692
- uri = command.to_uri_format
693
- @input.write("#{uri}\n")
694
- if body
695
- body.each_line do |line|
696
- @input.write("#{line}\n")
699
+
700
+ def execute(name, arguments={})
701
+ command = build_command(name, arguments)
702
+ body = nil
703
+ if command.name == "load"
704
+ body = command.arguments.delete(:values)
697
705
  end
706
+ uri = command.to_uri_format
707
+ @input.write("#{uri}\n")
708
+ if body
709
+ body.each_line do |line|
710
+ @input.write("#{line}\n")
711
+ end
712
+ end
713
+ @input.flush
714
+ read_output(uri)
698
715
  end
699
- @input.flush
700
- read_output(uri)
701
- end
702
716
 
703
- private
704
- def run_groonga
705
- env = {}
706
- input = IO.pipe("ASCII-8BIT")
707
- output = IO.pipe("ASCII-8BIT")
708
- error = IO.pipe("ASCII-8BIT")
709
- input_fd = input[0].to_i
710
- output_fd = output[1].to_i
711
- options = {
712
- input_fd => input_fd,
713
- output_fd => output_fd,
714
- :err => error[1],
715
- }
716
- arguments = @arguments
717
- arguments += [
718
- "--input-fd", input_fd.to_s,
719
- "--output-fd", output_fd.to_s,
720
- ]
721
- unless File.exist?(@database)
722
- FileUtils.mkdir_p(File.dirname(@database))
723
- arguments << "-n"
724
- end
725
- arguments << @database
726
- @pid = spawn(env, @groonga, *arguments, options)
727
- input[0].close
728
- @input = input[1]
729
- output[1].close
730
- @output = output[0]
731
- error[1].close
732
- @error = error[0]
733
- end
734
-
735
- def read_output(context)
736
- output_message = ""
737
- error_message = ""
738
-
739
- loop do
740
- readables = IO.select([@output, @error], nil, nil, 0)
741
- break if readables.nil?
742
-
743
- readables.each do |readable|
744
- case readable
745
- when @output
746
- output_message << @output.gets
747
- when @error
748
- error_message << @error.gets
717
+ private
718
+ def run_groonga
719
+ env = {}
720
+ input = IO.pipe("ASCII-8BIT")
721
+ output = IO.pipe("ASCII-8BIT")
722
+ error = IO.pipe("ASCII-8BIT")
723
+ input_fd = input[0].to_i
724
+ output_fd = output[1].to_i
725
+ options = {
726
+ input_fd => input_fd,
727
+ output_fd => output_fd,
728
+ :err => error[1],
729
+ }
730
+ arguments = @arguments
731
+ arguments += [
732
+ "--input-fd", input_fd.to_s,
733
+ "--output-fd", output_fd.to_s,
734
+ ]
735
+ unless File.exist?(@database)
736
+ FileUtils.mkdir_p(File.dirname(@database))
737
+ arguments << "-n"
738
+ end
739
+ arguments << @database
740
+ @pid = spawn(env, @groonga, *arguments, options)
741
+ input[0].close
742
+ @input = input[1]
743
+ output[1].close
744
+ @output = output[0]
745
+ error[1].close
746
+ @error = error[0]
747
+ end
748
+
749
+ def read_output(context)
750
+ output_message = ""
751
+ error_message = ""
752
+
753
+ loop do
754
+ readables = IO.select([@output, @error], nil, nil, 0)
755
+ break if readables.nil?
756
+
757
+ readables.each do |readable|
758
+ case readable
759
+ when @output
760
+ output_message << @output.gets
761
+ when @error
762
+ error_message << @error.gets
763
+ end
749
764
  end
750
765
  end
751
- end
752
766
 
753
- unless output_message.empty?
754
- $log.debug("[output][groonga][output]",
755
- :context => context,
756
- :message => output_message)
757
- end
758
- unless error_message.empty?
759
- $log.error("[output][groonga][error]",
760
- :context => context,
761
- :message => error_message)
767
+ unless output_message.empty?
768
+ @output_plugin.log.debug("[output][groonga][output]",
769
+ :context => context,
770
+ :message => output_message)
771
+ end
772
+ unless error_message.empty?
773
+ @output_plugin.log.error("[output][groonga][error]",
774
+ :context => context,
775
+ :message => error_message)
776
+ end
762
777
  end
763
778
  end
764
779
  end