fluent-plugin-droonga 0.0.2 → 0.7.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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.yardopts +7 -0
  4. data/Gemfile +14 -2
  5. data/LICENSE.txt +1 -1
  6. data/README.md +1 -1
  7. data/Rakefile +27 -5
  8. data/benchmark/benchmark.rb +1 -1
  9. data/benchmark/utils.rb +9 -6
  10. data/benchmark/watch/benchmark-notify.rb +2 -2
  11. data/benchmark/watch/benchmark-publish.rb +1 -1
  12. data/benchmark/watch/benchmark-scan.rb +1 -1
  13. data/benchmark/watch/catalog.json +1 -1
  14. data/bin/grn2jsons +1 -1
  15. data/fluent-plugin-droonga.gemspec +5 -3
  16. data/lib/droonga/adapter.rb +13 -130
  17. data/lib/droonga/adapter_plugin.rb +51 -0
  18. data/lib/droonga/catalog.rb +2 -2
  19. data/lib/droonga/collector.rb +107 -0
  20. data/lib/droonga/collector_plugin.rb +82 -0
  21. data/lib/droonga/command_mapper.rb +1 -1
  22. data/lib/droonga/{proxy.rb → dispatcher.rb} +116 -151
  23. data/lib/droonga/distributor.rb +51 -0
  24. data/lib/droonga/distributor_plugin.rb +59 -0
  25. data/lib/droonga/engine.rb +9 -50
  26. data/lib/droonga/farm.rb +47 -0
  27. data/lib/droonga/forwarder.rb +125 -0
  28. data/lib/droonga/handler.rb +69 -60
  29. data/lib/droonga/handler_plugin.rb +22 -11
  30. data/lib/droonga/input_message.rb +51 -0
  31. data/lib/droonga/job_queue.rb +5 -1
  32. data/lib/droonga/job_queue_schema.rb +1 -1
  33. data/lib/droonga/logger.rb +1 -1
  34. data/lib/droonga/partition.rb +76 -0
  35. data/lib/droonga/pluggable.rb +62 -0
  36. data/lib/droonga/plugin.rb +18 -16
  37. data/lib/droonga/plugin/{adapter_groonga.rb → adapter/groonga.rb} +10 -10
  38. data/lib/droonga/plugin/adapter/groonga/select.rb +13 -4
  39. data/lib/droonga/plugin/collector/basic.rb +142 -0
  40. data/lib/droonga/plugin/distributor/crud.rb +43 -0
  41. data/lib/droonga/plugin/distributor/groonga.rb +37 -0
  42. data/lib/droonga/plugin/distributor/search.rb +273 -0
  43. data/lib/droonga/plugin/distributor/watch.rb +39 -0
  44. data/lib/droonga/plugin/{handler_add.rb → handler/add.rb} +6 -6
  45. data/lib/droonga/plugin/{handler_forward.rb → handler/forward.rb} +9 -4
  46. data/lib/droonga/plugin/{handler_groonga.rb → handler/groonga.rb} +36 -4
  47. data/lib/droonga/plugin/handler/groonga/column_create.rb +5 -9
  48. data/lib/droonga/plugin/handler/groonga/table_create.rb +9 -18
  49. data/lib/droonga/plugin/{handler_search.rb → handler/search.rb} +4 -4
  50. data/lib/droonga/plugin/{handler_watch.rb → handler/watch.rb} +4 -4
  51. data/lib/droonga/plugin_loader.rb +45 -0
  52. data/lib/droonga/plugin_registerable.rb +51 -0
  53. data/lib/droonga/plugin_repository.rb +56 -0
  54. data/lib/droonga/processor.rb +64 -0
  55. data/lib/droonga/searcher.rb +16 -7
  56. data/lib/droonga/server.rb +5 -9
  57. data/lib/droonga/sweeper.rb +1 -1
  58. data/lib/droonga/watch_schema.rb +1 -1
  59. data/lib/droonga/watcher.rb +1 -1
  60. data/lib/droonga/worker.rb +21 -9
  61. data/lib/fluent/plugin/out_droonga.rb +33 -15
  62. data/lib/groonga_command_converter.rb +1 -1
  63. data/sample/cluster/fluentd.conf +0 -1
  64. data/test/command/config/default/catalog.json +43 -0
  65. data/test/command/config/default/fluentd.conf +11 -0
  66. data/test/command/fixture/documents.jsons +208 -0
  67. data/test/command/fixture/user-table-array.jsons +38 -0
  68. data/test/command/fixture/user-table.jsons +38 -0
  69. data/test/command/run-test.rb +35 -0
  70. data/test/command/suite/add/minimum.expected +12 -0
  71. data/test/command/suite/add/minimum.test +11 -0
  72. data/test/command/suite/add/with-values.expected +12 -0
  73. data/test/command/suite/add/with-values.test +17 -0
  74. data/test/command/suite/add/without-key.expected +12 -0
  75. data/test/command/suite/add/without-key.test +16 -0
  76. data/test/command/suite/groonga/column_create/scalar.expected +34 -0
  77. data/test/command/suite/groonga/column_create/scalar.test +17 -0
  78. data/test/command/suite/groonga/column_create/vector.expected +34 -0
  79. data/test/command/suite/groonga/column_create/vector.test +18 -0
  80. data/test/command/suite/groonga/select/minimum.expected +26 -0
  81. data/test/command/suite/groonga/select/minimum.test +8 -0
  82. data/test/command/suite/groonga/table_create/array.expected +17 -0
  83. data/test/command/suite/groonga/table_create/array.test +8 -0
  84. data/test/command/suite/groonga/table_create/hash.expected +17 -0
  85. data/test/command/suite/groonga/table_create/hash.test +8 -0
  86. data/test/command/suite/search/array-attribute-label.expected +25 -0
  87. data/test/command/suite/search/array-attribute-label.test +30 -0
  88. data/test/command/suite/search/chained-queries.expected +45 -0
  89. data/test/command/suite/search/chained-queries.test +43 -0
  90. data/test/command/suite/search/complex.expected +52 -0
  91. data/test/command/suite/search/complex.test +25 -0
  92. data/test/command/suite/search/condition-nested.expected +19 -0
  93. data/test/command/suite/search/condition-nested.test +29 -0
  94. data/test/command/suite/search/condition-query.expected +28 -0
  95. data/test/command/suite/search/condition-query.test +25 -0
  96. data/test/command/suite/search/condition-script.expected +28 -0
  97. data/test/command/suite/search/condition-script.test +28 -0
  98. data/test/command/suite/search/hash-attribute-label.expected +34 -0
  99. data/test/command/suite/search/hash-attribute-label.test +38 -0
  100. data/test/command/suite/search/minimum.expected +13 -0
  101. data/test/command/suite/search/minimum.test +16 -0
  102. data/test/command/suite/search/multiple-queries.expected +39 -0
  103. data/test/command/suite/search/multiple-queries.test +39 -0
  104. data/test/command/suite/search/output-range.expected +28 -0
  105. data/test/command/suite/search/output-range.test +25 -0
  106. data/test/command/suite/search/simple.expected +52 -0
  107. data/test/command/suite/search/simple.test +24 -0
  108. data/test/command/suite/search/sort-and-output-range.expected +25 -0
  109. data/test/command/suite/search/sort-and-output-range.test +29 -0
  110. data/test/command/suite/search/sort-range.expected +28 -0
  111. data/test/command/suite/search/sort-range.test +28 -0
  112. data/test/command/suite/search/sort-with-invisible-column.expected +28 -0
  113. data/test/command/suite/search/sort-with-invisible-column.test +28 -0
  114. data/test/unit/fixtures/array.grn +18 -0
  115. data/test/{fixtures → unit/fixtures}/catalog.json +0 -0
  116. data/test/{fixtures → unit/fixtures}/document.grn +20 -9
  117. data/test/unit/fixtures/reference/array.grn +11 -0
  118. data/test/unit/fixtures/reference/hash.grn +7 -0
  119. data/test/{helper.rb → unit/helper.rb} +2 -1
  120. data/test/{helper → unit/helper}/fixture.rb +1 -1
  121. data/test/unit/helper/plugin_helper.rb +38 -0
  122. data/test/{helper → unit/helper}/sandbox.rb +19 -6
  123. data/test/{helper → unit/helper}/stub_worker.rb +1 -1
  124. data/test/{helper → unit/helper}/watch_helper.rb +1 -13
  125. data/test/{plugin → unit/plugin}/adapter/groonga/test_select.rb +108 -4
  126. data/test/unit/plugin/collector/test_basic.rb +558 -0
  127. data/test/unit/plugin/distributor/test_search.rb +914 -0
  128. data/test/{plugin → unit/plugin}/handler/groonga/test_column_create.rb +18 -14
  129. data/test/{plugin → unit/plugin}/handler/groonga/test_table_create.rb +13 -11
  130. data/test/{plugin/handler/test_handler_add.rb → unit/plugin/handler/test_add.rb} +2 -14
  131. data/test/{plugin/handler/test_handler_groonga.rb → unit/plugin/handler/test_groonga.rb} +6 -26
  132. data/test/unit/plugin/handler/test_search.rb +601 -0
  133. data/test/{plugin/handler/test_handler_watch.rb → unit/plugin/handler/test_watch.rb} +2 -2
  134. data/test/{run-test.rb → unit/run-test.rb} +3 -3
  135. data/test/{test_adapter.rb → unit/test_adapter.rb} +17 -14
  136. data/test/{test_catalog.rb → unit/test_catalog.rb} +4 -4
  137. data/test/{test_command_mapper.rb → unit/test_command_mapper.rb} +1 -1
  138. data/test/{test_groonga_command_converter.rb → unit/test_groonga_command_converter.rb} +3 -3
  139. data/test/{test_job_queue_schema.rb → unit/test_job_queue_schema.rb} +1 -1
  140. data/test/{test_output.rb → unit/test_output.rb} +9 -9
  141. data/test/{test_handler.rb → unit/test_plugin.rb} +19 -22
  142. data/test/unit/test_plugin_repository.rb +89 -0
  143. data/test/{test_sweeper.rb → unit/test_sweeper.rb} +1 -1
  144. data/test/{test_watch_schema.rb → unit/test_watch_schema.rb} +1 -1
  145. data/test/{test_watcher.rb → unit/test_watcher.rb} +1 -1
  146. metadata +226 -66
  147. data/lib/droonga/executor.rb +0 -289
  148. data/lib/droonga/plugin/handler_proxy.rb +0 -82
  149. data/test/plugin/handler/test_handler_search.rb +0 -512
  150. data/test/test_worker.rb +0 -144
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 droonga project
3
+ # Copyright (C) 2013 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -15,21 +15,32 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
 
