csdn-tire 0.5
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/.gitignore +15 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +291 -0
- data/Rakefile +78 -0
- data/examples/tire.rb +81 -0
- data/lib/tire.rb +26 -0
- data/lib/tire/configuration.rb +31 -0
- data/lib/tire/count.rb +24 -0
- data/lib/tire/dsl.rb +26 -0
- data/lib/tire/http/client.rb +63 -0
- data/lib/tire/http/clients/curb.rb +62 -0
- data/lib/tire/http/response.rb +28 -0
- data/lib/tire/index.rb +98 -0
- data/lib/tire/logger.rb +61 -0
- data/lib/tire/results/pagination.rb +55 -0
- data/lib/tire/rubyext/ruby_1_8.rb +54 -0
- data/lib/tire/rubyext/to_json.rb +22 -0
- data/lib/tire/search.rb +80 -0
- data/lib/tire/state.rb +27 -0
- data/lib/tire/utils.rb +40 -0
- data/lib/tire/version.rb +4 -0
- data/test/integration/count_test.rb +20 -0
- data/test/integration/index_test.rb +58 -0
- data/test/integration/search_test.rb +23 -0
- data/test/integration/state_test.rb +22 -0
- data/test/test_helper.rb +104 -0
- data/test/unit/configuration_test.rb +74 -0
- data/test/unit/http_client_test.rb +77 -0
- data/test/unit/http_response_test.rb +50 -0
- data/test/unit/logger_test.rb +126 -0
- data/tire.gemspec +49 -0
- metadata +275 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# Stolen from ruby core's uri/common.rb, with modifications to support 1.8.x
|
3
|
+
#
|
4
|
+
# https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb
|
5
|
+
#
|
6
|
+
|
7
|
+
module URI
|
8
|
+
TBLENCWWWCOMP_ = {} # :nodoc:
|
9
|
+
256.times do |i|
|
10
|
+
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
11
|
+
end
|
12
|
+
TBLENCWWWCOMP_[' '] = '+'
|
13
|
+
TBLENCWWWCOMP_.freeze
|
14
|
+
TBLDECWWWCOMP_ = {} # :nodoc:
|
15
|
+
256.times do |i|
|
16
|
+
h, l = i>>4, i&15
|
17
|
+
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
18
|
+
TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
|
19
|
+
TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
|
20
|
+
TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
|
21
|
+
end
|
22
|
+
TBLDECWWWCOMP_['+'] = ' '
|
23
|
+
TBLDECWWWCOMP_.freeze
|
24
|
+
|
25
|
+
# Encode given +s+ to URL-encoded form data.
|
26
|
+
#
|
27
|
+
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
|
28
|
+
# (ASCII space) to + and converts others to %XX.
|
29
|
+
#
|
30
|
+
# This is an implementation of
|
31
|
+
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
32
|
+
#
|
33
|
+
# See URI.decode_www_form_component, URI.encode_www_form
|
34
|
+
def self.encode_www_form_component(s)
|
35
|
+
str = s.to_s
|
36
|
+
if RUBY_VERSION < "1.9" && $KCODE =~ /u/i
|
37
|
+
str.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
38
|
+
'%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
|
39
|
+
end.tr(' ', '+')
|
40
|
+
else
|
41
|
+
str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Decode given +str+ of URL-encoded form data.
|
46
|
+
#
|
47
|
+
# This decods + to SP.
|
48
|
+
#
|
49
|
+
# See URI.encode_www_form_component, URI.decode_www_form
|
50
|
+
def self.decode_www_form_component(str, enc=nil)
|
51
|
+
raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str
|
52
|
+
str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]}
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
class Array
|
5
|
+
def to_json(options=nil)
|
6
|
+
MultiJson.encode(self)
|
7
|
+
end unless method_defined? :to_json
|
8
|
+
end
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
def to_json(options=nil)
|
12
|
+
MultiJson.encode(self)
|
13
|
+
end unless method_defined? :to_json
|
14
|
+
|
15
|
+
alias_method :to_indexed_json, :to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
class Time
|
19
|
+
def to_json(options=nil)
|
20
|
+
%Q/"#{self.iso8601}"/
|
21
|
+
end
|
22
|
+
end
|
data/lib/tire/search.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
class SearchRequestFailed < StandardError; end
|
4
|
+
|
5
|
+
class Search
|
6
|
+
|
7
|
+
attr_reader :payload, :payload_hash, :indices
|
8
|
+
|
9
|
+
def initialize(indices, types, payload)
|
10
|
+
@indices = Array(indices)
|
11
|
+
@types = Array(types).map { |type| Utils.escape(type) }
|
12
|
+
if payload.is_a?(String)
|
13
|
+
@payload = payload
|
14
|
+
else
|
15
|
+
@payload_hash = payload
|
16
|
+
end
|
17
|
+
|
18
|
+
@path = ['/', @indices.join(','), @types.join(','), '_search'].compact.join('/').squeeze('/')
|
19
|
+
end
|
20
|
+
|
21
|
+
def results
|
22
|
+
@json || (perform; @json)
|
23
|
+
end
|
24
|
+
|
25
|
+
def response
|
26
|
+
@response || (perform; @response)
|
27
|
+
end
|
28
|
+
|
29
|
+
def url
|
30
|
+
Configuration.url + @path
|
31
|
+
end
|
32
|
+
|
33
|
+
def perform
|
34
|
+
@response = Configuration.client.get(self.url, self.payload)
|
35
|
+
if @response.failure?
|
36
|
+
STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
|
37
|
+
raise SearchRequestFailed, @response.to_s
|
38
|
+
end
|
39
|
+
@json = MultiJson.decode(@response.body)
|
40
|
+
return @json
|
41
|
+
ensure
|
42
|
+
logged
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_curl
|
46
|
+
%Q|curl -X GET #{url} -d '#{payload}'|
|
47
|
+
end
|
48
|
+
|
49
|
+
def payload
|
50
|
+
@payload || MultiJson.encode(@payload_hash)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_hash
|
54
|
+
@hash_payload ||= MultiJson.decode(payload)
|
55
|
+
end
|
56
|
+
|
57
|
+
def logged(error=nil)
|
58
|
+
if Configuration.logger
|
59
|
+
|
60
|
+
Configuration.logger.log_request '_search', indices, to_curl
|
61
|
+
|
62
|
+
code = @response.code rescue nil
|
63
|
+
|
64
|
+
if Configuration.logger.level.to_s == 'debug'
|
65
|
+
# FIXME: Depends on RestClient implementation
|
66
|
+
body = if @json
|
67
|
+
defined?(Yajl) ? Yajl::Encoder.encode(@json, :pretty => true) : MultiJson.encode(@json)
|
68
|
+
else
|
69
|
+
@response.body rescue nil
|
70
|
+
end
|
71
|
+
else
|
72
|
+
body = ''
|
73
|
+
end
|
74
|
+
|
75
|
+
Configuration.logger.log_response code || 'N/A', "N/A", body || 'N/A'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/lib/tire/state.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
class State
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def url
|
11
|
+
"#{Configuration.url}/cluster/_state"
|
12
|
+
end
|
13
|
+
|
14
|
+
def info(more = false)
|
15
|
+
desc_url = more ? "#{url}?more=true" : "#{url}?more=false"
|
16
|
+
@response = Configuration.client.get(desc_url)
|
17
|
+
if @response.success?
|
18
|
+
MultiJson.decode(@response.body)
|
19
|
+
else
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
ensure
|
23
|
+
curl = %Q|curl -X GET #{desc_url}|
|
24
|
+
logged('CLUSTER_STATE', curl)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/tire/utils.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
def escape(s)
|
8
|
+
URI.encode_www_form_component(s.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def unescape(s)
|
12
|
+
s = s.to_s.respond_to?(:force_encoding) ? s.to_s.force_encoding(Encoding::UTF_8) : s.to_s
|
13
|
+
URI.decode_www_form_component(s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def logged(endpoint='/', curl='')
|
17
|
+
if Configuration.logger
|
18
|
+
error = $!
|
19
|
+
|
20
|
+
Configuration.logger.log_request endpoint, @name, curl
|
21
|
+
|
22
|
+
code = @response ? @response.code : error.class rescue 200
|
23
|
+
|
24
|
+
if Configuration.logger.level.to_s == 'debug'
|
25
|
+
body = if @response
|
26
|
+
defined?(Yajl) ? Yajl::Encoder.encode(@response.body, :pretty => true) : MultiJson.encode(@response.body)
|
27
|
+
else
|
28
|
+
error.message rescue ''
|
29
|
+
end
|
30
|
+
else
|
31
|
+
body = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
Configuration.logger.log_response code, nil, body
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module_function :escape, :unescape
|
39
|
+
end
|
40
|
+
end
|
data/lib/tire/version.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
|
6
|
+
class CountIntegrationTest < Test::Unit::TestCase
|
7
|
+
include Test::Integration
|
8
|
+
|
9
|
+
context "count" do
|
10
|
+
should "count results" do
|
11
|
+
setup_bulk
|
12
|
+
@index.refresh
|
13
|
+
count = Tire.count(INDEX, TYPE, '{"query":{"text":{"title":"java"}},"size":4,"from":0}')
|
14
|
+
num = count.results
|
15
|
+
assert_kind_of Integer, num
|
16
|
+
assert_equal 6, num
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
|
6
|
+
class IndexIntegrationTest < Test::Unit::TestCase
|
7
|
+
include Test::Integration
|
8
|
+
|
9
|
+
context "index" do
|
10
|
+
|
11
|
+
should "regist_shard" do
|
12
|
+
assert @index.regist_shard(6)
|
13
|
+
end
|
14
|
+
|
15
|
+
should "not regist exist index" do
|
16
|
+
@index.regist_shard(6)
|
17
|
+
assert !@index.regist_shard(6)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "delete index" do
|
21
|
+
assert @index.delete
|
22
|
+
end
|
23
|
+
|
24
|
+
should "get shard info" do
|
25
|
+
@index.regist_shard(6)
|
26
|
+
assert_kind_of Array, @index.shard_info
|
27
|
+
assert !@index.shard_info.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
should "create mapping" do
|
31
|
+
assert setup_mapping
|
32
|
+
end
|
33
|
+
|
34
|
+
should "get mapping info" do
|
35
|
+
setup_mapping
|
36
|
+
assert_kind_of Hash, @index.mapping(TYPE)
|
37
|
+
assert_not_nil @index.mapping(TYPE)[TYPE]
|
38
|
+
end
|
39
|
+
|
40
|
+
should "bulk insert" do
|
41
|
+
assert setup_bulk
|
42
|
+
end
|
43
|
+
|
44
|
+
should "flush index" do
|
45
|
+
setup_bulk
|
46
|
+
assert @index.flush
|
47
|
+
end
|
48
|
+
|
49
|
+
should "refresh index" do
|
50
|
+
setup_bulk
|
51
|
+
assert @index.refresh
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
|
6
|
+
class SearchIntegrationTest < Test::Unit::TestCase
|
7
|
+
include Test::Integration
|
8
|
+
|
9
|
+
context "search" do
|
10
|
+
should "search results" do
|
11
|
+
setup_bulk
|
12
|
+
@index.refresh
|
13
|
+
search = Tire.search(INDEX, TYPE, '{"query":{"text":{"title":"java"}},"size":4,"from":0}')
|
14
|
+
results = search.results
|
15
|
+
assert_kind_of Hash, results
|
16
|
+
assert results["total"] > 0
|
17
|
+
assert_kind_of Array, results["hits"]
|
18
|
+
assert_equal 6, results["total"]
|
19
|
+
assert_equal 4, results["hits"].size
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
|
6
|
+
class StateIntegrationTest < Test::Unit::TestCase
|
7
|
+
include Test::Integration
|
8
|
+
|
9
|
+
context "state" do
|
10
|
+
should "get short state" do
|
11
|
+
assert_kind_of Array, Tire.state.info
|
12
|
+
assert_kind_of Hash, Tire.state.info.first
|
13
|
+
end
|
14
|
+
|
15
|
+
should "get more state" do
|
16
|
+
assert_kind_of Array, Tire.state.info(true)
|
17
|
+
assert_kind_of Hash, Tire.state.info(true).first
|
18
|
+
assert_not_nil Tire.state.info(true).first["blog"]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
require 'yajl'
|
8
|
+
#require 'yajl/json_gem'
|
9
|
+
|
10
|
+
require 'shoulda'
|
11
|
+
require 'turn/autorun' unless defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
|
12
|
+
require 'mocha'
|
13
|
+
|
14
|
+
require 'tire'
|
15
|
+
|
16
|
+
class Test::Unit::TestCase
|
17
|
+
|
18
|
+
def mock_response(body, code=200, headers={})
|
19
|
+
Tire::HTTP::Response.new(body, code, headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
module Test::Integration
|
25
|
+
URL = "http://192.168.6.35:9400"
|
26
|
+
INDEX = "articles_test"
|
27
|
+
TYPE = "type_test"
|
28
|
+
|
29
|
+
def setup
|
30
|
+
ENV['ELASTICSEARCH_URL'] = URL
|
31
|
+
@index = Tire.index(INDEX)
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_shard
|
35
|
+
@index.regist_shard(6)
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup_mapping
|
39
|
+
mapping = {TYPE => {"_source"=>{"enabled"=>false},
|
40
|
+
"properties"=>
|
41
|
+
{"title"=>
|
42
|
+
{"type"=>"string",
|
43
|
+
"term_vector"=>"with_positions_offsets",
|
44
|
+
"boost"=>2.0},
|
45
|
+
"body"=>{"type"=>"string", "term_vector"=>"with_positions_offsets"},
|
46
|
+
"username"=>{"type"=>"string", "index"=>"not_analyzed", "store"=>"no"},
|
47
|
+
"id"=>
|
48
|
+
{"type"=>"integer", "index"=>"not_analyzed", "include_in_all"=>false},
|
49
|
+
"created_at"=>
|
50
|
+
{"type"=>"integer", "index"=>"not_analyzed", "include_in_all"=>false}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
setup_shard
|
55
|
+
@index.create_mapping(TYPE, mapping)
|
56
|
+
end
|
57
|
+
|
58
|
+
def setup_bulk
|
59
|
+
doc = [
|
60
|
+
{"title"=>"java 是好东西",
|
61
|
+
"body"=>"hey java",
|
62
|
+
"id"=>"1",
|
63
|
+
"username"=>"jack",
|
64
|
+
"created_at"=>2007072323},
|
65
|
+
{"title"=>"this java cool",
|
66
|
+
"body"=>"hey java",
|
67
|
+
"id"=>"2",
|
68
|
+
"created_at"=>2009072323,
|
69
|
+
"username"=>"robbin"},
|
70
|
+
{"title"=>"this is java cool",
|
71
|
+
"body"=>"hey java",
|
72
|
+
"id"=>"3",
|
73
|
+
"created_at"=>2010072323,
|
74
|
+
"username"=>"www"},
|
75
|
+
{"title"=>"java is really cool",
|
76
|
+
"body"=>"hey java",
|
77
|
+
"id"=>"4",
|
78
|
+
"created_at"=>2007062323,
|
79
|
+
"username"=>"google"},
|
80
|
+
{"title"=>"this is wakak cool",
|
81
|
+
"body"=>"hey java",
|
82
|
+
"id"=>"5",
|
83
|
+
"created_at"=>2007062323,
|
84
|
+
"username"=>"jackde"},
|
85
|
+
{"title"=>"this is java cool",
|
86
|
+
"body"=>"hey java",
|
87
|
+
"id"=>"6",
|
88
|
+
"created_at"=>2007012323,
|
89
|
+
"username"=>"jackk wa"},
|
90
|
+
{"title"=>"this java really cool",
|
91
|
+
"body"=>"hey java",
|
92
|
+
"id"=>"7",
|
93
|
+
"created_at"=>2002072323,
|
94
|
+
"username"=>"william"}
|
95
|
+
]
|
96
|
+
setup_mapping
|
97
|
+
@index.bulk(TYPE, doc)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def teardown
|
102
|
+
::RestClient.delete "#{URL}/#{INDEX}" rescue nil
|
103
|
+
end
|
104
|
+
end
|