groonga-client 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -102,31 +102,31 @@ module Groonga
102
102
  end
103
103
  end
104
104
 
105
- class Type < Hash
105
+ class Type < ::Hash
106
106
  include Hashie::Extensions::MethodAccess
107
107
  end
108
108
 
109
- class Tokenizer < Hash
109
+ class Tokenizer < ::Hash
110
110
  include Hashie::Extensions::MethodAccess
111
111
  end
112
112
 
113
- class Normalizer < Hash
113
+ class Normalizer < ::Hash
114
114
  include Hashie::Extensions::MethodAccess
115
115
  end
116
116
 
117
- class TokenFilter < Hash
117
+ class TokenFilter < ::Hash
118
118
  include Hashie::Extensions::MethodAccess
119
119
  end
120
120
 
121
- class KeyType < Hash
121
+ class KeyType < ::Hash
122
122
  include Hashie::Extensions::MethodAccess
123
123
  end
124
124
 
125
- class ValueType < Hash
125
+ class ValueType < ::Hash
126
126
  include Hashie::Extensions::MethodAccess
127
127
  end
128
128
 
129
- class Index < Hash
129
+ class Index < ::Hash
130
130
  include Hashie::Extensions::MethodAccess
131
131
 
132
132
  def initialize(schema, raw_index)
@@ -155,13 +155,17 @@ module Groonga
155
155
  end
156
156
  end
157
157
 
158
+ def full_text_searchable?
159
+ table.tokenizer and column.position
160
+ end
161
+
158
162
  private
159
163
  def coerce_table(table_name)
160
164
  @schema.tables[table_name]
161
165
  end
162
166
  end
163
167
 
164
- class Column < Hash
168
+ class Column < ::Hash
165
169
  include Hashie::Extensions::MethodAccess
166
170
 
167
171
  def initialize(schema, raw_column)
@@ -181,6 +185,12 @@ module Groonga
181
185
  end
182
186
  end
183
187
 
188
+ def have_full_text_search_index?
189
+ indexes.any? do |index|
190
+ index.full_text_searchable?
191
+ end
192
+ end
193
+
184
194
  private
185
195
  def coerce_indexes(raw_indexes)
186
196
  raw_indexes.collect do |raw_index|
@@ -189,7 +199,7 @@ module Groonga
189
199
  end
190
200
  end
191
201
 
192
- class Table < Hash
202
+ class Table < ::Hash
193
203
  include Hashie::Extensions::MethodAccess
194
204
 
195
205
  def initialize(schema)
@@ -214,6 +224,12 @@ module Groonga
214
224
  end
215
225
  end
216
226
 
227
+ def have_full_text_search_index?
228
+ indexes.any? do |index|
229
+ index.full_text_searchable?
230
+ end
231
+ end
232
+
217
233
  private
218
234
  def coerce_key_type(raw_key_type)
219
235
  if raw_key_type.nil?
@@ -1,5 +1,6 @@
1
1
  # Copyright (C) 2013-2016 Kouhei Sutou <kou@clear-code.com>
2
2
  # Copyright (C) 2013 Kosuke Asami
3
+ # Copyright (C) 2016 Masafumi Yokoyama <yokoyama@clear-code.com>
3
4
  #
4
5
  # This library is free software; you can redistribute it and/or
5
6
  # modify it under the terms of the GNU Lesser General Public
@@ -23,9 +24,13 @@ module Groonga
23
24
  class Select < Base
24
25
  Response.register("select", self)
25
26
 
27
+ include Enumerable
28
+
26
29
  # @return [Integer] The number of records that match againt
27
30
  # a search condition.
28
31
  attr_accessor :n_hits
32
+ # For Kaminari
33
+ alias_method :total_count, :n_hits
29
34
  attr_accessor :records
30
35
 
31
36
  # @return [::Array<Groonga::Client::Response::Select::Drilldown>,
@@ -37,19 +42,51 @@ module Groonga
37
42
  # Otherwise, `[drilldown1, drilldown2]` is returned.
38
43
  attr_accessor :drilldowns
39
44
 
45
+ # @return [::Hash<String, Groonga::Client::Response::Select::Slice>]
46
+ #
47
+ # @since 0.3.4
48
+ attr_accessor :slices
49
+
40
50
  def body=(body)