18
+ require "droonga/plugin"
19
+
18
20
  module Droonga
19
- class HandlerPlugin
20
- @@plugins = {}
21
- class << self
22
- def register(name, handler_class)
23
- @@plugins[name] = handler_class
24
- end
21
+ class HandlerPlugin < Plugin
22
+ extend PluginRegisterable
23
+
24
+ def initialize(handler)
25
+ super()
26
+ @handler = handler
27
+ @context = @handler.context
28
+ end
29
+
30
+ def envelope
31
+ @handler.envelope
32
+ end
33
+
34
+ def emit(value, name=nil)
35
+ @handler.emit(value, name)
25
36
  end
26
37
 
27
- def initialize(name)
28
- @name = name
38
+ def post(body, destination=nil)
39
+ @handler.post(body, destination)
29
40
  end
30
41
 
31
- def instantiate(*args)
32
- @@plugins[@name].new(*args)
42
+ def prefer_synchronous?(command)
43
+ false
33
44
  end
34
45
  end
35
46
  end
@@ -0,0 +1,51 @@
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
+ module Droonga
19
+ class InputMessage
20
+ def initialize(envelope)
21
+ @envelope = envelope
22
+ end
23
+
24
+ def adapted_envelope
25
+ # TODO: We can create adapted envelope non-destructively.
26
+ # If it is not performance issue, it is better that we don't
27
+ # change envelope destructively. Consider about it later.
28
+ @envelope
29
+ end
30
+
31
+ def add_route(route)
32
+ @envelope["via"].push(route)
33
+ end
34
+
35
+ def body
36
+ @envelope["body"]
37
+ end
38
+
39
+ def body=(body)
40
+ @envelope["body"] = body
41
+ end
42
+
43
+ def command
44
+ @envelope["type"]
45
+ end
46
+
47
+ def command=(command)
48
+ @envelope["type"] = command
49
+ end
50
+ end
51
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 droonga project
3
+ # Copyright (C) 2013 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -67,6 +67,10 @@ module Droonga
67
67
  MessagePack.unpack(packed_message)
