waistband 0.7.4 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/README.md +30 -3
- data/lib/waistband/configuration.rb +1 -0
- data/lib/waistband/connection.rb +166 -45
- data/lib/waistband/index.rb +58 -41
- data/lib/waistband/query.rb +1 -5
- data/lib/waistband/version.rb +1 -1
- data/spec/config/waistband/waistband_events.yml +1 -3
- data/spec/config/waistband/waistband_events_no_name.yml +13 -0
- data/spec/config/waistband/waistband_geo.yml +2 -3
- data/spec/config/waistband/waistband_search.yml +2 -3
- data/spec/lib/configuration_spec.rb +2 -4
- data/spec/lib/connection_spec.rb +227 -36
- data/spec/lib/index_spec.rb +54 -4
- data/spec/lib/query_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/index_helper.rb +7 -8
- metadata +4 -2
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -56,7 +56,6 @@ You'll need a separate config file for each index you use, containing the index
|
|
56
56
|
```yml
|
57
57
|
# #{APP_DIR}/config/waistband/waistband_search.yml
|
58
58
|
development:
|
59
|
-
name: search
|
60
59
|
stringify: false
|
61
60
|
settings:
|
62
61
|
index:
|
@@ -69,10 +68,10 @@ development:
|
|
69
68
|
|
70
69
|
## List of config settings:
|
71
70
|
|
72
|
-
* `name`: name of the index. You can (and probably should) have a different name for the index for your test environment.
|
73
|
-
* `stringify`: determines wether whatever is stored into the index is going to be converted to a string before storage. Usually false unless you need it to be true for specific cases, like if for some `key => value` pairs the value is of different types some times.
|
74
71
|
* `settings`: settings for the Elastic Search index. Refer to the ["admin indices update settings"](http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings/) document for more info.
|
75
72
|
* `mappings`: the index mappings. More often than not you'll want to include all of the document attribute, so you'll do something like in the example above. For more info, refer to the [mapping reference]("http://www.elasticsearch.org/guide/reference/mapping/").
|
73
|
+
* `name`: optional - name of the index. You can (and probably should) have a different name for the index for your test environment. If not specified, it defaults to the name of the yml file minus the `waistband_` portion, so in the above example, the index name would become `search_#{env}`, where env is your environment variable as defined in `Waistband::Configuration#setup` (determined by `RAILS_ENV` or `RACK_ENV`).
|
74
|
+
* `stringify`: optional - determines wether whatever is stored into the index is going to be converted to a string before storage. Usually false unless you need it to be true for specific cases, like if for some `key => value` pairs the value is of different types some times.
|
76
75
|
|
77
76
|
## Initializer
|
78
77
|
|
@@ -114,6 +113,8 @@ When writing tests, it would generally be advisable to destroy and create the in
|
|
114
113
|
Waistband::Index.new('search').refresh
|
115
114
|
```
|
116
115
|
|
116
|
+
Note: most index methods such as `create`, `destroy`, `read`, etc, have an equivalent bang method (`destroy!`) that will actually throw an exception if something goes wrong. For example, `destroy` will return nil if the index doesn't exist, but will raise any other unrelated exceptions, whereas `destroy!` will raise even the Index Not Found exception.
|
117
|
+
|
117
118
|
#### Writing, reading and deleting from an index
|
118
119
|
|
119
120
|
```ruby
|
@@ -168,6 +169,32 @@ For paginating the results, you can use the `#paginated_results` method, which r
|
|
168
169
|
|
169
170
|
For more information and extra methods, take a peek into the class docs.
|
170
171
|
|
172
|
+
### Sub-Indexes
|
173
|
+
|
174
|
+
Sometimes it can be useful to sub-divide your index into smaller indexes based on dates or other partitioning schemes. To do this, the `Index` class exposes the `subs` option on instantiation:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
index = Waistband::Index.new('events', subs: %w(2013 01))
|
178
|
+
index.create!
|
179
|
+
```
|
180
|
+
|
181
|
+
This creates the index `events__2013_01`, which in your application logic you could design to store all event data for Jan 2013. You'd do the same for Feb, etc., and when you no longer need one of the older ones, you could delete just that sub-index, instead of things getting more complicated.
|
182
|
+
|
183
|
+
### Aliasing
|
184
|
+
|
185
|
+
Part of subbing is gonna be creating the correct aliases that group up your sub-indexes.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
index = Waistband::Index.new('events', subs: %w(2013 01))
|
189
|
+
index.create!
|
190
|
+
index.alias!('my_super_events_alias')
|
191
|
+
=> true
|
192
|
+
index.fetch_alias('my_super_events_alias')
|
193
|
+
=> {"events__2013_01"=>{"aliases"=>{"my_super_events_alias"=>{}}}}
|
194
|
+
```
|
195
|
+
|
196
|
+
The `alias!` methods receives a param to define the alias name.
|
197
|
+
|
171
198
|
## Contributing
|
172
199
|
|
173
200
|
1. Fork it
|
data/lib/waistband/connection.rb
CHANGED
@@ -6,52 +6,170 @@ require 'active_support/core_ext/hash/indifferent_access'
|
|
6
6
|
module Waistband
|
7
7
|
class Connection
|
8
8
|
|
9
|
-
|
9
|
+
##########
|
10
|
+
# Errors #
|
11
|
+
##########
|
10
12
|
|
11
|
-
|
13
|
+
module Error
|
14
|
+
|
15
|
+
class Resquest < Exception
|
16
|
+
attr_accessor :result, :kind
|
17
|
+
|
18
|
+
def index_already_exists?
|
19
|
+
kind == "IndexAlreadyExistsException"
|
20
|
+
end
|
21
|
+
|
22
|
+
def alias_with_name_exists?
|
23
|
+
kind == "InvalidIndexNameException" && !!message.match("an alias with the same name already exists")
|
24
|
+
end
|
25
|
+
|
26
|
+
def index_missing?
|
27
|
+
kind == "IndexMissingException"
|
28
|
+
end
|
29
|
+
|
30
|
+
def key_missing?
|
31
|
+
kind == "KeyMissing"
|
32
|
+
end
|
33
|
+
|
34
|
+
def alias_not_ok?
|
35
|
+
kind == "AliasNotOk"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class NoMoreServers < Exception; end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
####################
|
44
|
+
# Instance methods #
|
45
|
+
####################
|
46
|
+
|
47
|
+
def initialize(index, options = {})
|
48
|
+
@index = index
|
12
49
|
@blacklist = []
|
13
50
|
@retry_on_fail = options.fetch :retry_on_fail, true
|
14
51
|
@orderly = options.fetch :orderly, false
|
15
52
|
pick_server
|
16
53
|
end
|
17
54
|
|
18
|
-
def
|
19
|
-
execute! '
|
20
|
-
|
21
|
-
|
55
|
+
def mapping!
|
56
|
+
execute! 'get', "#{@index.config_name}/_mapping"
|
57
|
+
end
|
58
|
+
|
59
|
+
def mapping
|
60
|
+
mapping!
|
61
|
+
rescue Waistband::Connection::Error::Resquest => ex
|
62
|
+
raise ex unless ex.index_missing?
|
63
|
+
end
|
64
|
+
|
65
|
+
def exists?
|
66
|
+
!!mapping.present?
|
67
|
+
end
|
68
|
+
|
69
|
+
def create!
|
70
|
+
execute! 'post', @index.config_name, @index.config_json
|
22
71
|
end
|
23
72
|
|
24
|
-
def
|
25
|
-
|
26
|
-
rescue
|
27
|
-
|
73
|
+
def create
|
74
|
+
create!
|
75
|
+
rescue Waistband::Connection::Error::Resquest => ex
|
76
|
+
raise ex if !ex.index_already_exists? && ! ex.alias_with_name_exists?
|
28
77
|
end
|
29
78
|
|
30
|
-
def
|
31
|
-
|
79
|
+
def destroy
|
80
|
+
destroy!
|
81
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
82
|
+
raise ex unless ex.index_missing?
|
32
83
|
end
|
33
84
|
|
34
|
-
def
|
35
|
-
execute! '
|
85
|
+
def destroy!
|
86
|
+
execute! 'delete', @index.config_name
|
36
87
|
end
|
37
88
|
|
38
|
-
def
|
39
|
-
|
40
|
-
JSON.parse(fetched)['_source'].with_indifferent_access
|
41
|
-
rescue RestClient::ResourceNotFound => ex
|
42
|
-
nil
|
89
|
+
def update_settings!
|
90
|
+
execute! 'put', "#{@index.config_name}/_settings", settings_json
|
43
91
|
end
|
44
92
|
|
45
|
-
def
|
46
|
-
|
93
|
+
def update_settings
|
94
|
+
update_settings!
|
95
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
96
|
+
raise ex unless ex.index_missing?
|
47
97
|
end
|
48
98
|
|
49
|
-
def
|
50
|
-
execute! '
|
99
|
+
def refresh!
|
100
|
+
execute! 'post', "#{@index.config_name}/_refresh", {}
|
51
101
|
end
|
52
102
|
|
53
|
-
def
|
54
|
-
|
103
|
+
def refresh
|
104
|
+
refresh!
|
105
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
106
|
+
raise ex unless ex.index_missing?
|
107
|
+
end
|
108
|
+
|
109
|
+
def alias!(alias_name)
|
110
|
+
alias_name = full_alias_name alias_name
|
111
|
+
fetched = execute! 'put', "#{@index.config_name}/_alias/#{alias_name}"
|
112
|
+
parsed = JSON.parse(fetched)
|
113
|
+
|
114
|
+
error_with('Alias not OK', result: parsed, kind: 'AliasNotOk') unless parsed['ok'] == true
|
115
|
+
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def alias(alias_name)
|
120
|
+
alias! alias_name
|
121
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
122
|
+
raise ex unless ex.alias_not_ok?
|
123
|
+
end
|
124
|
+
|
125
|
+
def fetch_alias(alias_name)
|
126
|
+
alias_name = full_alias_name alias_name
|
127
|
+
fetched = execute! 'get', "#{@index.config_name}/_alias/#{alias_name}"
|
128
|
+
JSON.parse(fetched)
|
129
|
+
end
|
130
|
+
|
131
|
+
def full_alias_name(alias_name)
|
132
|
+
name = alias_name
|
133
|
+
name << "_#{@index.env}" unless @index.custom_name?
|
134
|
+
name
|
135
|
+
end
|
136
|
+
|
137
|
+
def read!(key)
|
138
|
+
fetched = execute! 'get', relative_url_for_key(key)
|
139
|
+
parsed = JSON.parse(fetched)
|
140
|
+
|
141
|
+
error_with('Key not found', result: parsed, kind: 'KeyMissing') if parsed['exists'] == false
|
142
|
+
|
143
|
+
parsed['_source'].with_indifferent_access
|
144
|
+
end
|
145
|
+
|
146
|
+
def read(key)
|
147
|
+
read! key
|
148
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
149
|
+
raise ex unless ex.key_missing?
|
150
|
+
end
|
151
|
+
|
152
|
+
def put(key, data)
|
153
|
+
execute! 'put', relative_url_for_key(key), data.to_json
|
154
|
+
end
|
155
|
+
|
156
|
+
def delete!(key)
|
157
|
+
fetched = execute! 'delete', relative_url_for_key(key)
|
158
|
+
parsed = JSON.parse(fetched)
|
159
|
+
|
160
|
+
error_with('Key not found', result: parsed, kind: 'KeyMissing') if parsed['found'] == false
|
161
|
+
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
def delete(key)
|
166
|
+
delete! key
|
167
|
+
rescue ::Waistband::Connection::Error::Resquest => ex
|
168
|
+
raise ex unless ex.key_missing?
|
169
|
+
end
|
170
|
+
|
171
|
+
def search_url
|
172
|
+
"#{url}/#{@index.config_name}/_search"
|
55
173
|
end
|
56
174
|
|
57
175
|
private
|
@@ -60,7 +178,15 @@ module Waistband
|
|
60
178
|
full_url = "#{url}/#{relative_url}"
|
61
179
|
|
62
180
|
Timeout::timeout ::Waistband.config.timeout do
|
63
|
-
RestClient.send method_name, full_url, data
|
181
|
+
RestClient.send method_name, full_url, data do |response, request, result|
|
182
|
+
response_hash = JSON.parse(response)
|
183
|
+
|
184
|
+
if msg = response_hash['error']
|
185
|
+
error_with(msg, result: result, kind: msg.split('[').first)
|
186
|
+
end
|
187
|
+
|
188
|
+
response
|
189
|
+
end
|
64
190
|
end
|
65
191
|
rescue Timeout::Error, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET
|
66
192
|
# something's wrong, lets blacklist this sucker
|
@@ -68,35 +194,23 @@ module Waistband
|
|
68
194
|
retry if @retry_on_fail
|
69
195
|
end
|
70
196
|
|
71
|
-
def relative_url_for_key(
|
72
|
-
"#{
|
73
|
-
end
|
74
|
-
|
75
|
-
def relative_url_for_index(index)
|
76
|
-
"#{index_name(index)}"
|
197
|
+
def relative_url_for_key(key)
|
198
|
+
"#{@index.config_name}/#{@index.base_name.singularize}/#{key}"
|
77
199
|
end
|
78
200
|
|
79
201
|
def url
|
80
202
|
"#{@server['host']}:#{@server['port']}"
|
81
203
|
end
|
82
204
|
|
83
|
-
def
|
84
|
-
config(
|
85
|
-
end
|
86
|
-
|
87
|
-
def index_json(index)
|
88
|
-
config(index).except('name', 'stringify').to_json
|
205
|
+
def index_json
|
206
|
+
@index.config.except('name', 'stringify').to_json
|
89
207
|
end
|
90
208
|
|
91
|
-
def settings_json
|
92
|
-
settings = config
|
209
|
+
def settings_json
|
210
|
+
settings = @index.config['settings']['index'].except('number_of_shards')
|
93
211
|
{index: settings}.to_json
|
94
212
|
end
|
95
213
|
|
96
|
-
def config(index)
|
97
|
-
Waistband.config.index(index)
|
98
|
-
end
|
99
|
-
|
100
214
|
def blacklist!(server)
|
101
215
|
@blacklist << server['_id'] unless @blacklist.include? server['_id']
|
102
216
|
@blacklist
|
@@ -108,7 +222,7 @@ module Waistband
|
|
108
222
|
@server = next_server
|
109
223
|
|
110
224
|
unless @server
|
111
|
-
raise ::Waistband::Connection::NoMoreServers.new "No available servers remain"
|
225
|
+
raise ::Waistband::Connection::Error::NoMoreServers.new "No available servers remain"
|
112
226
|
end
|
113
227
|
|
114
228
|
@server
|
@@ -119,6 +233,13 @@ module Waistband
|
|
119
233
|
available_servers.sample
|
120
234
|
end
|
121
235
|
|
236
|
+
def error_with(msg, options = {})
|
237
|
+
exception = ::Waistband::Connection::Error::Resquest.new(msg)
|
238
|
+
exception.result = options[:result]
|
239
|
+
exception.kind = options[:kind]
|
240
|
+
raise exception
|
241
|
+
end
|
242
|
+
|
122
243
|
def available_servers
|
123
244
|
::Waistband.config.servers.reject {|server| @blacklist.include? server['_id']}
|
124
245
|
end
|
data/lib/waistband/index.rb
CHANGED
@@ -1,75 +1,92 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
|
1
4
|
module Waistband
|
2
5
|
class Index
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
delegate :create!, :create, :destroy!, :destroy,
|
8
|
+
:update_settings!, :update_settings,
|
9
|
+
:delete!, :delete, :read!, :read,
|
10
|
+
:alias, :alias!,
|
11
|
+
:fetch_alias, :mapping, :exists?,
|
12
|
+
:refresh!, :refresh,
|
13
|
+
:search_url,
|
14
|
+
to: :connection
|
9
15
|
|
10
|
-
|
11
|
-
def create!
|
12
|
-
connection.create! @index
|
13
|
-
end
|
16
|
+
attr_reader :base_name
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
connection.destroy! @index
|
18
|
-
end
|
18
|
+
def initialize(index, options = {})
|
19
|
+
options = options.stringify_keys
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
@index = index
|
22
|
+
@base_name = index
|
23
|
+
@stringify = config['stringify']
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
connection.refresh @index
|
25
|
+
@subs = [options['subs']] if options['subs'].present?
|
26
|
+
@subs = @subs.flatten if @subs.is_a?(Array)
|
27
27
|
end
|
28
28
|
|
29
29
|
def store!(key, data)
|
30
30
|
# map everything to strings
|
31
31
|
if @stringify
|
32
32
|
original_data = data
|
33
|
-
|
34
|
-
if data.is_a? Array
|
35
|
-
data = ::Waistband::StringifiedArray.new data
|
36
|
-
elsif data.is_a? Hash
|
37
|
-
data = ::Waistband::StringifiedHash.new_from data
|
38
|
-
end
|
39
|
-
|
40
|
-
data = data.stringify_all if data.respond_to? :stringify_all
|
33
|
+
data = stringify_all data
|
41
34
|
end
|
42
35
|
|
43
|
-
result
|
44
|
-
data
|
36
|
+
result = connection.put key, data
|
37
|
+
data = original_data if @stringify
|
45
38
|
|
46
39
|
result
|
47
40
|
end
|
48
41
|
|
49
|
-
def
|
50
|
-
|
42
|
+
def query(options = {})
|
43
|
+
::Waistband::Query.new self, options
|
51
44
|
end
|
52
45
|
|
53
|
-
def
|
54
|
-
|
46
|
+
def name
|
47
|
+
@subs ? "#{@index}__#{@subs.join('_')}" : @index
|
55
48
|
end
|
56
49
|
|
57
|
-
def
|
58
|
-
|
50
|
+
def config_name
|
51
|
+
@subs ? "#{base_config_name}__#{@subs.join('_')}" : base_config_name
|
52
|
+
end
|
53
|
+
|
54
|
+
def config_json
|
55
|
+
config.except('name', 'stringify').to_json
|
56
|
+
end
|
57
|
+
|
58
|
+
def base_config_name
|
59
|
+
return config['name'] if config['name']
|
60
|
+
"#{@base_name}_#{env}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def custom_name?
|
64
|
+
!!config['name']
|
65
|
+
end
|
66
|
+
|
67
|
+
def config
|
68
|
+
Waistband.config.index @index
|
59
69
|
end
|
60
70
|
|
61
|
-
def
|
62
|
-
|
71
|
+
def env
|
72
|
+
Waistband.config.env
|
63
73
|
end
|
64
74
|
|
65
75
|
private
|
66
76
|
|
67
|
-
def
|
68
|
-
|
77
|
+
def stringify_all(data)
|
78
|
+
data = if data.is_a? Array
|
79
|
+
::Waistband::StringifiedArray.new data
|
80
|
+
elsif data.is_a? Hash
|
81
|
+
::Waistband::StringifiedHash.new_from data
|
82
|
+
end
|
83
|
+
|
84
|
+
data = data.stringify_all if data.respond_to? :stringify_all
|
85
|
+
data
|
69
86
|
end
|
70
87
|
|
71
|
-
def
|
72
|
-
Waistband.
|
88
|
+
def connection
|
89
|
+
::Waistband::Connection.new self
|
73
90
|
end
|
74
91
|
|
75
92
|
# /private
|
data/lib/waistband/query.rb
CHANGED
data/lib/waistband/version.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
development: &DEV
|
2
|
-
name: waistband_geo
|
3
2
|
stringify: false
|
4
3
|
settings:
|
5
4
|
index:
|
6
|
-
number_of_shards:
|
5
|
+
number_of_shards: 1
|
6
|
+
number_of_replicas: 1
|
7
7
|
mappings:
|
8
8
|
geo:
|
9
9
|
_source:
|
@@ -16,4 +16,3 @@ development: &DEV
|
|
16
16
|
|
17
17
|
test:
|
18
18
|
<<: *DEV
|
19
|
-
name: waistband_geo_test
|
@@ -1,9 +1,9 @@
|
|
1
1
|
development: &DEV
|
2
|
-
name: search
|
3
2
|
stringify: false
|
4
3
|
settings:
|
5
4
|
index:
|
6
|
-
number_of_shards:
|
5
|
+
number_of_shards: 1
|
6
|
+
number_of_replicas: 1
|
7
7
|
mappings:
|
8
8
|
event:
|
9
9
|
_source:
|
@@ -11,4 +11,3 @@ development: &DEV
|
|
11
11
|
|
12
12
|
test:
|
13
13
|
<<: *DEV
|
14
|
-
name: search_test
|
@@ -12,14 +12,12 @@ describe Waistband::Configuration do
|
|
12
12
|
|
13
13
|
it "loads indexes config" do
|
14
14
|
config.index('search').should be_a Hash
|
15
|
-
config.index('search')['
|
16
|
-
config.index('search')['settings']['index']['number_of_shards'].should eql 4
|
15
|
+
config.index('search')['settings']['index']['number_of_shards'].should eql 1
|
17
16
|
end
|
18
17
|
|
19
18
|
it "loads multiple indexes config" do
|
20
19
|
config.index('events').should be_a Hash
|
21
|
-
config.index('events')['
|
22
|
-
config.index('events')['settings']['index']['number_of_shards'].should eql 4
|
20
|
+
config.index('events')['settings']['index']['number_of_shards'].should eql 1
|
23
21
|
end
|
24
22
|
|
25
23
|
describe '#servers' do
|
data/spec/lib/connection_spec.rb
CHANGED
@@ -2,24 +2,227 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Waistband::Connection do
|
4
4
|
|
5
|
-
let(:connection) { Waistband::Connection.new }
|
5
|
+
let(:connection) { Waistband::Connection.new(index) }
|
6
|
+
let(:search_connection) { Waistband::Connection.new(index_search) }
|
7
|
+
let(:index) { ::Waistband::Index.new('events') }
|
8
|
+
let(:index_search) { ::Waistband::Index.new('search') }
|
6
9
|
|
7
10
|
def blacklist_server!
|
8
11
|
connection.send(:blacklist!, Waistband.config.servers.first)
|
9
12
|
end
|
10
13
|
|
11
14
|
it "constructs the settings json" do
|
12
|
-
connection.send(:settings_json
|
15
|
+
connection.send(:settings_json).should eql '{"index":{"number_of_replicas":1}}'
|
13
16
|
end
|
14
17
|
|
15
18
|
it "constructs the index json" do
|
16
|
-
connection.send(:index_json
|
19
|
+
connection.send(:index_json).should eql '{"settings":{"index":{"number_of_shards":1,"number_of_replicas":1}},"mappings":{"event":{"_source":{"includes":["*"]}}}}'
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'urls for subindexes' do
|
23
|
+
|
24
|
+
let(:index) { ::Waistband::Index.new('events', subs: %w(2013 01)) }
|
25
|
+
let(:connection) { Waistband::Connection.new(index, orderly: true) }
|
26
|
+
|
27
|
+
describe '#destroy!' do
|
28
|
+
|
29
|
+
it "targets the correct url" do
|
30
|
+
RestClient.should_receive(:send).with('delete', "http://localhost:9200/events_test__2013_01", nil).once
|
31
|
+
connection.destroy!
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#create!' do
|
37
|
+
|
38
|
+
it "targets the correct url" do
|
39
|
+
RestClient.should_receive(:send).with('post', "http://localhost:9200/events_test__2013_01", index.config_json).once
|
40
|
+
connection.create!
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#update_settings!' do
|
46
|
+
|
47
|
+
it "targets the correct url" do
|
48
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test__2013_01/_settings", connection.send(:settings_json)).once
|
49
|
+
connection.update_settings!
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#refresh!' do
|
55
|
+
|
56
|
+
it "targets the correct url" do
|
57
|
+
RestClient.should_receive(:send).with('post', "http://localhost:9200/events_test__2013_01/_refresh", {}).once
|
58
|
+
connection.refresh!
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#read' do
|
64
|
+
|
65
|
+
it "targets the correct url" do
|
66
|
+
RestClient.should_receive(:send).with('get', "http://localhost:9200/events_test__2013_01/event/my_key", nil).once.and_call_original
|
67
|
+
connection.read 'my_key'
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#put' do
|
73
|
+
|
74
|
+
it "targets the correct url" do
|
75
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test__2013_01/event/my_key", {oh_yeah: true}.to_json).once.and_call_original
|
76
|
+
connection.put 'my_key', {oh_yeah: true}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#delete!' do
|
82
|
+
|
83
|
+
it "targets the correct url" do
|
84
|
+
RestClient.should_receive(:send).with('delete', "http://localhost:9200/events_test__2013_01/event/my_key", nil).once.and_call_original
|
85
|
+
connection.delete 'my_key'
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#search_url' do
|
91
|
+
|
92
|
+
it "targets the correct url" do
|
93
|
+
connection.search_url.should eql "http://localhost:9200/events_test__2013_01/_search"
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#alias!' do
|
99
|
+
|
100
|
+
it "targets the correct url with a specific alias_name" do
|
101
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test__2013_01/_alias/all_events_test", nil).once.and_call_original
|
102
|
+
connection.alias! 'all_events'
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#full_alias_name' do
|
110
|
+
|
111
|
+
it "returns the alias name with the env" do
|
112
|
+
connection.full_alias_name('all_events').should eql 'all_events_test'
|
113
|
+
end
|
114
|
+
|
115
|
+
it "if the index has a custom name, the alias name doesn't automatically append the env" do
|
116
|
+
connection.instance_variable_get('@index').stub(:config).and_return({
|
117
|
+
'name' => 'super_custom'
|
118
|
+
})
|
119
|
+
connection.full_alias_name('all_events').should eql 'all_events'
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'urls' do
|
125
|
+
|
126
|
+
let(:connection) { Waistband::Connection.new(index, orderly: true) }
|
127
|
+
|
128
|
+
describe '#destroy!' do
|
129
|
+
|
130
|
+
it "targets the correct url" do
|
131
|
+
RestClient.should_receive(:send).with('delete', "http://localhost:9200/events_test", nil).once
|
132
|
+
connection.destroy!
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#create!' do
|
138
|
+
|
139
|
+
it "targets the correct url" do
|
140
|
+
RestClient.should_receive(:send).with('post', "http://localhost:9200/events_test", index.config_json).once
|
141
|
+
connection.create!
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#update_settings!' do
|
147
|
+
|
148
|
+
it "targets the correct url" do
|
149
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test/_settings", connection.send(:settings_json)).once
|
150
|
+
connection.update_settings!
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#refresh!' do
|
156
|
+
|
157
|
+
it "targets the correct url" do
|
158
|
+
RestClient.should_receive(:send).with('post', "http://localhost:9200/events_test/_refresh", {}).once
|
159
|
+
connection.refresh!
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#read' do
|
165
|
+
|
166
|
+
it "targets the correct url" do
|
167
|
+
RestClient.should_receive(:send).with('get', "http://localhost:9200/events_test/event/my_key", nil).once.and_call_original
|
168
|
+
connection.read 'my_key'
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#put' do
|
174
|
+
|
175
|
+
it "targets the correct url" do
|
176
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test/event/my_key", {oh_yeah: true}.to_json).once.and_call_original
|
177
|
+
connection.put 'my_key', {oh_yeah: true}
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
describe '#delete!' do
|
183
|
+
|
184
|
+
it "targets the correct url" do
|
185
|
+
connection.put 'my_key', {oh_yeah: true}
|
186
|
+
|
187
|
+
RestClient.should_receive(:send).with('delete', "http://localhost:9200/events_test/event/my_key", nil).once.and_call_original
|
188
|
+
connection.delete! 'my_key'
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#search_url' do
|
194
|
+
|
195
|
+
it "targets the correct url" do
|
196
|
+
connection.search_url.should eql "http://localhost:9200/events_test/_search"
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#alias!' do
|
202
|
+
|
203
|
+
it "targets the correct url with a specific alias_name" do
|
204
|
+
RestClient.should_receive(:send).with('put', "http://localhost:9200/events_test/_alias/all_events_test", nil).once.and_call_original
|
205
|
+
connection.alias! 'all_events'
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#alias!' do
|
213
|
+
|
214
|
+
it "creates an alias for the index" do
|
215
|
+
connection.alias! 'all_events'
|
216
|
+
aliases = connection.fetch_alias 'all_events'
|
217
|
+
aliases.should eql({"events_test"=>{"aliases"=>{"all_events_test"=>{}}}})
|
218
|
+
end
|
219
|
+
|
17
220
|
end
|
18
221
|
|
19
222
|
describe '#execute!' do
|
20
223
|
|
21
224
|
it "wraps directly to rest client" do
|
22
|
-
connection = Waistband::Connection.new(orderly: true)
|
225
|
+
connection = Waistband::Connection.new(index, orderly: true)
|
23
226
|
|
24
227
|
RestClient.should_receive(:get).with('http://localhost:9200/somekey', nil).once
|
25
228
|
connection.send(:execute!, 'get', 'somekey')
|
@@ -29,7 +232,7 @@ describe Waistband::Connection do
|
|
29
232
|
|
30
233
|
[Timeout::Error, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET].each do |exception|
|
31
234
|
it "blacklists the server when #{exception}" do
|
32
|
-
connection = Waistband::Connection.new(retry_on_fail: false)
|
235
|
+
connection = Waistband::Connection.new(index, retry_on_fail: false)
|
33
236
|
|
34
237
|
RestClient.should_receive(:get).with(kind_of(String), nil).and_raise exception
|
35
238
|
connection.send(:execute!, 'get', 'somekey')
|
@@ -54,8 +257,8 @@ describe Waistband::Connection do
|
|
54
257
|
]
|
55
258
|
)
|
56
259
|
|
57
|
-
connection = Waistband::Connection.new(orderly: true)
|
58
|
-
expect { connection.refresh
|
260
|
+
connection = Waistband::Connection.new(index, orderly: true)
|
261
|
+
expect { connection.refresh }.to_not raise_error
|
59
262
|
|
60
263
|
connection.instance_variable_get('@blacklist').should eql ['567890a5ce74182e5cd123e299993ab510c56123']
|
61
264
|
end
|
@@ -67,7 +270,7 @@ describe Waistband::Connection do
|
|
67
270
|
expect {
|
68
271
|
connection.send(:execute!, 'get', 'somekey')
|
69
272
|
}.to raise_error(
|
70
|
-
Waistband::Connection::NoMoreServers,
|
273
|
+
Waistband::Connection::Error::NoMoreServers,
|
71
274
|
"No available servers remain"
|
72
275
|
)
|
73
276
|
end
|
@@ -79,27 +282,15 @@ describe Waistband::Connection do
|
|
79
282
|
describe '#relative_url_for_key' do
|
80
283
|
|
81
284
|
it "returns the relative url for a key" do
|
82
|
-
url =
|
285
|
+
url = search_connection.send(:relative_url_for_key, 'key123')
|
83
286
|
url.should match /^search_test\/search\/key123$/
|
84
287
|
|
85
|
-
url = connection.send(:relative_url_for_key, '
|
288
|
+
url = connection.send(:relative_url_for_key, '9986')
|
86
289
|
url.should match /^events_test\/event\/9986$/
|
87
290
|
end
|
88
291
|
|
89
292
|
end
|
90
293
|
|
91
|
-
describe '#relative_url_for_index' do
|
92
|
-
|
93
|
-
it "returns the url for an index" do
|
94
|
-
url = connection.send(:relative_url_for_index, 'search')
|
95
|
-
url.should match /^search_test$/
|
96
|
-
|
97
|
-
url = connection.send(:relative_url_for_index, 'events')
|
98
|
-
url.should match /^events_test$/
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
294
|
describe '#url' do
|
104
295
|
|
105
296
|
it "returns url string for the selected server" do
|
@@ -134,7 +325,7 @@ describe Waistband::Connection do
|
|
134
325
|
expect {
|
135
326
|
connection.send(:blacklist!, Waistband.config.servers.last)
|
136
327
|
}.to raise_error(
|
137
|
-
Waistband::Connection::NoMoreServers,
|
328
|
+
Waistband::Connection::Error::NoMoreServers,
|
138
329
|
"No available servers remain"
|
139
330
|
)
|
140
331
|
end
|
@@ -188,38 +379,38 @@ describe Waistband::Connection do
|
|
188
379
|
|
189
380
|
describe "storing" do
|
190
381
|
|
191
|
-
let(:
|
192
|
-
let(:index2) { ::Waistband::Index.new('search') }
|
193
|
-
let(:attrs) { {'ok' => {'yeah' => true}} }
|
194
|
-
|
195
|
-
before { IndexHelper.prepare! }
|
382
|
+
let(:attrs) { {'other_ok' => {'yeah' => true}} }
|
196
383
|
|
197
384
|
it "stores data" do
|
198
|
-
connection.put('
|
385
|
+
connection.put('__test_write', {'ok' => 'yeah'})
|
199
386
|
index.read('__test_write').should eql({'ok' => 'yeah'})
|
200
387
|
end
|
201
388
|
|
202
389
|
it "data is indirectly accessible" do
|
203
|
-
connection.put('
|
204
|
-
index.read('__test_not_string')[:
|
390
|
+
connection.put('__test_not_string', attrs)
|
391
|
+
index.read('__test_not_string')[:other_ok][:yeah].should eql true
|
205
392
|
end
|
206
393
|
|
207
394
|
it "deletes data" do
|
208
|
-
connection.put('
|
209
|
-
connection.delete!('
|
395
|
+
connection.put('__test_write', attrs)
|
396
|
+
connection.delete!('__test_write')
|
210
397
|
index.read('__test_write').should be_nil
|
211
398
|
end
|
212
399
|
|
400
|
+
it "blows up when trying to delete non-existent data" do
|
401
|
+
expect { connection.delete!('__test_write') }.to raise_error
|
402
|
+
end
|
403
|
+
|
213
404
|
it "returns nil on 404" do
|
214
405
|
index.read('__not_here').should be_nil
|
215
406
|
end
|
216
407
|
|
217
408
|
it "doesn't mix data between two indexes" do
|
218
|
-
connection.put('
|
219
|
-
|
409
|
+
connection.put('__test_write', {'data' => 'index_1'})
|
410
|
+
search_connection.put('__test_write', {'data' => 'index_2'})
|
220
411
|
|
221
412
|
index.read('__test_write').should eql({'data' => 'index_1'})
|
222
|
-
|
413
|
+
index_search.read('__test_write').should eql({'data' => 'index_2'})
|
223
414
|
end
|
224
415
|
|
225
416
|
end
|
data/spec/lib/index_spec.rb
CHANGED
@@ -7,21 +7,20 @@ describe Waistband::Index do
|
|
7
7
|
let(:attrs) { {'ok' => {'yeah' => true}} }
|
8
8
|
|
9
9
|
it "initializes values" do
|
10
|
-
index.instance_variable_get('@index_name').should eql 'events_test'
|
11
10
|
index.instance_variable_get('@stringify').should eql true
|
12
11
|
end
|
13
12
|
|
14
13
|
it "creates the index" do
|
15
14
|
index.destroy!
|
16
|
-
expect{ index.refresh }.to raise_error(
|
15
|
+
expect{ index.refresh! }.to raise_error(Waistband::Connection::Error::Resquest)
|
17
16
|
|
18
17
|
index.create!
|
19
|
-
expect{ index.refresh }.to_not raise_error
|
18
|
+
expect{ index.refresh! }.to_not raise_error
|
20
19
|
end
|
21
20
|
|
22
21
|
it "destroys the index" do
|
23
22
|
index.destroy!
|
24
|
-
expect{ index.refresh }.to raise_error(
|
23
|
+
expect{ index.refresh! }.to raise_error(Waistband::Connection::Error::Resquest)
|
25
24
|
index.create!
|
26
25
|
end
|
27
26
|
|
@@ -72,4 +71,55 @@ describe Waistband::Index do
|
|
72
71
|
|
73
72
|
end
|
74
73
|
|
74
|
+
describe 'subindexes' do
|
75
|
+
|
76
|
+
let(:sharded_index) { Waistband::Index.new('events', subs: %w(2013 01)) }
|
77
|
+
|
78
|
+
it "permits subbing the index" do
|
79
|
+
sharded_index.name.should eql 'events__2013_01'
|
80
|
+
sharded_index.config_name.should eql 'events_test__2013_01'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "permits sharding into singles" do
|
84
|
+
index = Waistband::Index.new 'events', subs: '2013'
|
85
|
+
index.name.should eql 'events__2013'
|
86
|
+
index.config_name.should eql 'events_test__2013'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "retains the same options from the parent index config" do
|
90
|
+
config = sharded_index.send(:config)
|
91
|
+
|
92
|
+
sharded_index.base_config_name.should eql 'events_test'
|
93
|
+
config['stringify'].should be_true
|
94
|
+
config['settings'].should be_present
|
95
|
+
end
|
96
|
+
|
97
|
+
it "creates the sharded index with the same mappings as the parent" do
|
98
|
+
sharded_index.destroy
|
99
|
+
|
100
|
+
expect {
|
101
|
+
sharded_index.create!
|
102
|
+
}.to_not raise_error
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'no configged index name' do
|
106
|
+
|
107
|
+
it "gets a default name that makes sense for the index when not defined" do
|
108
|
+
index = Waistband::Index.new 'events_no_name', subs: %w(2013 01)
|
109
|
+
index.config_name.should eql 'events_no_name_test__2013_01'
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#base_config_name' do
|
117
|
+
|
118
|
+
it "gets a default name that makes sense for the index when not defined" do
|
119
|
+
index = Waistband::Index.new 'events_no_name'
|
120
|
+
index.base_config_name.should eql 'events_no_name_test'
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
75
125
|
end
|
data/spec/lib/query_spec.rb
CHANGED
@@ -5,8 +5,8 @@ describe Waistband::Query do
|
|
5
5
|
let(:index) { Waistband::Index.new('search') }
|
6
6
|
let(:geo_index) { Waistband::Index.new('geo') }
|
7
7
|
|
8
|
-
let(:query) { Waistband::Query.new(
|
9
|
-
let(:geo_query) { Waistband::Query.new(
|
8
|
+
let(:query) { Waistband::Query.new(index) }
|
9
|
+
let(:geo_query) { Waistband::Query.new(geo_index) }
|
10
10
|
|
11
11
|
let(:attrs) do
|
12
12
|
{
|
data/spec/spec_helper.rb
CHANGED
@@ -4,21 +4,20 @@ class IndexHelper
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
|
7
|
-
def destroy_all
|
7
|
+
def destroy_all
|
8
8
|
IndexHelper::INDEXES.each do |index|
|
9
|
-
Waistband::Index.new(index).destroy
|
9
|
+
Waistband::Index.new(index).destroy
|
10
10
|
end
|
11
|
+
|
12
|
+
Waistband::Index.new('events', subs: %w(2013 01)).destroy
|
11
13
|
end
|
12
14
|
|
13
|
-
def create_all
|
15
|
+
def create_all
|
14
16
|
IndexHelper::INDEXES.each do |index|
|
15
|
-
Waistband::Index.new(index).create
|
17
|
+
Waistband::Index.new(index).create
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
destroy_all!
|
21
|
-
create_all!
|
20
|
+
Waistband::Index.new('events', subs: %w(2013 01)).create
|
22
21
|
end
|
23
22
|
|
24
23
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waistband
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-02-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- lib/waistband/version.rb
|
119
119
|
- spec/config/waistband/waistband.yml
|
120
120
|
- spec/config/waistband/waistband_events.yml
|
121
|
+
- spec/config/waistband/waistband_events_no_name.yml
|
121
122
|
- spec/config/waistband/waistband_geo.yml
|
122
123
|
- spec/config/waistband/waistband_search.yml
|
123
124
|
- spec/lib/configuration_spec.rb
|
@@ -158,6 +159,7 @@ summary: Elastic Search all the things!
|
|
158
159
|
test_files:
|
159
160
|
- spec/config/waistband/waistband.yml
|
160
161
|
- spec/config/waistband/waistband_events.yml
|
162
|
+
- spec/config/waistband/waistband_events_no_name.yml
|
161
163
|
- spec/config/waistband/waistband_geo.yml
|
162
164
|
- spec/config/waistband/waistband_search.yml
|
163
165
|
- spec/lib/configuration_spec.rb
|