elastics-client 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/VERSION +1 -0
- data/elastics-client.gemspec +33 -0
- data/lib/elastics.rb +108 -0
- data/lib/elastics/api_stubs.rb +1268 -0
- data/lib/elastics/api_templates/cluster_api.yml +94 -0
- data/lib/elastics/api_templates/core_api.yml +202 -0
- data/lib/elastics/api_templates/indices_api.yml +304 -0
- data/lib/elastics/class_proxy/base.rb +29 -0
- data/lib/elastics/class_proxy/templates.rb +97 -0
- data/lib/elastics/class_proxy/templates/doc.rb +91 -0
- data/lib/elastics/class_proxy/templates/search.rb +72 -0
- data/lib/elastics/configuration.rb +25 -0
- data/lib/elastics/deprecation.rb +153 -0
- data/lib/elastics/errors.rb +43 -0
- data/lib/elastics/http_clients/base.rb +15 -0
- data/lib/elastics/http_clients/loader.rb +51 -0
- data/lib/elastics/http_clients/patron.rb +29 -0
- data/lib/elastics/http_clients/rest_client.rb +36 -0
- data/lib/elastics/logger.rb +37 -0
- data/lib/elastics/prog_bar.rb +39 -0
- data/lib/elastics/rails.rb +1 -0
- data/lib/elastics/result.rb +24 -0
- data/lib/elastics/result/bulk.rb +20 -0
- data/lib/elastics/result/document.rb +67 -0
- data/lib/elastics/result/multi_get.rb +24 -0
- data/lib/elastics/result/search.rb +28 -0
- data/lib/elastics/struct/array.rb +25 -0
- data/lib/elastics/struct/hash.rb +105 -0
- data/lib/elastics/struct/paginable.rb +58 -0
- data/lib/elastics/struct/prunable.rb +60 -0
- data/lib/elastics/struct/symbolize.rb +27 -0
- data/lib/elastics/tasks.rb +62 -0
- data/lib/elastics/template.rb +124 -0
- data/lib/elastics/template/common.rb +42 -0
- data/lib/elastics/template/logger.rb +66 -0
- data/lib/elastics/template/partial.rb +28 -0
- data/lib/elastics/template/search.rb +30 -0
- data/lib/elastics/template/slim_search.rb +13 -0
- data/lib/elastics/template/tags.rb +56 -0
- data/lib/elastics/templates.rb +20 -0
- data/lib/elastics/utility_methods.rb +131 -0
- data/lib/elastics/utils.rb +103 -0
- data/lib/elastics/variables.rb +62 -0
- data/lib/tasks.rake +28 -0
- metadata +148 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Struct
|
3
|
+
class Hash < ::Hash
|
4
|
+
include Symbolize
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super do |hash, key|
|
8
|
+
if key[-1] == '!'
|
9
|
+
klass = (key[0] == '_' ? Array : Hash)
|
10
|
+
hash[clean_key(key)] = klass.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def merge(hash)
|
16
|
+
super symbolize(hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge!(hash)
|
20
|
+
super symbolize(hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def store(key, val)
|
24
|
+
if key[-1] == '='
|
25
|
+
super key[0..-2].to_sym, val.extend(AsIs)
|
26
|
+
else
|
27
|
+
super clean_key(key), symbolize(val)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias_method :[]=, :store
|
31
|
+
|
32
|
+
def fetch(key, *rest, &block)
|
33
|
+
cleaned = clean_key(key)
|
34
|
+
super has_key?(cleaned) ? cleaned : key.to_sym, *rest, &block
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](key)
|
38
|
+
cleaned = clean_key(key)
|
39
|
+
super has_key?(cleaned) ? cleaned : key.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def deep_merge(*hashes)
|
43
|
+
dupe = deep_dup(self)
|
44
|
+
hashes.each {|h2| dupe.replace(deep_merge_hash(dupe,h2))}
|
45
|
+
dupe
|
46
|
+
end
|
47
|
+
|
48
|
+
def deep_merge!(*hashes)
|
49
|
+
replace deep_merge(*hashes)
|
50
|
+
end
|
51
|
+
|
52
|
+
module Nil
|
53
|
+
def method_missing(*)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def try(key)
|
59
|
+
has_key?(key) ? self[key] : nil.extend(Nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
def try_delete(key, *rest, &block)
|
63
|
+
val = delete clean_key(key), *rest, &block
|
64
|
+
val.nil? ? nil.extend(Nil) : val
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def clean_key(key)
|
71
|
+
key[-1] == '!' ? key[0..-2].to_sym : key.to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
def deep_merge_hash(h1, h2)
|
75
|
+
h2 ||= {}
|
76
|
+
h1.merge(h2) do |key, oldval, newval|
|
77
|
+
case
|
78
|
+
when oldval.is_a?(Hash) && newval.is_a?(Hash)
|
79
|
+
deep_merge_hash(oldval, newval)
|
80
|
+
when oldval.is_a?(Array) && newval.is_a?(Array)
|
81
|
+
oldval | newval
|
82
|
+
else
|
83
|
+
newval
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def deep_dup(obj)
|
89
|
+
case obj
|
90
|
+
when ::Hash, Elastics::Struct::Hash
|
91
|
+
h = obj.dup
|
92
|
+
h.each_pair do |k,v|
|
93
|
+
h[k] = deep_dup(v)
|
94
|
+
end
|
95
|
+
h
|
96
|
+
when ::Array, Elastics::Struct::Array
|
97
|
+
obj.map{|i| deep_dup(i)}
|
98
|
+
else
|
99
|
+
obj
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Struct
|
3
|
+
module Paginable
|
4
|
+
|
5
|
+
attr_accessor :total_entries, :variables
|
6
|
+
|
7
|
+
def setup(total_entries, variables)
|
8
|
+
@total_entries = total_entries
|
9
|
+
@variables = variables
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def per_page
|
14
|
+
(@variables[:per_page] || @variables[:limit_value] ||
|
15
|
+
@variables[:params] && @variables[:params][:size] || 10).to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def total_pages
|
19
|
+
( @total_entries.to_f / per_page ).ceil
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_page
|
23
|
+
(@variables[:page] || @variables[:current_page] || 1).to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def previous_page
|
27
|
+
current_page > 1 ? (current_page - 1) : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def next_page
|
31
|
+
current_page < total_pages ? (current_page + 1) : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def last_page?
|
35
|
+
total_pages == current_page
|
36
|
+
end
|
37
|
+
|
38
|
+
def first_page?
|
39
|
+
current_page == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def offset
|
43
|
+
per_page * (current_page - 1)
|
44
|
+
end
|
45
|
+
|
46
|
+
def out_of_bounds?
|
47
|
+
current_page > total_pages
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :limit_value, :per_page
|
51
|
+
alias_method :total_count, :total_entries
|
52
|
+
alias_method :num_pages, :total_pages
|
53
|
+
alias_method :offset_value, :offset
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Struct
|
3
|
+
module Prunable
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
VALUES = [ nil, '', {}, [], false ]
|
8
|
+
|
9
|
+
class Value
|
10
|
+
class << self
|
11
|
+
def to_s; '' end
|
12
|
+
alias_method :===, :==
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def prune_blanks(obj)
|
17
|
+
prune(obj, *VALUES) || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# prunes the branch when the leaf is Prunable
|
21
|
+
# and compact.flatten the Array values
|
22
|
+
# values are the prunable values, like VALUES or Prunable::Value,
|
23
|
+
# or any arbitrary value
|
24
|
+
def prune(obj, *values)
|
25
|
+
case
|
26
|
+
when values.include?(obj)
|
27
|
+
obj
|
28
|
+
when obj.is_a?(::Array)
|
29
|
+
return obj if obj.empty?
|
30
|
+
ar = []
|
31
|
+
obj.each do |i|
|
32
|
+
pruned = prune(i, *values)
|
33
|
+
next if values.include?(pruned)
|
34
|
+
ar << pruned
|
35
|
+
end
|
36
|
+
a = ar.compact.flatten
|
37
|
+
a.empty? ? values.first : a
|
38
|
+
when obj.is_a?(::Hash)
|
39
|
+
return obj if obj.empty?
|
40
|
+
h = {}
|
41
|
+
obj.each do |k, v|
|
42
|
+
pruned = prune(v, *values)
|
43
|
+
next if values.include?(pruned)
|
44
|
+
# when a key is prunable merges the value if it is a hash (allows merging of partials)
|
45
|
+
if VALUES.include?(k)
|
46
|
+
h.merge!(pruned) if pruned.is_a?(::Hash)
|
47
|
+
else
|
48
|
+
h[k] = pruned
|
49
|
+
end
|
50
|
+
end
|
51
|
+
h.empty? ? values.first : h
|
52
|
+
else
|
53
|
+
obj
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
Prunable = Struct::Prunable
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Struct
|
3
|
+
module AsIs end
|
4
|
+
module Symbolize
|
5
|
+
|
6
|
+
def symbolize(obj)
|
7
|
+
case obj
|
8
|
+
when Elastics::Struct::Hash, Elastics::Struct::Array, Elastics::Struct::AsIs
|
9
|
+
obj
|
10
|
+
when ::Hash
|
11
|
+
h = Struct::Hash.new
|
12
|
+
obj.each do |k,v|
|
13
|
+
h[k.to_sym] = symbolize(v)
|
14
|
+
end
|
15
|
+
h
|
16
|
+
when ::Array
|
17
|
+
a = Struct::Array.new
|
18
|
+
obj.each{|i| a << i}
|
19
|
+
a
|
20
|
+
else
|
21
|
+
obj
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Tasks
|
3
|
+
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(overrides={})
|
7
|
+
options = Elastics::Utils.env2options *default_options.keys
|
8
|
+
options[:index] = options[:index].split(',') if options[:index]
|
9
|
+
@options = default_options.merge(options).merge(overrides)
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_options
|
13
|
+
@default_options ||= { :force => false,
|
14
|
+
:index => Conf.variables[:index],
|
15
|
+
:config_file => Conf.config_file }
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_indices
|
19
|
+
indices.each do |index|
|
20
|
+
delete_index(index) if options[:force]
|
21
|
+
raise ExistingIndexError, "#{index.inspect} already exists. Please use FORCE=1 if you want to delete it first." \
|
22
|
+
if exist?(index)
|
23
|
+
create(index)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete_indices
|
28
|
+
indices.each { |index| delete_index(index) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def config_hash
|
32
|
+
@config_hash ||= ( hash = YAML.load(Utils.erb_process(config_path))
|
33
|
+
Utils.delete_allcaps_keys(hash) )
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def indices
|
39
|
+
i = options[:index] || config_hash.keys
|
40
|
+
i.is_a?(Array) ? i : [i]
|
41
|
+
end
|
42
|
+
|
43
|
+
def exist?(index)
|
44
|
+
Elastics.exist?(:index => index)
|
45
|
+
end
|
46
|
+
|
47
|
+
def config_path
|
48
|
+
@config_path ||= options[:config_file] || Conf.config_file
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_index(index)
|
52
|
+
Elastics.delete_index(:index => index) if exist?(index)
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(index)
|
56
|
+
config_hash[index] = {} unless config_hash.has_key?(index)
|
57
|
+
Elastics.POST "/#{index}", config_hash[index]
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Elastics
|
2
|
+
|
3
|
+
# Generic Elastics::Template object.
|
4
|
+
# This class represents a generic Elastics::Template object.
|
5
|
+
# It is used as the base class by all the more specific Elastics::Template::* classes.
|
6
|
+
# You usually don't need to instantiate this class manually, since it is usually used internally.
|
7
|
+
# For more details about templates, see the documentation.
|
8
|
+
class Template
|
9
|
+
|
10
|
+
include Logger
|
11
|
+
include Common
|
12
|
+
|
13
|
+
def self.variables
|
14
|
+
Vars.new
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :method, :path
|
18
|
+
|
19
|
+
def initialize(method, path, data=nil, *vars)
|
20
|
+
@method = method.to_s.upcase
|
21
|
+
raise ArgumentError, "#@method method not supported" \
|
22
|
+
unless %w[HEAD GET PUT POST DELETE].include?(@method)
|
23
|
+
@path = path =~ /^\// ? path : "/#{path}"
|
24
|
+
@data = Utils.parse_source(data)
|
25
|
+
@instance_vars = Vars.new(*vars)
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(*vars)
|
29
|
+
do_render(*vars) do |response, int|
|
30
|
+
Result.new(self, int[:vars], response).to_elastics_result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_a(*vars)
|
35
|
+
vars = Vars.new(*vars)
|
36
|
+
int = interpolate(vars)
|
37
|
+
a = [method, int[:path], Utils.keyfy(:to_s, int[:data]), Utils.keyfy(:to_s, @instance_vars)]
|
38
|
+
2.times { a.pop if a.last.nil? || a.last.empty? }
|
39
|
+
a
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_source
|
43
|
+
{@name.to_s => to_a}.to_yaml
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def do_render(*vars)
|
50
|
+
vars = Vars.new(*vars)
|
51
|
+
int, path, encoded_data, response = try_clean_and_retry(vars)
|
52
|
+
return response.status == 200 if method == 'HEAD' # used in Elastics.exist?
|
53
|
+
if Conf.http_client.raise_proc.call(response)
|
54
|
+
int[:vars][:raise].is_a?(FalseClass) ? return : raise(HttpError.new(response, caller_line))
|
55
|
+
end
|
56
|
+
result = yield(response, int)
|
57
|
+
ensure
|
58
|
+
log_render(int, path, encoded_data, result)
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
# This allows to use Lucene style search language in the :cleanable_query declared variable and
|
63
|
+
# in case of a syntax error it will remove all the problematic characters and retry with a cleaned query_string
|
64
|
+
# http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/queryparsersyntax.html
|
65
|
+
def try_clean_and_retry(vars)
|
66
|
+
response_vars = request(vars)
|
67
|
+
if !Prunable::VALUES.include?(vars[:cleanable_query]) && Conf.http_client.raise_proc.call(response_vars[3])
|
68
|
+
e = HttpError.new(response_vars[3], caller_line)
|
69
|
+
e.to_hash['error'] =~ /^SearchPhaseExecutionException/
|
70
|
+
(vars[:cleanable_query].is_a?(String) ? vars[:cleanable_query] : vars[:cleanable_query][:query]).tr!('"&|!(){}[]~^:+-\\', '')
|
71
|
+
request vars
|
72
|
+
else
|
73
|
+
response_vars
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def request(vars)
|
78
|
+
int = interpolate(vars, strict=true)
|
79
|
+
path = build_path(int, vars)
|
80
|
+
encoded_data = build_data(int, vars)
|
81
|
+
response = Conf.http_client.request(method, path, encoded_data)
|
82
|
+
return int, path, encoded_data, response
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_path(int, vars)
|
86
|
+
params = int[:vars][:params]
|
87
|
+
path = vars[:path] || int[:path]
|
88
|
+
unless params.empty?
|
89
|
+
path << ((path =~ /\?/) ? '&' : '?')
|
90
|
+
path << params.map { |p| p.join('=') }.join('&')
|
91
|
+
end
|
92
|
+
path
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_data(int, vars)
|
96
|
+
data = vars[:data] && Utils.parse_source(vars[:data]) || int[:data]
|
97
|
+
(data.nil? || data.is_a?(String)) ? data : MultiJson.encode(data)
|
98
|
+
end
|
99
|
+
|
100
|
+
def interpolate(*args)
|
101
|
+
tags = Tags.new
|
102
|
+
stringified = tags.stringify(:path => @path, :data => @data)
|
103
|
+
@partials, @tags = tags.partial_and_tag_names
|
104
|
+
@base_variables = Conf.variables.deep_merge(self.class.variables)
|
105
|
+
@temp_variables = Vars.new(@source_vars, @instance_vars, tags.variables)
|
106
|
+
instance_eval <<-ruby, __FILE__, __LINE__
|
107
|
+
def interpolate(vars={}, strict=false)
|
108
|
+
vars = Vars.new(vars) unless vars.is_a?(Elastics::Vars)
|
109
|
+
return {:path => path, :data => data, :vars => vars} if vars.empty? && !strict
|
110
|
+
context_variables = vars[:context] ? vars[:context].elastics.variables : (@host_elastics && @host_elastics.variables)
|
111
|
+
vars = @base_variables.deep_merge(context_variables, @temp_variables, vars).finalize
|
112
|
+
vars = interpolate_partials(vars)
|
113
|
+
obj = #{stringified}
|
114
|
+
obj = Prunable.prune(obj, Prunable::Value)
|
115
|
+
obj[:path].tr_s!('/', '/') # removes empty path segments
|
116
|
+
obj[:vars] = vars
|
117
|
+
obj
|
118
|
+
end
|
119
|
+
ruby
|
120
|
+
interpolate(*args)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
module Common
|
4
|
+
|
5
|
+
attr_reader :name, :partials, :tags, :data
|
6
|
+
|
7
|
+
def setup(host_elastics, name=nil, *vars)
|
8
|
+
@host_elastics = host_elastics
|
9
|
+
@name = name
|
10
|
+
@source_vars = Vars.new(*vars) if is_a?(Elastics::Template)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def interpolate_partials(vars)
|
15
|
+
@partials.each do |name|
|
16
|
+
partial_assigned_vars = vars[name]
|
17
|
+
next if Prunable::VALUES.include?(partial_assigned_vars)
|
18
|
+
vars[name] = case partial_assigned_vars
|
19
|
+
when Array
|
20
|
+
partial_assigned_vars.map {|v| @host_elastics.partials[name].interpolate(vars, v)}
|
21
|
+
# other partial name (usable as a case output)
|
22
|
+
when Symbol
|
23
|
+
@host_elastics.partials[partial_assigned_vars].interpolate(vars, vars[partial_assigned_vars])
|
24
|
+
# a partial object
|
25
|
+
when Template::Partial
|
26
|
+
partial_assigned_vars.interpolate(vars, vars)
|
27
|
+
# on-the-fly partial creation (an empty string would prune it before)
|
28
|
+
when String
|
29
|
+
Template::Partial.new(partial_assigned_vars).interpolate(vars, vars)
|
30
|
+
# switch to include the partial (a false value would prune it before)
|
31
|
+
when TrueClass
|
32
|
+
@host_elastics.partials[name].interpolate(vars, vars)
|
33
|
+
else
|
34
|
+
@host_elastics.partials[name].interpolate(vars, partial_assigned_vars)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
vars
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|