68
68
  end
69
69
 
70
+ def unblock
71
+ @queue.unblock
72
+ end
73
+
70
74
  def close
71
75
  @queue = nil
72
76
  if @database
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 droonga project
3
+ # Copyright (C) 2013 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 droonga project
3
+ # Copyright (C) 2013 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -0,0 +1,76 @@
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 "serverengine"
19
+
20
+ require "droonga/server"
21
+ require "droonga/worker"
22
+ require "droonga/processor"
23
+
24
+ module Droonga
25
+ class Partition
26
+ def initialize(options={})
27
+ @options = options
28
+ @n_workers = @options[:n_workers] || 0
29
+ @processor = Processor.new(@options)
30
+ @supervisor = nil
31
+ end
32
+
33
+ def start
34
+ @processor.start
35
+ start_supervisor if @n_workers > 0
36
+ end
37
+
38
+ def shutdown
39
+ $log.trace("partition: shutdown: start")
40
+ shutdown_supervisor if @supervisor
41
+ @processor.shutdown
42
+ $log.trace("partition: shutdown: done")
43
+ end
44
+
45
+ def process(envelope, synchronous=nil)
46
+ $log.trace("partition: process: start")
47
+ @processor.process(envelope, synchronous)
48
+ $log.trace("partition: process: done")
49
+ end
50
+
51
+ private
52
+ def start_supervisor
53
+ @supervisor = ServerEngine::Supervisor.new(Server, Worker) do
54
+ force_options = {
55
+ :worker_type => "process",
56
+ :workers => @options[:n_workers],
57
+ :log_level => $log.level,
58
+ :server_process_name => "Server[#{@options[:database]}] #$0",
59
+ :worker_process_name => "Worker[#{@options[:database]}] #$0"
60
+ }
61
+ @options.merge(force_options)
62
+ end
63
+ @supervisor_thread = Thread.new do
64
+ @supervisor.main
65
+ end
66
+ end
67
+
68
+ def shutdown_supervisor
69
+ $log.trace("supervisor: shutdown: start")
70
+ @supervisor.stop(true)
71
+ $log.trace("supervisor: shutdown: stopped")
72
+ @supervisor_thread.join
73
+ $log.trace("supervisor: shutdown: done")
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,62 @@
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
+ module Droonga
19
+ module Pluggable
20
+ def shutdown
21
+ $log.trace("#{log_tag}: shutdown: plugin: start")
22
+ @plugins.each do |plugin|
23
+ plugin.shutdown
24
+ end
25
+ $log.trace("#{log_tag}: shutdown: plugin: done")
26
+ end
27
+
28
+ def processable?(command)
29
+ not find_plugin(command).nil?
30
+ end
31
+
32
+ def process(command, *arguments)
33
+ plugin = find_plugin(command)
34
+ $log.trace("#{log_tag}: process: start: <#{command}>",
35
+ :plugin => plugin.class)
36
+ if plugin.nil?
37
+ raise "unknown plugin: <#{command}>: " +
38
+ "TODO: improve error handling"
39
+ end
40
+ plugin.process(command, *arguments)
41
+ $log.trace("#{log_tag}: process: done: <#{command}>",
42
+ :plugin => plugin.class)
43
+ end
44
+
45
+ private
46
+ def load_plugins(names)
47
+ @plugins = names.collect do |name|
48
+ plugin = instantiate_plugin(name)
49
+ if plugin.nil?
50
+ raise "unknown plugin: <#{name}>: TODO: improve error handling"
51
+ end
52
+ plugin
53
+ end
54
+ end
55
+
56
+ def find_plugin(command)
57
+ @plugins.find do |plugin|
58
+ plugin.processable?(command)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 droonga project
3
+ # Copyright (C) 2013 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -15,27 +15,29 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
 
