groonga-client 0.0.2 → 0.0.3
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.
- data/.yardopts +1 -0
- data/README.md +1 -0
- data/doc/text/news.md +16 -3
- data/groonga-client.gemspec +1 -1
- data/lib/groonga/client.rb +69 -7
- data/lib/groonga/client/command.rb +1 -2
- data/lib/groonga/client/connection/error.rb +31 -0
- data/lib/groonga/client/{protocol → connection}/gqtp.rb +41 -1
- data/lib/groonga/client/connection/http.rb +83 -0
- data/lib/groonga/client/{protocol/http.rb → connection/request.rb} +5 -18
- data/lib/groonga/client/response/base.rb +38 -8
- data/lib/groonga/client/response/column_list.rb +3 -2
- data/lib/groonga/client/response/select.rb +46 -14
- data/lib/groonga/client/response/table_create.rb +12 -0
- data/lib/groonga/client/response/table_list.rb +3 -2
- data/lib/groonga/client/version.rb +1 -1
- data/test/connection/test-gqtp.rb +105 -0
- data/test/connection/test-http.rb +97 -0
- data/test/response/helper.rb +4 -0
- data/test/response/test-column-list.rb +2 -11
- data/test/response/test-select.rb +127 -12
- data/test/response/test-table-list.rb +2 -11
- data/test/results/test-column-list.rb +2 -1
- data/test/results/test-table-list.rb +2 -1
- data/test/test-client.rb +43 -27
- data/test/test-command.rb +36 -3
- metadata +36 -31
- data/test/results/test-select.rb +0 -63
@@ -1,3 +1,20 @@
|
|
1
|
+
# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
# Copyright (C) 2013 Kosuke Asami
|
3
|
+
#
|
4
|
+
# This library is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
6
|
+
# License as published by the Free Software Foundation; either
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
1
18
|
require "groonga/client/response/base"
|
2
19
|
|
3
20
|
module Groonga
|
@@ -6,32 +23,45 @@ module Groonga
|
|
6
23
|
class Select < Base
|
7
24
|
Response.register("select", self)
|
8
25
|
|
9
|
-
|
26
|
+
# @return [Integer] The number of records that match againt
|
27
|
+
# a search condition.
|
28
|
+
attr_accessor :n_hits
|
29
|
+
attr_accessor :records
|
10
30
|
attr_accessor :drilldowns
|
11
31
|
|
12
|
-
def
|
13
|
-
super(
|
32
|
+
def body=(body)
|
33
|
+
super(body)
|
34
|
+
parse_body(body)
|
14
35
|
end
|
15
36
|
|
16
37
|
private
|
17
38
|
def parse_body(body)
|
18
|
-
@
|
39
|
+
@n_hits, @records = parse_match_records(body.first)
|
19
40
|
@drilldowns = parse_drilldowns(body[1..-1])
|
20
41
|
body
|
21
42
|
end
|
22
43
|
|
23
44
|
def parse_result(raw_result)
|
24
|
-
|
45
|
+
n_hits = raw_result.first.first
|
25
46
|
properties = raw_result[1]
|
26
|
-
infos = raw_result[2..-1]
|
47
|
+
infos = raw_result[2..-1] || []
|
27
48
|
items = infos.collect do |info|
|
28
49
|
item = {}
|
29
|
-
properties.each_with_index do |(name,
|
30
|
-
item[name] = info[i]
|
50
|
+
properties.each_with_index do |(name, type), i|
|
51
|
+
item[name] = convert_value(info[i], type)
|
31
52
|
end
|
32
53
|
item
|
33
|
-
end
|
34
|
-
[
|
54
|
+
end
|
55
|
+
[n_hits, items]
|
56
|
+
end
|
57
|
+
|
58
|
+
def convert_value(value, type)
|
59
|
+
case type
|
60
|
+
when "Time"
|
61
|
+
Time.at(value)
|
62
|
+
else
|
63
|
+
value
|
64
|
+
end
|
35
65
|
end
|
36
66
|
|
37
67
|
def parse_match_records(raw_records)
|
@@ -39,13 +69,15 @@ module Groonga
|
|
39
69
|
end
|
40
70
|
|
41
71
|
def parse_drilldowns(raw_drilldowns)
|
42
|
-
raw_drilldowns.collect do |raw_drilldown|
|
72
|
+
(raw_drilldowns || []).collect.with_index do |raw_drilldown, i|
|
73
|
+
key = @command.drilldowns[i]
|
43
74
|
n_hits, items = parse_result(raw_drilldown)
|
44
|
-
Drilldown.new(n_hits, items)
|
45
|
-
end
|
75
|
+
Drilldown.new(key, n_hits, items)
|
76
|
+
end
|
46
77
|
end
|
47
78
|
|
48
|
-
Drilldown
|
79
|
+
class Drilldown < Struct.new(:key, :n_hits, :items)
|
80
|
+
end
|
49
81
|
end
|
50
82
|
end
|
51
83
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
|
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 as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "socket"
|
20
|
+
|
21
|
+
require "groonga/client/connection/gqtp"
|
22
|
+
|
23
|
+
class TestConnectionGQTP < Test::Unit::TestCase
|
24
|
+
def setup
|
25
|
+
setup_server
|
26
|
+
setup_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def teardown
|
30
|
+
teardown_connection
|
31
|
+
teardown_server
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_server
|
35
|
+
@address = "127.0.0.1"
|
36
|
+
@server = TCPServer.new(@address, 0)
|
37
|
+
@port = @server.addr[1]
|
38
|
+
@protocol = :gqtp
|
39
|
+
|
40
|
+
@thread = Thread.new do
|
41
|
+
client = @server.accept
|
42
|
+
@server.close
|
43
|
+
|
44
|
+
process_client_close(client)
|
45
|
+
|
46
|
+
client.close
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def teardown_server
|
51
|
+
@thread.kill
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup_connection
|
55
|
+
@connection = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def teardown_connection
|
59
|
+
@connection.close {} if @connection
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def connect(options={})
|
64
|
+
default_options = {
|
65
|
+
:host => @address,
|
66
|
+
:port => @port,
|
67
|
+
}
|
68
|
+
Groonga::Client::Connection::GQTP.new(default_options.merge(options))
|
69
|
+
end
|
70
|
+
|
71
|
+
def process_client_close(client)
|
72
|
+
response_body = [].to_json
|
73
|
+
2.times do
|
74
|
+
header = GQTP::Header.parse(client.read(GQTP::Header.size))
|
75
|
+
client.read(header.size)
|
76
|
+
|
77
|
+
response_header = GQTP::Header.new
|
78
|
+
response_header.size = response_body.bytesize
|
79
|
+
|
80
|
+
client.write(response_header.pack)
|
81
|
+
client.write(response_body)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class TestConnected < self
|
86
|
+
def test_opened
|
87
|
+
@connection = connect
|
88
|
+
assert_true(@connection.connected?)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_closed
|
92
|
+
@connection = connect
|
93
|
+
@connection.close
|
94
|
+
assert_false(@connection.connected?)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class TestClose < self
|
99
|
+
def test_twice
|
100
|
+
@connection = connect
|
101
|
+
@connection.close
|
102
|
+
assert_false(@connection.close)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
|
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 as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "socket"
|
20
|
+
|
21
|
+
require "groonga/client/connection/http"
|
22
|
+
|
23
|
+
class TestConnectionHTTP < Test::Unit::TestCase
|
24
|
+
def setup
|
25
|
+
setup_server
|
26
|
+
setup_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def teardown
|
30
|
+
teardown_connection
|
31
|
+
teardown_server
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_server
|
35
|
+
@address = "127.0.0.1"
|
36
|
+
@server = TCPServer.new(@address, 0)
|
37
|
+
@port = @server.addr[1]
|
38
|
+
@protocol = :http
|
39
|
+
|
40
|
+
@response_body = nil
|
41
|
+
@thread = Thread.new do
|
42
|
+
client = @server.accept
|
43
|
+
@server.close
|
44
|
+
|
45
|
+
status = 0
|
46
|
+
start = Time.now.to_f
|
47
|
+
elapsed = rand
|
48
|
+
header = "[#{status},#{start},#{elapsed}]"
|
49
|
+
body = "[#{header},#{@response_body}]"
|
50
|
+
|
51
|
+
http_header = <<-EOH
|
52
|
+
HTTP/1.1 200 OK
|
53
|
+
Connection: close
|
54
|
+
Content-Type: application/json
|
55
|
+
Content-Length: #{body.bytesize}
|
56
|
+
|
57
|
+
EOH
|
58
|
+
|
59
|
+
client.write(http_header)
|
60
|
+
client.write(body)
|
61
|
+
client.close
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def teardown_server
|
66
|
+
@thread.kill
|
67
|
+
end
|
68
|
+
|
69
|
+
def setup_connection
|
70
|
+
@connection = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def teardown_connection
|
74
|
+
@connection.close if @connection
|
75
|
+
end
|
76
|
+
|
77
|
+
def connect(options={})
|
78
|
+
default_options = {
|
79
|
+
:host => @address,
|
80
|
+
:port => @port,
|
81
|
+
}
|
82
|
+
Groonga::Client::Connection::HTTP.new(default_options.merge(options))
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_connected?
|
86
|
+
@connection = connect
|
87
|
+
assert_false(@connection.connected?)
|
88
|
+
end
|
89
|
+
|
90
|
+
class TestClose < self
|
91
|
+
def test_twice
|
92
|
+
@connection = connect
|
93
|
+
assert_false(@connection.close)
|
94
|
+
assert_false(@connection.close)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/test/response/helper.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
module TestResponseHelper
|
2
|
+
def parse_raw_response(command_name, raw_response)
|
3
|
+
make_command(command_name).send(:parse_raw_response, raw_response)
|
4
|
+
end
|
5
|
+
|
2
6
|
def make_command(command_name, parameters = {})
|
3
7
|
command_class = Groonga::Command.find(command_name)
|
4
8
|
command = command_class.new(command_name, parameters)
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "test/unit/rr"
|
2
|
-
|
3
1
|
require "response/helper"
|
4
2
|
|
5
3
|
class TestResponseColumnList < Test::Unit::TestCase
|
@@ -9,16 +7,9 @@ class TestResponseColumnList < Test::Unit::TestCase
|
|
9
7
|
header = [0,1372430096.70991,0.000522851943969727]
|
10
8
|
body = [[["id","UInt32"],["name","ShortText"],["path","ShortText"],["type","ShortText"],["flags","ShortText"],["domain","ShortText"],["range","ShortText"],["source","ShortText"]],
|
11
9
|
[256,"Text","/tmp/test.db.0000100","var","COLUMN_SCALAR|PERSISTENT","TestTable","ShortText",[]]]
|
10
|
+
raw_response = [header, body].to_json
|
12
11
|
|
13
|
-
|
14
|
-
stub(connection).send.with_any_args.yields([header, body].to_json) do
|
15
|
-
request = Object.new
|
16
|
-
stub(request).wait do
|
17
|
-
true
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
response = make_command("column_list").execute(connection)
|
12
|
+
response = parse_raw_response("column_list", raw_response)
|
22
13
|
assert_equal(Groonga::Client::Response::ColumnList, response.class)
|
23
14
|
end
|
24
15
|
end
|
@@ -1,23 +1,138 @@
|
|
1
|
-
|
1
|
+
# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
# Copyright (C) 2013 Kosuke Asami
|
3
|
+
#
|
4
|
+
# This library is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
6
|
+
# License as published by the Free Software Foundation; either
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2
17
|
|
3
18
|
require "response/helper"
|
4
19
|
|
5
20
|
class TestResponseSelect < Test::Unit::TestCase
|
6
|
-
|
21
|
+
class TestParseRawResponse < self
|
22
|
+
include TestResponseHelper
|
7
23
|
|
8
|
-
|
9
|
-
|
10
|
-
|
24
|
+
def test_select
|
25
|
+
header = [0,1372430096.70991,0.000522851943969727]
|
26
|
+
body = [[[1], [["_id", "UInt32"]], [1]]]
|
27
|
+
raw_response = [header, body].to_json
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
29
|
+
response = parse_raw_response("select", raw_response)
|
30
|
+
assert_equal(Groonga::Client::Response::Select, response.class)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TestBody < self
|
35
|
+
def setup
|
36
|
+
@command = Groonga::Command::Select.new("select", {})
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_n_hits
|
40
|
+
response = create_response([[[29], [["_id", "UInt32"]]]])
|
41
|
+
assert_equal(29, response.n_hits)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def create_response(body)
|
46
|
+
header = [0, 1372430096.70991, 0.000522851943969727]
|
47
|
+
Groonga::Client::Response::Select.new(@command, header, body)
|
48
|
+
end
|
49
|
+
|
50
|
+
class TestRecords < self
|
51
|
+
def test_time
|
52
|
+
updated_at = 1379040474
|
53
|
+
assert_equal([{"updated_at" => Time.at(updated_at)}],
|
54
|
+
records([[[1], [["updated_at", "Time"]], [updated_at]]]))
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def records(body)
|
59
|
+
create_response(body).records
|
17
60
|
end
|
18
61
|
end
|
19
62
|
|
20
|
-
|
21
|
-
|
63
|
+
class TestDrilldowns < self
|
64
|
+
def setup
|
65
|
+
pair_arguments = {
|
66
|
+
"drilldown" => "_key",
|
67
|
+
"drilldown_output_columns" => "_key,_nsubrecs",
|
68
|
+
}
|
69
|
+
@command = Groonga::Command::Select.new("select", pair_arguments)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_key
|
73
|
+
body = [
|
74
|
+
[[0], []],
|
75
|
+
[
|
76
|
+
[29],
|
77
|
+
[
|
78
|
+
["_key", "ShortText"],
|
79
|
+
["_nsubrecs", "Int32"],
|
80
|
+
],
|
81
|
+
["groonga", 29],
|
82
|
+
["Ruby", 19],
|
83
|
+
["rroonga", 9],
|
84
|
+
],
|
85
|
+
]
|
86
|
+
assert_equal(["_key"],
|
87
|
+
drilldowns(body).collect(&:key))
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_n_hits
|
91
|
+
body = [
|
92
|
+
[[0], []],
|
93
|
+
[
|
94
|
+
[29],
|
95
|
+
[
|
96
|
+
["_key", "ShortText"],
|
97
|
+
["_nsubrecs", "Int32"],
|
98
|
+
],
|
99
|
+
["groonga", 29],
|
100
|
+
["Ruby", 19],
|
101
|
+
["rroonga", 9],
|
102
|
+
],
|
103
|
+
]
|
104
|
+
assert_equal([29],
|
105
|
+
drilldowns(body).collect(&:n_hits))
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_items
|
109
|
+
body = [
|
110
|
+
[[0], []],
|
111
|
+
[
|
112
|
+
[29],
|
113
|
+
[
|
114
|
+
["_key", "ShortText"],
|
115
|
+
["_nsubrecs", "Int32"],
|
116
|
+
],
|
117
|
+
["groonga", 29],
|
118
|
+
["Ruby", 19],
|
119
|
+
["rroonga", 9],
|
120
|
+
],
|
121
|
+
]
|
122
|
+
assert_equal([
|
123
|
+
[
|
124
|
+
{"_key" => "groonga", "_nsubrecs" => 29},
|
125
|
+
{"_key" => "Ruby", "_nsubrecs" => 19},
|
126
|
+
{"_key" => "rroonga", "_nsubrecs" => 9},
|
127
|
+
],
|
128
|
+
],
|
129
|
+
drilldowns(body).collect(&:items))
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
def drilldowns(body)
|
134
|
+
create_response(body).drilldowns
|
135
|
+
end
|
136
|
+
end
|
22
137
|
end
|
23
138
|
end
|