41
51
  super(body)
42
52
  parse_body(body)
43
53
  end
44
54
 
55
+ # For Kaminari
56
+ def limit_value
57
+ (@command[:limit] || 10).to_i
58
+ end
59
+
60
+ # For Kaminari
61
+ def offset_value
62
+ (@command[:offset] || 0).to_i
63
+ end
64
+
65
+ # For Kaminari
66
+ def size
67
+ records.size
68
+ end
69
+
70
+ def each(&block)
71
+ records.each(&block)
72
+ end
73
+
45
74
  private
46
75
  def parse_body(body)
47
76
  if body.is_a?(::Array)
48
77
  @n_hits, @records = parse_match_records_v1(body.first)
49
- @drilldowns = parse_drilldowns_v1(body[1..-1])
78
+ if @command.slices.empty?
79
+ raw_slices = nil
80
+ raw_drilldowns = body[1..-1]
81
+ else
82
+ raw_slices, *raw_drilldowns = body[1..-1]
83
+ end
84
+ @slices = parse_slices_v1(raw_slices)
85
+ @drilldowns = parse_drilldowns_v1(raw_drilldowns)
50
86
  else
51
87
  @n_hits, @records = parse_match_records_v3(body)
52
88
  @drilldowns = parse_drilldowns_v3(body["drilldowns"])
89
+ @slices = parse_slices_v3(body["slices"])
53
90
  end
54
91
  body
55
92
  end
@@ -74,7 +111,7 @@ module Groonga
74
111
  end
75
112
 
76
113
  (raw_records || []).collect do |raw_record|
77
- record = {}
114
+ record = Record.new
78
115
  columns.each_with_index do |(name, type), i|
79
116
  record[name] = convert_value(raw_record[i], type)
80
117
  end
@@ -139,10 +176,35 @@ module Groonga
139
176
  drilldowns
140
177
  end
141
178
 
179
+ def parse_slices_v1(raw_slices)
180
+ slices = {}
181
+ (raw_slices || {}).each do |key, raw_slice|
182
+ n_hits, records = parse_match_records_v1(raw_slice)
183
+ slices[key] = Slice.new(key, n_hits, records)
184
+ end
185
+ slices
186
+ end
187
+
188
+ def parse_slices_v3(raw_slices)
189
+ slices = {}
190
+ (raw_slices || {}).each do |key, raw_slice|
191
+ n_hits, records = parse_match_records_v3(raw_slice)
192
+ slices[key] = Slice.new(key, n_hits, records)
193
+ end
194
+ slices
195
+ end
196
+
197
+ class Record < ::Hash
198
+ include Hashie::Extensions::MethodAccess
199
+ end
200
+
142
201
  class Drilldown < Struct.new(:key, :n_hits, :records)
143
202
  # @deprecated since 0.2.6. Use {#records} instead.
144
203
  alias_method :items, :records
145
204
  end
205
+
206
+ class Slice < Struct.new(:key, :n_hits, :records)
207
+ end
146
208
  end
147
209
  end
148
210
  end