18
+ require "droonga/plugin_registerable"
19
+
18
20
  module Droonga
19
21
  class Plugin
20
- class << self
21
- def load_all
22
- $LOAD_PATH.each do |load_path|
23
- Dir.glob("#{load_path}/droonga/plugin/*_*.rb") do |path|
24
- type, name = File.basename(path, ".rb").split(/_/, 2)
25
- plugin = new(type, name)
26
- plugin.load
27
- end
28
- end
29
- end
22
+ def initialize
23
+ end
24
+
25
+ def start
26
+ end
27
+
28
+ def shutdown
30
29
  end
31
30
 
32
- def initialize(type, name)
33
- @type = type
34
- @name = name
31
+ def processable?(command)
32
+ self.class.processable?(command)
35
33
  end
36
34
 
37
- def load
38
- require "droonga/plugin/#{@type}_#{@name}"
35
+ def process(command, *arguments)
36
+ __send__(self.class.method_name(command), *arguments)
37
+ rescue => exception
38
+ Logger.error("error while processing #{command}",
39
+ arguments: arguments,
40
+ exception: exception)
39
41
  end
40
42
  end
41
43
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013 droonga project
1
+ # Copyright (C) 2013 Droonga Project
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -13,20 +13,20 @@
13
13
  # License along with this library; if not, write to the Free Software
