fluent-plugin-droonga 0.0.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/.yardopts +7 -0
- data/Gemfile +14 -2
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +27 -5
- data/benchmark/benchmark.rb +1 -1
- data/benchmark/utils.rb +9 -6
- data/benchmark/watch/benchmark-notify.rb +2 -2
- data/benchmark/watch/benchmark-publish.rb +1 -1
- data/benchmark/watch/benchmark-scan.rb +1 -1
- data/benchmark/watch/catalog.json +1 -1
- data/bin/grn2jsons +1 -1
- data/fluent-plugin-droonga.gemspec +5 -3
- data/lib/droonga/adapter.rb +13 -130
- data/lib/droonga/adapter_plugin.rb +51 -0
- data/lib/droonga/catalog.rb +2 -2
- data/lib/droonga/collector.rb +107 -0
- data/lib/droonga/collector_plugin.rb +82 -0
- data/lib/droonga/command_mapper.rb +1 -1
- data/lib/droonga/{proxy.rb → dispatcher.rb} +116 -151
- data/lib/droonga/distributor.rb +51 -0
- data/lib/droonga/distributor_plugin.rb +59 -0
- data/lib/droonga/engine.rb +9 -50
- data/lib/droonga/farm.rb +47 -0
- data/lib/droonga/forwarder.rb +125 -0
- data/lib/droonga/handler.rb +69 -60
- data/lib/droonga/handler_plugin.rb +22 -11
- data/lib/droonga/input_message.rb +51 -0
- data/lib/droonga/job_queue.rb +5 -1
- data/lib/droonga/job_queue_schema.rb +1 -1
- data/lib/droonga/logger.rb +1 -1
- data/lib/droonga/partition.rb +76 -0
- data/lib/droonga/pluggable.rb +62 -0
- data/lib/droonga/plugin.rb +18 -16
- data/lib/droonga/plugin/{adapter_groonga.rb → adapter/groonga.rb} +10 -10
- data/lib/droonga/plugin/adapter/groonga/select.rb +13 -4
- data/lib/droonga/plugin/collector/basic.rb +142 -0
- data/lib/droonga/plugin/distributor/crud.rb +43 -0
- data/lib/droonga/plugin/distributor/groonga.rb +37 -0
- data/lib/droonga/plugin/distributor/search.rb +273 -0
- data/lib/droonga/plugin/distributor/watch.rb +39 -0
- data/lib/droonga/plugin/{handler_add.rb → handler/add.rb} +6 -6
- data/lib/droonga/plugin/{handler_forward.rb → handler/forward.rb} +9 -4
- data/lib/droonga/plugin/{handler_groonga.rb → handler/groonga.rb} +36 -4
- data/lib/droonga/plugin/handler/groonga/column_create.rb +5 -9
- data/lib/droonga/plugin/handler/groonga/table_create.rb +9 -18
- data/lib/droonga/plugin/{handler_search.rb → handler/search.rb} +4 -4
- data/lib/droonga/plugin/{handler_watch.rb → handler/watch.rb} +4 -4
- data/lib/droonga/plugin_loader.rb +45 -0
- data/lib/droonga/plugin_registerable.rb +51 -0
- data/lib/droonga/plugin_repository.rb +56 -0
- data/lib/droonga/processor.rb +64 -0
- data/lib/droonga/searcher.rb +16 -7
- data/lib/droonga/server.rb +5 -9
- data/lib/droonga/sweeper.rb +1 -1
- data/lib/droonga/watch_schema.rb +1 -1
- data/lib/droonga/watcher.rb +1 -1
- data/lib/droonga/worker.rb +21 -9
- data/lib/fluent/plugin/out_droonga.rb +33 -15
- data/lib/groonga_command_converter.rb +1 -1
- data/sample/cluster/fluentd.conf +0 -1
- data/test/command/config/default/catalog.json +43 -0
- data/test/command/config/default/fluentd.conf +11 -0
- data/test/command/fixture/documents.jsons +208 -0
- data/test/command/fixture/user-table-array.jsons +38 -0
- data/test/command/fixture/user-table.jsons +38 -0
- data/test/command/run-test.rb +35 -0
- data/test/command/suite/add/minimum.expected +12 -0
- data/test/command/suite/add/minimum.test +11 -0
- data/test/command/suite/add/with-values.expected +12 -0
- data/test/command/suite/add/with-values.test +17 -0
- data/test/command/suite/add/without-key.expected +12 -0
- data/test/command/suite/add/without-key.test +16 -0
- data/test/command/suite/groonga/column_create/scalar.expected +34 -0
- data/test/command/suite/groonga/column_create/scalar.test +17 -0
- data/test/command/suite/groonga/column_create/vector.expected +34 -0
- data/test/command/suite/groonga/column_create/vector.test +18 -0
- data/test/command/suite/groonga/select/minimum.expected +26 -0
- data/test/command/suite/groonga/select/minimum.test +8 -0
- data/test/command/suite/groonga/table_create/array.expected +17 -0
- data/test/command/suite/groonga/table_create/array.test +8 -0
- data/test/command/suite/groonga/table_create/hash.expected +17 -0
- data/test/command/suite/groonga/table_create/hash.test +8 -0
- data/test/command/suite/search/array-attribute-label.expected +25 -0
- data/test/command/suite/search/array-attribute-label.test +30 -0
- data/test/command/suite/search/chained-queries.expected +45 -0
- data/test/command/suite/search/chained-queries.test +43 -0
- data/test/command/suite/search/complex.expected +52 -0
- data/test/command/suite/search/complex.test +25 -0
- data/test/command/suite/search/condition-nested.expected +19 -0
- data/test/command/suite/search/condition-nested.test +29 -0
- data/test/command/suite/search/condition-query.expected +28 -0
- data/test/command/suite/search/condition-query.test +25 -0
- data/test/command/suite/search/condition-script.expected +28 -0
- data/test/command/suite/search/condition-script.test +28 -0
- data/test/command/suite/search/hash-attribute-label.expected +34 -0
- data/test/command/suite/search/hash-attribute-label.test +38 -0
- data/test/command/suite/search/minimum.expected +13 -0
- data/test/command/suite/search/minimum.test +16 -0
- data/test/command/suite/search/multiple-queries.expected +39 -0
- data/test/command/suite/search/multiple-queries.test +39 -0
- data/test/command/suite/search/output-range.expected +28 -0
- data/test/command/suite/search/output-range.test +25 -0
- data/test/command/suite/search/simple.expected +52 -0
- data/test/command/suite/search/simple.test +24 -0
- data/test/command/suite/search/sort-and-output-range.expected +25 -0
- data/test/command/suite/search/sort-and-output-range.test +29 -0
- data/test/command/suite/search/sort-range.expected +28 -0
- data/test/command/suite/search/sort-range.test +28 -0
- data/test/command/suite/search/sort-with-invisible-column.expected +28 -0
- data/test/command/suite/search/sort-with-invisible-column.test +28 -0
- data/test/unit/fixtures/array.grn +18 -0
- data/test/{fixtures → unit/fixtures}/catalog.json +0 -0
- data/test/{fixtures → unit/fixtures}/document.grn +20 -9
- data/test/unit/fixtures/reference/array.grn +11 -0
- data/test/unit/fixtures/reference/hash.grn +7 -0
- data/test/{helper.rb → unit/helper.rb} +2 -1
- data/test/{helper → unit/helper}/fixture.rb +1 -1
- data/test/unit/helper/plugin_helper.rb +38 -0
- data/test/{helper → unit/helper}/sandbox.rb +19 -6
- data/test/{helper → unit/helper}/stub_worker.rb +1 -1
- data/test/{helper → unit/helper}/watch_helper.rb +1 -13
- data/test/{plugin → unit/plugin}/adapter/groonga/test_select.rb +108 -4
- data/test/unit/plugin/collector/test_basic.rb +558 -0
- data/test/unit/plugin/distributor/test_search.rb +914 -0
- data/test/{plugin → unit/plugin}/handler/groonga/test_column_create.rb +18 -14
- data/test/{plugin → unit/plugin}/handler/groonga/test_table_create.rb +13 -11
- data/test/{plugin/handler/test_handler_add.rb → unit/plugin/handler/test_add.rb} +2 -14
- data/test/{plugin/handler/test_handler_groonga.rb → unit/plugin/handler/test_groonga.rb} +6 -26
- data/test/unit/plugin/handler/test_search.rb +601 -0
- data/test/{plugin/handler/test_handler_watch.rb → unit/plugin/handler/test_watch.rb} +2 -2
- data/test/{run-test.rb → unit/run-test.rb} +3 -3
- data/test/{test_adapter.rb → unit/test_adapter.rb} +17 -14
- data/test/{test_catalog.rb → unit/test_catalog.rb} +4 -4
- data/test/{test_command_mapper.rb → unit/test_command_mapper.rb} +1 -1
- data/test/{test_groonga_command_converter.rb → unit/test_groonga_command_converter.rb} +3 -3
- data/test/{test_job_queue_schema.rb → unit/test_job_queue_schema.rb} +1 -1
- data/test/{test_output.rb → unit/test_output.rb} +9 -9
- data/test/{test_handler.rb → unit/test_plugin.rb} +19 -22
- data/test/unit/test_plugin_repository.rb +89 -0
- data/test/{test_sweeper.rb → unit/test_sweeper.rb} +1 -1
- data/test/{test_watch_schema.rb → unit/test_watch_schema.rb} +1 -1
- data/test/{test_watcher.rb → unit/test_watcher.rb} +1 -1
- metadata +226 -66
- data/lib/droonga/executor.rb +0 -289
- data/lib/droonga/plugin/handler_proxy.rb +0 -82
- data/test/plugin/handler/test_handler_search.rb +0 -512
- data/test/test_worker.rb +0 -144
data/lib/droonga/executor.rb
DELETED
@@ -1,289 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
# Copyright (C) 2013 droonga project
|
4
|
-
#
|
5
|
-
# This library is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU Lesser General Public
|
7
|
-
# License version 2.1 as published by the Free Software Foundation.
|
8
|
-
#
|
9
|
-
# This library is distributed in the hope that it will be useful,
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
-
|
18
|
-
require "fluent-logger"
|
19
|
-
require "groonga"
|
20
|
-
|
21
|
-
require "droonga/job_queue"
|
22
|
-
require "droonga/handler_plugin"
|
23
|
-
require "droonga/plugin"
|
24
|
-
require "droonga/proxy"
|
25
|
-
|
26
|
-
module Droonga
|
27
|
-
class Executor
|
28
|
-
attr_reader :context, :envelope, :name
|
29
|
-
|
30
|
-
def initialize(options={})
|
31
|
-
@handlers = []
|
32
|
-
@outputs = {}
|
33
|
-
@options = options
|
34
|
-
@name = options[:name]
|
35
|
-
@database_name = options[:database]
|
36
|
-
@queue_name = options[:queue_name]
|
37
|
-
@handler_names = options[:handlers]
|
38
|
-
@pool_size = options[:n_workers]
|
39
|
-
# load_handlers
|
40
|
-
Droonga::Plugin.load_all
|
41
|
-
prepare
|
42
|
-
end
|
43
|
-
|
44
|
-
def shutdown
|
45
|
-
$log.trace("#{log_tag}: shutdown: start")
|
46
|
-
@handlers.each do |handler|
|
47
|
-
handler.shutdown
|
48
|
-
end
|
49
|
-
@outputs.each do |dest, output|
|
50
|
-
output[:logger].close if output[:logger]
|
51
|
-
end
|
52
|
-
if @database
|
53
|
-
@database.close
|
54
|
-
@context.close
|
55
|
-
@database = @context = nil
|
56
|
-
end
|
57
|
-
if @job_queue
|
58
|
-
@job_queue.close
|
59
|
-
@job_queue = nil
|
60
|
-
end
|
61
|
-
$log.trace("#{log_tag}: shutdown: done")
|
62
|
-
end
|
63
|
-
|
64
|
-
def add_handler(name)
|
65
|
-
plugin = HandlerPlugin.new(name)
|
66
|
-
@handlers << plugin.instantiate(self)
|
67
|
-
end
|
68
|
-
|
69
|
-
def add_route(route)
|
70
|
-
envelope["via"].push(route)
|
71
|
-
end
|
72
|
-
|
73
|
-
def dispatch(tag, time, record, synchronous=nil)
|
74
|
-
$log.trace("#{log_tag}: dispatch: start")
|
75
|
-
message = [tag, time, record]
|
76
|
-
body, type, arguments = parse_message([tag, time, record])
|
77
|
-
reply_to = envelope["replyTo"]
|
78
|
-
if reply_to.is_a? String
|
79
|
-
envelope["replyTo"] = {
|
80
|
-
"type" => type + ".result",
|
81
|
-
"to" => reply_to
|
82
|
-
}
|
83
|
-
end
|
84
|
-
post_or_push(message, body,
|
85
|
-
"type" => type,
|
86
|
-
"arguments" => arguments,
|
87
|
-
"synchronous" => synchronous)
|
88
|
-
$log.trace("#{log_tag}: dispatch: done")
|
89
|
-
end
|
90
|
-
|
91
|
-
def execute_one
|
92
|
-
$log.trace("#{log_tag}: execute_one: start")
|
93
|
-
message = @job_queue.pull_message
|
94
|
-
unless message
|
95
|
-
$log.trace("#{log_tag}: execute_one: abort: no message")
|
96
|
-
return
|
97
|
-
end
|
98
|
-
body, command, arguments = parse_message(message)
|
99
|
-
handler = find_handler(command)
|
100
|
-
if handler
|
101
|
-
$log.trace("#{log_tag}: execute_one: handle: start",
|
102
|
-
:hander => handler.class)
|
103
|
-
handler.handle(command, body, *arguments)
|
104
|
-
$log.trace("#{log_tag}: execute_one: handle: done",
|
105
|
-
:hander => handler.class)
|
106
|
-
end
|
107
|
-
$log.trace("#{log_tag}: execute_one: done")
|
108
|
-
end
|
109
|
-
|
110
|
-
def post(body, destination=nil)
|
111
|
-
$log.trace("#{log_tag}: post: start")
|
112
|
-
post_or_push(nil, body, destination)
|
113
|
-
$log.trace("#{log_tag}: post: done")
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
def post_or_push(message, body, destination)
|
118
|
-
route = nil
|
119
|
-
unless is_route?(destination)
|
120
|
-
route = envelope["via"].pop
|
121
|
-
destination = route
|
122
|
-
end
|
123
|
-
unless is_route?(destination)
|
124
|
-
destination = envelope["replyTo"]
|
125
|
-
end
|
126
|
-
command = nil
|
127
|
-
receiver = nil
|
128
|
-
arguments = nil
|
129
|
-
synchronous = nil
|
130
|
-
case destination
|
131
|
-
when String
|
132
|
-
command = destination
|
133
|
-
when Hash
|
134
|
-
command = destination["type"]
|
135
|
-
receiver = destination["to"]
|
136
|
-
arguments = destination["arguments"]
|
137
|
-
synchronous = destination["synchronous"]
|
138
|
-
end
|
139
|
-
if receiver
|
140
|
-
output(receiver, body, command, arguments)
|
141
|
-
else
|
142
|
-
handler = find_handler(command)
|
143
|
-
if handler
|
144
|
-
if synchronous.nil?
|
145
|
-
synchronous = handler.prefer_synchronous?(command)
|
146
|
-
end
|
147
|
-
if route || @pool_size.zero? || synchronous
|
148
|
-
$log.trace("#{log_tag}: post_or_push: handle: start")
|
149
|
-
handler.handle(command, body, *arguments)
|
150
|
-
$log.trace("#{log_tag}: post_or_push: handle: done")
|
151
|
-
else
|
152
|
-
unless message
|
153
|
-
envelope["body"] = body
|
154
|
-
envelope["type"] = command
|
155
|
-
envelope["arguments"] = arguments
|
156
|
-
message = ['', Time.now.to_f, envelope]
|
157
|
-
end
|
158
|
-
@job_queue.push_message(message)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
add_route(route) if route
|
163
|
-
end
|
164
|
-
|
165
|
-
def is_route?(route)
|
166
|
-
route.is_a?(String) || route.is_a?(Hash)
|
167
|
-
end
|
168
|
-
|
169
|
-
def output(receiver, body, command, arguments)
|
170
|
-
$log.trace("#{log_tag}: output: start")
|
171
|
-
unless receiver.is_a?(String) && command.is_a?(String)
|
172
|
-
$log.trace("#{log_tag}: output: abort: invalid argument",
|
173
|
-
:receiver => receiver,
|
174
|
-
:command => command)
|
175
|
-
return
|
176
|
-
end
|
177
|
-
unless receiver =~ /\A(.*):(\d+)\/(.*?)(\?.+)?\z/
|
178
|
-
raise "format: hostname:port/tag(?params)"
|
179
|
-
end
|
180
|
-
host = $1
|
181
|
-
port = $2
|
182
|
-
tag = $3
|
183
|
-
params = $4
|
184
|
-
output = get_output(host, port, params)
|
185
|
-
unless output
|
186
|
-
$log.trace("#{log_tag}: output: abort: no output",
|
187
|
-
:host => host,
|
188
|
-
:port => port,
|
189
|
-
:params => params)
|
190
|
-
return
|
191
|
-
end
|
192
|
-
if command =~ /\.result$/
|
193
|
-
message = {
|
194
|
-
inReplyTo: envelope["id"],
|
195
|
-
statusCode: 200,
|
196
|
-
type: command,
|
197
|
-
body: body
|
198
|
-
}
|
199
|
-
else
|
200
|
-
message = envelope.merge(
|
201
|
-
body: body,
|
202
|
-
type: command,
|
203
|
-
arguments: arguments
|
204
|
-
)
|
205
|
-
end
|
206
|
-
output_tag = "#{tag}.message"
|
207
|
-
$log.trace("#{log_tag}: output: post: start: <#{output_tag}>")
|
208
|
-
output.post(output_tag, message)
|
209
|
-
$log.trace("#{log_tag}: output: post: done: <#{output_tag}>")
|
210
|
-
$log.trace("#{log_tag}: output: done")
|
211
|
-
end
|
212
|
-
|
213
|
-
def parse_message(message)
|
214
|
-
tag, time, record = message
|
215
|
-
prefix, type, *arguments = tag.split(/\./)
|
216
|
-
if type.nil? || type.empty? || type == 'message'
|
217
|
-
@envelope = record
|
218
|
-
else
|
219
|
-
@envelope = {
|
220
|
-
"type" => type,
|
221
|
-
"arguments" => arguments,
|
222
|
-
"body" => record
|
223
|
-
}
|
224
|
-
end
|
225
|
-
envelope["via"] ||= []
|
226
|
-
[envelope["body"], envelope["type"], envelope["arguments"]]
|
227
|
-
end
|
228
|
-
|
229
|
-
def load_handlers
|
230
|
-
@handler_names.each do |handler_name|
|
231
|
-
plugin = Droonga::Plugin.new("handler", handler_name)
|
232
|
-
plugin.load
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def prepare
|
237
|
-
if @database_name && !@database_name.empty?
|
238
|
-
@context = Groonga::Context.new
|
239
|
-
@database = @context.open_database(@database_name)
|
240
|
-
@job_queue = JobQueue.open(@database_name, @queue_name)
|
241
|
-
end
|
242
|
-
@handler_names.each do |handler_name|
|
243
|
-
add_handler(handler_name)
|
244
|
-
end
|
245
|
-
add_handler("proxy_message") if @options[:proxy]
|
246
|
-
end
|
247
|
-
|
248
|
-
def find_handler(command)
|
249
|
-
@handlers.find do |handler|
|
250
|
-
handler.handlable?(command)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
def get_output(host, port, params)
|
255
|
-
host_port = "#{host}:#{port}"
|
256
|
-
@outputs[host_port] ||= {}
|
257
|
-
output = @outputs[host_port]
|
258
|
-
|
259
|
-
has_connection_id = (not params.nil? \
|
260
|
-
and params =~ /[\?&;]connection_id=([^&;]+)/)
|
261
|
-
if output[:logger].nil? or has_connection_id
|
262
|
-
connection_id = $1
|
263
|
-
if not has_connection_id or output[:connection_id] != connection_id
|
264
|
-
output[:connection_id] = connection_id
|
265
|
-
logger = create_logger(:host => host, :port => port.to_i)
|
266
|
-
# output[:logger] should be closed if it exists beforehand?
|
267
|
-
output[:logger] = logger
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
has_client_session_id = (not params.nil? \
|
272
|
-
and params =~ /[\?&;]client_session_id=([^&;]+)/)
|
273
|
-
if has_client_session_id
|
274
|
-
client_session_id = $1
|
275
|
-
# some generic way to handle client_session_id is expected
|
276
|
-
end
|
277
|
-
|
278
|
-
output[:logger]
|
279
|
-
end
|
280
|
-
|
281
|
-
def create_logger(options)
|
282
|
-
Fluent::Logger::FluentLogger.new(nil, options)
|
283
|
-
end
|
284
|
-
|
285
|
-
def log_tag
|
286
|
-
"[#{Process.ppid}][#{Process.pid}] executor"
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
# Copyright (C) 2013 droonga project
|
4
|
-
#
|
5
|
-
# This library is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU Lesser General Public
|
7
|
-
# License version 2.1 as published by the Free Software Foundation.
|
8
|
-
#
|
9
|
-
# This library is distributed in the hope that it will be useful,
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
-
|
18
|
-
require "droonga/handler"
|
19
|
-
require "droonga/searcher"
|
20
|
-
|
21
|
-
module Droonga
|
22
|
-
class BasicProxyHandler < Droonga::ProxyHandler
|
23
|
-
Droonga::HandlerPlugin.register("proxy", self)
|
24
|
-
|
25
|
-
command :proxy_gather
|
26
|
-
def proxy_gather(request)
|
27
|
-
output = body ? body[input_name] : input_name
|
28
|
-
emit(request, output)
|
29
|
-
end
|
30
|
-
|
31
|
-
command :proxy_reduce
|
32
|
-
def proxy_reduce(request)
|
33
|
-
return unless request
|
34
|
-
body[input_name].each do |output, elements|
|
35
|
-
value = request
|
36
|
-
old_value = output_values[output]
|
37
|
-
value = reduce(elements, old_value, request) if old_value
|
38
|
-
emit(value, output)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def reduce(elements, *values)
|
43
|
-
result = {}
|
44
|
-
elements.each do |key, deal|
|
45
|
-
func, *args = deal
|
46
|
-
case func
|
47
|
-
when "sum"
|
48
|
-
result[key] = values[0][key] + values[1][key]
|
49
|
-
when "sort"
|
50
|
-
result[key] = merge(values[0][key], values[1][key], args)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
return result
|
54
|
-
end
|
55
|
-
|
56
|
-
def merge(x, y, order)
|
57
|
-
index = 0
|
58
|
-
y.each do |_y|
|
59
|
-
loop do
|
60
|
-
_x = x[index]
|
61
|
-
break unless _x
|
62
|
-
break if compare(_y, _x, order)
|
63
|
-
index += 1
|
64
|
-
end
|
65
|
-
x.insert(index, _y)
|
66
|
-
index += 1
|
67
|
-
end
|
68
|
-
return x
|
69
|
-
end
|
70
|
-
|
71
|
-
def compare(x, y, operators)
|
72
|
-
for index in 0..x.size-1 do
|
73
|
-
_x = x[index]
|
74
|
-
_y = y[index]
|
75
|
-
operator = operators[index]
|
76
|
-
break unless operator
|
77
|
-
return true if _x.__send__(operator, _y)
|
78
|
-
end
|
79
|
-
return false
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,512 +0,0 @@
|
|
1
|
-
# Copyright (C) 2013 droonga project
|
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 version 2.1 as published by the Free Software Foundation.
|
6
|
-
#
|
7
|
-
# This library is distributed in the hope that it will be useful,
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
10
|
-
# Lesser General Public License for more details.
|
11
|
-
#
|
12
|
-
# You should have received a copy of the GNU Lesser General Public
|
13
|
-
# License along with this library; if not, write to the Free Software
|
14
|
-
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
15
|
-
|
16
|
-
require "droonga/plugin/handler_search"
|
17
|
-
|
18
|
-
class SearchHandlerTest < Test::Unit::TestCase
|
19
|
-
def setup
|
20
|
-
setup_database
|
21
|
-
setup_handler
|
22
|
-
end
|
23
|
-
|
24
|
-
def teardown
|
25
|
-
teardown_handler
|
26
|
-
teardown_database
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
def setup_database
|
31
|
-
restore(fixture_data("document.grn"))
|
32
|
-
@database = Groonga::Database.open(@database_path.to_s)
|
33
|
-
end
|
34
|
-
|
35
|
-
def teardown_database
|
36
|
-
@database.close
|
37
|
-
@database = nil
|
38
|
-
end
|
39
|
-
|
40
|
-
def setup_handler
|
41
|
-
@worker = StubWorker.new
|
42
|
-
@handler = Droonga::SearchHandler.new(@worker)
|
43
|
-
end
|
44
|
-
|
45
|
-
def teardown_handler
|
46
|
-
@handler = nil
|
47
|
-
end
|
48
|
-
|
49
|
-
def search(request)
|
50
|
-
@handler.search(request)
|
51
|
-
normalize_result_set(@worker.body)
|
52
|
-
end
|
53
|
-
|
54
|
-
def normalize_result_set(result_set)
|
55
|
-
result_set.each do |name, result|
|
56
|
-
result["startTime"] = start_time if result["startTime"]
|
57
|
-
result["elapsedTime"] = elapsed_time if result["elapsedTime"]
|
58
|
-
end
|
59
|
-
result_set
|
60
|
-
end
|
61
|
-
|
62
|
-
def start_time
|
63
|
-
"2013-01-31T14:34:47+09:00"
|
64
|
-
end
|
65
|
-
|
66
|
-
def elapsed_time
|
67
|
-
0.01
|
68
|
-
end
|
69
|
-
|
70
|
-
def assert_search(expected, request)
|
71
|
-
assert_equal(expected, search(request))
|
72
|
-
end
|
73
|
-
|
74
|
-
class NoParameterTest < self
|
75
|
-
def test_empty
|
76
|
-
assert_search({}, {})
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
class QueriesTest < self
|
81
|
-
def test_empty
|
82
|
-
assert_search({}, {"queries" => {}})
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
class HashQueryTest < self
|
87
|
-
def test_string_matchTo
|
88
|
-
request = base_request
|
89
|
-
request["queries"]["sections-result"]["condition"] = {
|
90
|
-
"query" => "Groonga",
|
91
|
-
"matchTo" => "title"
|
92
|
-
}
|
93
|
-
assert_search({
|
94
|
-
"sections-result" => {
|
95
|
-
"records" => [
|
96
|
-
{ "title" => "Groonga overview" },
|
97
|
-
],
|
98
|
-
},
|
99
|
-
},
|
100
|
-
request)
|
101
|
-
end
|
102
|
-
|
103
|
-
def test_array_matchTo
|
104
|
-
request = base_request
|
105
|
-
request["queries"]["sections-result"]["condition"] = {
|
106
|
-
"query" => "Groonga",
|
107
|
-
"matchTo" => ["title"]
|
108
|
-
}
|
109
|
-
assert_search({
|
110
|
-
"sections-result" => {
|
111
|
-
"records" => [
|
112
|
-
{ "title" => "Groonga overview" },
|
113
|
-
],
|
114
|
-
},
|
115
|
-
},
|
116
|
-
request)
|
117
|
-
end
|
118
|
-
|
119
|
-
def base_request
|
120
|
-
{
|
121
|
-
"queries" => {
|
122
|
-
"sections-result" => {
|
123
|
-
"source" => "Sections",
|
124
|
-
"output" => {
|
125
|
-
"elements" => [
|
126
|
-
"records",
|
127
|
-
],
|
128
|
-
"format" => "complex",
|
129
|
-
"limit" => 1,
|
130
|
-
"attributes" => ["title"],
|
131
|
-
},
|
132
|
-
},
|
133
|
-
},
|
134
|
-
}
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class SourceTest < self
|
139
|
-
def test_non_existent
|
140
|
-
assert_raise(Droonga::Searcher::UndefinedSourceError) do
|
141
|
-
search({
|
142
|
-
"queries" => {
|
143
|
-
"non-existent-result" => {
|
144
|
-
"source" => "non-existent",
|
145
|
-
},
|
146
|
-
},
|
147
|
-
})
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_existent
|
152
|
-
assert_search({
|
153
|
-
"sections-result" => {},
|
154
|
-
},
|
155
|
-
{
|
156
|
-
"queries" => {
|
157
|
-
"sections-result" => {
|
158
|
-
"source" => "Sections",
|
159
|
-
"output" => {},
|
160
|
-
},
|
161
|
-
},
|
162
|
-
})
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
class OutputTest < self
|
167
|
-
def test_count
|
168
|
-
assert_search({
|
169
|
-
"sections-result" => {
|
170
|
-
"count" => 9,
|
171
|
-
},
|
172
|
-
},
|
173
|
-
{
|
174
|
-
"queries" => {
|
175
|
-
"sections-result" => {
|
176
|
-
"source" => "Sections",
|
177
|
-
"output" => {
|
178
|
-
"elements" => [
|
179
|
-
"count",
|
180
|
-
],
|
181
|
-
},
|
182
|
-
},
|
183
|
-
},
|
184
|
-
})
|
185
|
-
end
|
186
|
-
|
187
|
-
def test_elapsed_time
|
188
|
-
assert_search({
|
189
|
-
"sections-result" => {
|
190
|
-
"startTime" => start_time,
|
191
|
-
"elapsedTime" => elapsed_time,
|
192
|
-
},
|
193
|
-
},
|
194
|
-
{
|
195
|
-
"queries" => {
|
196
|
-
"sections-result" => {
|
197
|
-
"source" => "Sections",
|
198
|
-
"output" => {
|
199
|
-
"elements" => [
|
200
|
-
"startTime",
|
201
|
-
"elapsedTime",
|
202
|
-
],
|
203
|
-
},
|
204
|
-
},
|
205
|
-
},
|
206
|
-
})
|
207
|
-
end
|
208
|
-
|
209
|
-
class AttributesTest < self
|
210
|
-
def test_source_only
|
211
|
-
expected = {
|
212
|
-
"sections-result" => {
|
213
|
-
"records" => [
|
214
|
-
{
|
215
|
-
"_key" => "1.1",
|
216
|
-
"title" => "Groonga overview",
|
217
|
-
},
|
218
|
-
{
|
219
|
-
"_key" => "1.2",
|
220
|
-
"title" => "Full text search and Instant update",
|
221
|
-
},
|
222
|
-
{
|
223
|
-
"_key" => "1.3",
|
224
|
-
"title" => "Column store and aggregate query",
|
225
|
-
},
|
226
|
-
],
|
227
|
-
},
|
228
|
-
}
|
229
|
-
request = {
|
230
|
-
"queries" => {
|
231
|
-
"sections-result" => {
|
232
|
-
"source" => "Sections",
|
233
|
-
"output" => {
|
234
|
-
"elements" => [
|
235
|
-
"records",
|
236
|
-
],
|
237
|
-
"format" => "complex",
|
238
|
-
"limit" => 3,
|
239
|
-
"attributes" => ["_key", "title"],
|
240
|
-
},
|
241
|
-
},
|
242
|
-
},
|
243
|
-
}
|
244
|
-
assert_search(expected, request)
|
245
|
-
end
|
246
|
-
|
247
|
-
def test_label
|
248
|
-
expected = {
|
249
|
-
"sections-result" => {
|
250
|
-
"records" => [
|
251
|
-
{
|
252
|
-
"key" => "1.1",
|
253
|
-
"title" => "Groonga overview",
|
254
|
-
},
|
255
|
-
{
|
256
|
-
"key" => "1.2",
|
257
|
-
"title" => "Full text search and Instant update",
|
258
|
-
},
|
259
|
-
{
|
260
|
-
"key" => "1.3",
|
261
|
-
"title" => "Column store and aggregate query",
|
262
|
-
},
|
263
|
-
],
|
264
|
-
},
|
265
|
-
}
|
266
|
-
request = {
|
267
|
-
"queries" => {
|
268
|
-
"sections-result" => {
|
269
|
-
"source" => "Sections",
|
270
|
-
"output" => {
|
271
|
-
"elements" => [
|
272
|
-
"records",
|
273
|
-
],
|
274
|
-
"format" => "complex",
|
275
|
-
"limit" => 3,
|
276
|
-
"attributes" => [
|
277
|
-
{
|
278
|
-
"label" => "key",
|
279
|
-
"source" => "_key",
|
280
|
-
},
|
281
|
-
"title",
|
282
|
-
],
|
283
|
-
},
|
284
|
-
},
|
285
|
-
},
|
286
|
-
}
|
287
|
-
assert_search(expected, request)
|
288
|
-
end
|
289
|
-
|
290
|
-
def test_static_value
|
291
|
-
expected = {
|
292
|
-
"sections-result" => {
|
293
|
-
"records" => [
|
294
|
-
{
|
295
|
-
"single_quote_string" => "string value",
|
296
|
-
"double_quote_string" => "string value",
|
297
|
-
"integer" => 29,
|
298
|
-
"complex_negative_number" => -29.29,
|
299
|
-
},
|
300
|
-
{
|
301
|
-
"single_quote_string" => "string value",
|
302
|
-
"double_quote_string" => "string value",
|
303
|
-
"integer" => 29,
|
304
|
-
"complex_negative_number" => -29.29,
|
305
|
-
},
|
306
|
-
],
|
307
|
-
},
|
308
|
-
}
|
309
|
-
request = {
|
310
|
-
"queries" => {
|
311
|
-
"sections-result" => {
|
312
|
-
"source" => "Sections",
|
313
|
-
"output" => {
|
314
|
-
"elements" => [
|
315
|
-
"records",
|
316
|
-
],
|
317
|
-
"format" => "complex",
|
318
|
-
"limit" => 2,
|
319
|
-
"attributes" => [
|
320
|
-
{
|
321
|
-
"label" => "single_quote_string",
|
322
|
-
"source" => "'string value'",
|
323
|
-
},
|
324
|
-
{
|
325
|
-
"label" => "double_quote_string",
|
326
|
-
"source" => '"string value"',
|
327
|
-
},
|
328
|
-
{
|
329
|
-
"label" => "integer",
|
330
|
-
"source" => "29",
|
331
|
-
},
|
332
|
-
{
|
333
|
-
"label" => "complex_negative_number",
|
334
|
-
"source" => "-29.29",
|
335
|
-
},
|
336
|
-
],
|
337
|
-
},
|
338
|
-
},
|
339
|
-
},
|
340
|
-
}
|
341
|
-
assert_search(expected, request)
|
342
|
-
end
|
343
|
-
|
344
|
-
def test_expression
|
345
|
-
expected = {
|
346
|
-
"sections-result" => {
|
347
|
-
"records" => [
|
348
|
-
{
|
349
|
-
"formatted title" => "<Groonga overview>",
|
350
|
-
"title" => "Groonga overview",
|
351
|
-
},
|
352
|
-
],
|
353
|
-
},
|
354
|
-
}
|
355
|
-
request = {
|
356
|
-
"queries" => {
|
357
|
-
"sections-result" => {
|
358
|
-
"source" => "Sections",
|
359
|
-
"output" => {
|
360
|
-
"elements" => [
|
361
|
-
"records",
|
362
|
-
],
|
363
|
-
"format" => "complex",
|
364
|
-
"limit" => 1,
|
365
|
-
"attributes" => [
|
366
|
-
"title",
|
367
|
-
{
|
368
|
-
"label" => "formatted title",
|
369
|
-
"source" => "'<' + title + '>'",
|
370
|
-
},
|
371
|
-
],
|
372
|
-
},
|
373
|
-
},
|
374
|
-
},
|
375
|
-
}
|
376
|
-
assert_search(expected, request)
|
377
|
-
end
|
378
|
-
|
379
|
-
def test_snippet_html
|
380
|
-
expected = {
|
381
|
-
"sections-result" => {
|
382
|
-
"records" => [
|
383
|
-
{
|
384
|
-
"title" => "Groonga overview",
|
385
|
-
"snippet" => [
|
386
|
-
"<span class=\"keyword\">Groonga</span> overview",
|
387
|
-
],
|
388
|
-
},
|
389
|
-
],
|
390
|
-
},
|
391
|
-
}
|
392
|
-
request = {
|
393
|
-
"queries" => {
|
394
|
-
"sections-result" => {
|
395
|
-
"source" => "Sections",
|
396
|
-
"condition" => {
|
397
|
-
"query" => "Groonga",
|
398
|
-
"matchTo" => ["title"],
|
399
|
-
},
|
400
|
-
"output" => {
|
401
|
-
"elements" => [
|
402
|
-
"records",
|
403
|
-
],
|
404
|
-
"format" => "complex",
|
405
|
-
"limit" => 1,
|
406
|
-
"attributes" => [
|
407
|
-
"title",
|
408
|
-
{
|
409
|
-
"label" => "snippet",
|
410
|
-
"source" => "snippet_html(title)",
|
411
|
-
},
|
412
|
-
],
|
413
|
-
},
|
414
|
-
},
|
415
|
-
},
|
416
|
-
}
|
417
|
-
assert_search(expected, request)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
class FormatTest < self
|
422
|
-
def test_complex
|
423
|
-
request = {
|
424
|
-
"queries" => {
|
425
|
-
"sections-result" => {
|
426
|
-
"source" => "Sections",
|
427
|
-
"output" => {
|
428
|
-
"elements" => [
|
429
|
-
"records",
|
430
|
-
],
|
431
|
-
"format" => "complex",
|
432
|
-
"limit" => 3,
|
433
|
-
"attributes" => ["_key", "title"],
|
434
|
-
},
|
435
|
-
},
|
436
|
-
},
|
437
|
-
}
|
438
|
-
assert_search(complex_result, request)
|
439
|
-
end
|
440
|
-
|
441
|
-
def test_simple
|
442
|
-
request = {
|
443
|
-
"queries" => {
|
444
|
-
"sections-result" => {
|
445
|
-
"source" => "Sections",
|
446
|
-
"output" => {
|
447
|
-
"elements" => [
|
448
|
-
"records",
|
449
|
-
],
|
450
|
-
"format" => "simple",
|
451
|
-
"limit" => 3,
|
452
|
-
"attributes" => ["_key", "title"],
|
453
|
-
},
|
454
|
-
},
|
455
|
-
},
|
456
|
-
}
|
457
|
-
assert_search(simple_result, request)
|
458
|
-
end
|
459
|
-
|
460
|
-
def test_default
|
461
|
-
request = {
|
462
|
-
"queries" => {
|
463
|
-
"sections-result" => {
|
464
|
-
"source" => "Sections",
|
465
|
-
"output" => {
|
466
|
-
"elements" => [
|
467
|
-
"records",
|
468
|
-
],
|
469
|
-
"limit" => 3,
|
470
|
-
"attributes" => ["_key", "title"],
|
471
|
-
},
|
472
|
-
},
|
473
|
-
},
|
474
|
-
}
|
475
|
-
assert_search(simple_result, request)
|
476
|
-
end
|
477
|
-
|
478
|
-
def complex_result
|
479
|
-
{
|
480
|
-
"sections-result" => {
|
481
|
-
"records" => [
|
482
|
-
{
|
483
|
-
"_key" => "1.1",
|
484
|
-
"title" => "Groonga overview",
|
485
|
-
},
|
486
|
-
{
|
487
|
-
"_key" => "1.2",
|
488
|
-
"title" => "Full text search and Instant update",
|
489
|
-
},
|
490
|
-
{
|
491
|
-
"_key" => "1.3",
|
492
|
-
"title" => "Column store and aggregate query",
|
493
|
-
},
|
494
|
-
],
|
495
|
-
},
|
496
|
-
}
|
497
|
-
end
|
498
|
-
|
499
|
-
def simple_result
|
500
|
-
{
|
501
|
-
"sections-result" => {
|
502
|
-
"records" => [
|
503
|
-
["1.1", "Groonga overview"],
|
504
|
-
["1.2", "Full text search and Instant update"],
|
505
|
-
["1.3", "Column store and aggregate query"],
|
506
|
-
],
|
507
|
-
},
|
508
|
-
}
|
509
|
-
end
|
510
|
-
end
|
511
|
-
end
|
512
|
-
end
|