@@ -0,0 +1,37 @@
1
+ # Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "groonga/client/test/fixture"
18
+
19
+ module Groonga
20
+ class Client
21
+ module SpecHelper
22
+ include Test::Fixture
23
+
24
+ class << self
25
+ def included(target)
26
+ target.before(:each) do
27
+ setup_groonga
28
+ end
29
+
30
+ target.after(:each) do
31
+ teardown_groonga
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "groonga/client/test/fixture"
18
+
19
+ module Groonga
20
+ class Client
21
+ module TestHelper
22
+ include Test::Fixture
23
+
24
+ class << self
25
+ def included(target)
26
+ target.setup do
27
+ setup_groonga
28
+ end
29
+
30
+ target.teardown do
31
+ teardown_groonga
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ # Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "groonga/client/test/groonga-server-runner"
18
+
19
+ module Groonga
20
+ class Client
21
+ module Test
22
+ module Fixture
23
+ def setup_groonga
24
+ @groonga_server_runner = GroongaServerRunner.new
25
+ @groonga_server_runner.run
26
+ end
27
+
28
+ def teardown_groonga
29
+ return if @groonga_server_runner.nil?
30
+ @groonga_server_runner.stop
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,158 @@
1
+ # Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "rbconfig"
18
+ require "fileutils"
19
+
20
+ module Groonga
21
+ class Client
22
+ module Test
23
+ class GroongaServerRunner
24
+ def initialize
25
+ @pid = nil
26
+ @using_running_server = false
27
+ @url = build_url
28
+ @groonga = find_groonga
29
+ @tmp_dir = nil
30
+ end
31
+
32
+ def run
33
+ if groonga_server_running?
34
+ @using_running_server = true
35
+ else
36
+ return if @groonga.nil?
37
+ @tmp_dir = create_tmp_dir
38
+ db_path = @tmp_dir + "db"
39
+ @pid = spawn(@groonga,
40
+ "--port", @url.port.to_s,
41
+ "--log-path", (@tmp_dir + "groonga.log").to_s,
42
+ "--query-log-path", (@tmp_dir + "query.log").to_s,
43
+ "--protocol", "http",
44
+ "-s",
45
+ "-n", db_path.to_s)
46
+ wait_groonga_ready
47
+ end
48
+ end
49
+
50
+ def stop
51
+ if @using_running_server
52
+ Groonga::Client.open(url: @url) do |client|
53
+ schema = client.schema
54
+ schema.tables.each do |name, _|
55
+ client.delete(table: name,
56
+ filter: "true")
57
+ end
58
+ end
59
+ else
60
+ if @pid
61
+ Groonga::Client.open(url: @url) do |client|
62
+ client.shutdown
63
+ end
64
+ wait_groonga_shutdown
65
+ end
66
+ if @tmp_dir
67
+ FileUtils.rm_rf(@tmp_dir)
68
+ end
69
+ end
70
+ end
71
+
72
+ private
73
+ def build_url
74
+ default_options = Groonga::Client.default_options
75
+ url = default_options[:url]
76
+ if url.nil?
77
+ host = default_options[:host] ||
78
+ default_options[:address] ||
79
+ "127.0.0.1"
80
+ port = default_options[:port] || 10041
81
+ path = default_options[:path]
82
+ url = URI("http://#{host}:#{port}#{path}")
83
+ end
84
+ url
85
+ end
86
+
87
+ def groonga_server_running?
88
+ begin
89
+ TCPSocket.open(@url.host, @url.port) do
90
+ end
91
+ rescue SystemCallError
92
+ false
93
+ else
94
+ true
95
+ end
96
+ end
97
+
98
+ def find_groonga
99
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
100
+ exeext = RbConfig::CONFIG["EXEEXT"]
101
+ paths.each do |path|
102
+ groonga = File.join(path, "groonga#{exeext}")
103
+ return groonga if File.executable?(groonga)
104
+ end
105
+ nil
106
+ end
107
+
108
+ def create_tmp_dir
109
+ tmpfs_dir = "/dev/shm"
110
+ if File.directory?(tmpfs_dir)
111
+ base_tmp_dir = Pathname(tmpfs_dir)
112
+ else
113
+ base_tmp_dir = Pathname("tmp")
114
+ end
115
+ tmp_dir = base_tmp_dir + "groonga-client.#{Process.pid}"
116
+ FileUtils.rm_rf(tmp_dir)
117
+ FileUtils.mkdir_p(tmp_dir)
118
+ tmp_dir
119
+ end
120
+
121
+ def wait_groonga_ready
122
+ n_retried = 0
123
+ while n_retried <= 20
124
+ n_retried += 1
125
+ sleep(0.05)
126
+ if groonga_server_running?
127
+ break
128
+ else
129
+ begin
130
+ pid = Process.waitpid(@pid, Process::WNOHANG)
131
+ rescue SystemCallError
132
+ @pid = nil
133
+ break
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ def wait_groonga_shutdown
140
+ # TODO: Remove me when Groonga 6.0.1 has been released.
141
+ # Workaround to shutdown as soon as possible.
142
+ groonga_server_running?
143
+
144
+ n_retried = 0
145
+ while n_retried <= 20
146
+ n_retried += 1
147
+ sleep(0.05)
148
+ pid = Process.waitpid(@pid, Process::WNOHANG)
149
+ return if pid
150
+ end
151
+
152
+ Process.kill(:KILL, @pid)
153
+ Process.waitpid(@pid)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end