14
14
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
15
 
16
- require "groonga"
17
-
18
- require "droonga/adapter"
16
+ require "droonga/adapter_plugin"
19
17
 
20
18
  module Droonga
21
- class GroongaAdapter < Droonga::Adapter
22
- # TODO: AdapterPlugin or something should be defined to avoid conflicts.
23
- Droonga::HandlerPlugin.register("select", self)
19
+ class GroongaAdapter < Droonga::AdapterPlugin
20
+ repository.register("select", self)
21
+
24
22
  command :select
25
- def select(select_request)
23
+ def select(input_message)
26
24
  command = Select.new
25
+ select_request = input_message.body
27
26
  search_request = command.convert_request(select_request)
28
- add_route("select_response")
29
- post(search_request, "search")
27
+ input_message.add_route("select_response")
28
+ input_message.command = "search"
29
+ input_message.body = search_request
30
30
  end
31
31
 
32
32
  command :select_response
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013 droonga project
1
+ # Copyright (C) 2013 Droonga Project
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -20,10 +20,12 @@ module Droonga
20
20
  table = select_request["table"]
21
21
  result_name = table + "_result"
22
22
  match_columns = select_request["match_columns"]
23
- match_to = match_columns
23
+ match_to = match_columns ? match_columns.split(/ *\|\| */) : []
24
24
  query = select_request["query"]
25
- output_columns = select_request["output_columns"]
25
+ output_columns = select_request["output_columns"] || ""
26
26
  attributes = output_columns.split(/, */)
27
+ offset = (select_request["offset"] || "0").to_i
28
+ limit = (select_request["limit"] || "10").to_i
27
29
 
28
30
  search_request = {
29
31
  "queries" => {
@@ -38,6 +40,8 @@ module Droonga
38
40
  "records",
39
41
  ],
40
42
  "attributes" => attributes,
43
+ "offset" => offset,
44
+ "limit" => limit,
41
45
  },
42
46
  }
43
47
  }
@@ -76,7 +80,12 @@ module Droonga
76
80
  end
77
81
 
