elastics-client 1.0.4
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/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
|