78
82
  header = [status_code, start_time_in_unix_time, elapsed_time]
79
- results = [[count], converted_attributes, value["records"]]
83
+ records = value["records"]
84
+ if records.empty?
85
+ results = [[count], converted_attributes]
86
+ else
87
+ results = [[count], converted_attributes, records]
88
+ end
80
89
  body = [results]
81
90
 
82
91
  [header, body]
@@ -0,0 +1,142 @@
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/collector_plugin"
19
+
20
+ module Droonga
21
+ class BasicCollector < Droonga::CollectorPlugin
22
+ repository.register("basic", self)
23
+
24
+ UNLIMITED = -1
25
+
26
+ command :collector_gather
27
+ def collector_gather(result)
28
+ output = body ? body[input_name] : input_name
29
+ if output.is_a?(Hash)
30
+ element = output["element"]
31
+ if element
32
+ result[element] = apply_output_range(result[element], output)
33
+ result[element] = apply_output_attributes_and_format(result[element], output)
34
+ end
35
+ output = output["output"]
36
+ end
37
+ emit(result, output)
38
+ end
39
+
40
+ def apply_output_range(items, output)
41
+ if items && items.is_a?(Array)
42
+ offset = output["offset"] || 0
43
+ unless offset.zero?
44
+ items = items[offset..-1]
45
+ end
46
+
47
+ limit = output["limit"] || 0
48
+ unless limit == UNLIMITED
49
+ items = items[0...limit]
50
+ end
51
+ end
52
+ items
53
+ end
54
+
55
+ def apply_output_attributes_and_format(items, output)
56
+ attributes = output["attributes"]
57
+ if attributes
58
+ format = output["format"]
59
+ if format == "complex"
60
+ items.collect! do |item|
61
+ complex_item = {}
62
+ attributes.each_with_index do |label, index|
63
+ complex_item[label] = item[index]
64
+ end
65
+ complex_item
66
+ end
67
+ else
68
+ items.collect! do |item|
69
+ item[0...attributes.size]
70
+ end
71
+ end
72
+ end
73
+ items
74
+ end
75
+
76
+ command :collector_reduce
77
+ def collector_reduce(request)
78
+ return unless request
79
+ body[input_name].each do |output, elements|
80
+ value = request
81
+ old_value = output_values[output]
82
+ value = reduce(elements, old_value, request) if old_value
83
+ emit(value, output)
84
+ end
85
+ end
86
+
87
+ def reduce(elements, *values)
88
+ result = {}
89
+ elements.each do |key, deal|
90
+ reduced_values = nil
91
+
92
+ case deal["type"]
93
+ when "sum"
94
+ reduced_values = values[0][key] + values[1][key]
95
+ when "sort"
96
+ reduced_values = merge(values[0][key], values[1][key], deal["operators"])
97
+ end
98
+
99
+ reduced_values = apply_output_range(reduced_values, "limit" => deal["limit"])
100
+
101
+ result[key] = reduced_values
102
+ end
103
+ return result
104
+ end
105
+
106
+ def merge(x, y, operators)
107
+ # Normalize operators at first for optimization.
108
+ operators ||= []
109
+ operators = operators.collect do |operator|
110
+ if operator.is_a?(String)
111
+ { "operator" => operator }
112
+ else
113
+ operator
114
+ end
115
+ end
116
+
117
+ index = 0
118
+ y.each do |_y|
119
+ loop do
120
+ _x = x[index]
121
+ break unless _x
122
+ break if compare(_y, _x, operators)
123
+ index += 1
124
+ end
125
+ x.insert(index, _y)
126
+ index += 1
127
+ end
128
+ return x
129
+ end
130
+
131
+ def compare(x, y, operators)
132
+ operators.each_with_index do |operator, index|
133
+ column = operator["column"] || index
134
+ operator = operator["operator"]
135
+ _x = x[column]
136
+ _y = y[column]
137
+ return true if _x.__send__(operator, _y)
138
+ end
139
+ return false
140
+ end
141
+ end
142
+ end