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,66 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
module Logger
|
4
|
+
|
5
|
+
def caller_line
|
6
|
+
caller.find{|l| l !~ /(#{LIB_PATHS.join('|')})/}
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def template_name
|
12
|
+
@host_elastics && @name && "#{@host_elastics.context}.#@name" || 'template'
|
13
|
+
end
|
14
|
+
|
15
|
+
def log_render(int, path, encoded_data, result)
|
16
|
+
logger = Conf.logger
|
17
|
+
return unless logger.is_a?(Elastics::Logger)
|
18
|
+
logger.info Dye.dye("Rendered #{template_name} from: #{caller_line}", :blue, :bold)
|
19
|
+
return unless logger.level == ::Logger::DEBUG
|
20
|
+
|
21
|
+
h = {}
|
22
|
+
if logger.debug_variables
|
23
|
+
h[:variables] = int[:vars] if int
|
24
|
+
end
|
25
|
+
if logger.debug_request
|
26
|
+
h[:request] = {}
|
27
|
+
h[:request][:method] = method
|
28
|
+
h[:request][:path] = path
|
29
|
+
h[:request][:data] = begin
|
30
|
+
MultiJson.decode(encoded_data) unless encoded_data.nil?
|
31
|
+
rescue MultiJson::DecodeError
|
32
|
+
encoded_data
|
33
|
+
end
|
34
|
+
h[:request].delete(:data) if h[:request][:data].nil?
|
35
|
+
end
|
36
|
+
if logger.debug_result
|
37
|
+
h[:result] = result if result
|
38
|
+
end
|
39
|
+
logger.debug logger.curl_format ? curl_format(h[:request]) : yaml_format(h)
|
40
|
+
end
|
41
|
+
|
42
|
+
def curl_format(h)
|
43
|
+
pretty = h[:path] =~ /\?/ ? '&pretty=1' : '?pretty=1'
|
44
|
+
curl = %(curl -X#{method} "#{Conf.base_uri}#{h[:path]}#{pretty}")
|
45
|
+
if h[:data]
|
46
|
+
data = h[:data].is_a?(String) ? h[:data] : MultiJson.encode(h[:data], :pretty => true)
|
47
|
+
curl << %( -d '\n#{data}\n')
|
48
|
+
end
|
49
|
+
curl
|
50
|
+
end
|
51
|
+
|
52
|
+
def yaml_format(hash)
|
53
|
+
hash.to_yaml.split("\n").map do |l|
|
54
|
+
case l
|
55
|
+
when /^---$/
|
56
|
+
when /^( |-)/
|
57
|
+
Dye.dye(l, :blue)
|
58
|
+
when /^:(variables|request|result)/
|
59
|
+
Dye.dye(l, :magenta, :bold) + (Dye.color ? Dye.sgr(:blue) : '')
|
60
|
+
end
|
61
|
+
end.compact.join("\n") + "\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
class Partial
|
4
|
+
|
5
|
+
include Common
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
tags = Tags.new
|
10
|
+
stringified = tags.stringify(data)
|
11
|
+
@partials, @tags = tags.partial_and_tag_names
|
12
|
+
@tags_variables = tags.variables
|
13
|
+
instance_eval <<-ruby, __FILE__, __LINE__
|
14
|
+
def interpolate(vars={}, partial_assigned_vars={})
|
15
|
+
vars = Vars.new(vars, @tags_variables, partial_assigned_vars)
|
16
|
+
vars = interpolate_partials(vars)
|
17
|
+
#{stringified}
|
18
|
+
end
|
19
|
+
ruby
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_source
|
23
|
+
{@name.to_s => @data}.to_yaml
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
class Search < Template
|
4
|
+
|
5
|
+
def initialize(data, vars=nil)
|
6
|
+
super('GET', '/<<index>>/<<type>>/_search', data, vars)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_a(*vars)
|
10
|
+
a = super
|
11
|
+
2.times{ a.delete_at 0 }
|
12
|
+
a
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_msearch(*vars)
|
16
|
+
vars = Vars.new(*vars)
|
17
|
+
int = interpolate(vars, strict=true)
|
18
|
+
header = {}
|
19
|
+
header[:index] = int[:vars][:index] if int[:vars][:index]
|
20
|
+
header[:type] = int[:vars][:type] if int[:vars][:type]
|
21
|
+
[:search_type, :preferences, :routing].each do |k|
|
22
|
+
header[k] = int[:vars][k] if int[:vars][k] || int[:vars][:params] && int[:vars][:params][k]
|
23
|
+
end
|
24
|
+
data, encoded = build_data(int, vars)
|
25
|
+
"#{MultiJson.encode(header)}\n#{encoded}\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
class SlimSearch < Search
|
4
|
+
|
5
|
+
# removes the fields param (no _source returned)
|
6
|
+
# the result.loaded_collection, will load the records from the db
|
7
|
+
def self.variables
|
8
|
+
super.deep_merge!(:params => {:fields => ''})
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Template
|
3
|
+
class Tags < Array
|
4
|
+
|
5
|
+
TAG_REGEXP = /<<\s*([\w\.]+)\s*(?:=([^>]*))*>>/
|
6
|
+
|
7
|
+
# tag variables are the defaults defined with the tag
|
8
|
+
# a variable could be optional, and the default could be nil
|
9
|
+
def variables
|
10
|
+
tag_variables = Vars.new
|
11
|
+
each do |t|
|
12
|
+
if t.default || t.optional
|
13
|
+
if t.name =~ /\./ # set default for nested var
|
14
|
+
tag_variables.store_nested(t.name, t.default)
|
15
|
+
else
|
16
|
+
tag_variables[t.name] = t.default
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
tag_variables
|
21
|
+
end
|
22
|
+
|
23
|
+
def stringify(structure)
|
24
|
+
structure.inspect.gsub(/(?:"#{TAG_REGEXP}"|#{TAG_REGEXP})/) do
|
25
|
+
match = $&
|
26
|
+
match =~ TAG_REGEXP
|
27
|
+
t = Tag.new($1, $2)
|
28
|
+
push t unless find{|i| i.name == t.name}
|
29
|
+
(match !~ /^"/) ? "\#{vars.get_prunable(:'#{t.name}')}" : "vars.get_prunable(:'#{t.name}')"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def partial_and_tag_names
|
34
|
+
map(&:name).partition{|n| n.to_s =~ /^_/}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class Tag
|
40
|
+
|
41
|
+
RESERVED = [:context, :path, :data, :params, :no_pruning, :raw_result, :raise]
|
42
|
+
|
43
|
+
attr_reader :optional, :name, :default
|
44
|
+
|
45
|
+
def initialize(name, default)
|
46
|
+
raise SourceError, ":#{name} is a reserved symbol and cannot be used as a tag name" \
|
47
|
+
if RESERVED.include?(name)
|
48
|
+
@name = name.to_sym
|
49
|
+
@optional = !!default
|
50
|
+
@default = YAML.load(default) unless default.nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Templates
|
3
|
+
|
4
|
+
extend self
|
5
|
+
attr_accessor :contexts
|
6
|
+
@contexts = []
|
7
|
+
|
8
|
+
def self.included(context)
|
9
|
+
context.class_eval do
|
10
|
+
Elastics::Templates.contexts |= [context]
|
11
|
+
@elastics ||= ClassProxy::Base.new(context)
|
12
|
+
@elastics.extend(ClassProxy::Templates).init
|
13
|
+
def self.elastics; @elastics end
|
14
|
+
def self.template_methods; elastics.templates.keys end
|
15
|
+
eval "extend module #{context}::ElasticsTemplateMethods; self end"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Elastics
|
2
|
+
module UtilityMethods
|
3
|
+
|
4
|
+
def search(data, vars={})
|
5
|
+
Template::Search.new(data).setup(Elastics.elastics).render(vars)
|
6
|
+
end
|
7
|
+
|
8
|
+
# like Elastics.search, but it will use the Elastics::Template::SlimSearch instead
|
9
|
+
def slim_search(data, vars={})
|
10
|
+
Template::SlimSearch.new(data).setup(Elastics.elastics).render(vars)
|
11
|
+
end
|
12
|
+
|
13
|
+
%w[HEAD GET PUT POST DELETE].each do |m|
|
14
|
+
class_eval <<-ruby, __FILE__, __LINE__
|
15
|
+
def #{m}(*args)
|
16
|
+
perform '#{m}', *args
|
17
|
+
end
|
18
|
+
ruby
|
19
|
+
end
|
20
|
+
|
21
|
+
def json2yaml(json)
|
22
|
+
YAML.dump(MultiJson.decode(json))
|
23
|
+
end
|
24
|
+
|
25
|
+
def yaml2json(yaml)
|
26
|
+
MultiJson.encode(YAML.load(yaml))
|
27
|
+
end
|
28
|
+
|
29
|
+
def reload!
|
30
|
+
elastics.variables.deep_merge! Conf.variables
|
31
|
+
Templates.contexts.each {|c| c.elastics.reload!}
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def doc(*args)
|
36
|
+
elastics.doc(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def scan_search(*args, &block)
|
40
|
+
elastics.scan_search(*args, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def scan_all(*vars, &block)
|
44
|
+
elastics.scan_search(:match_all, *vars) do |raw_result|
|
45
|
+
batch = raw_result['hits']['hits']
|
46
|
+
block.call(batch)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump_all(*vars, &block)
|
51
|
+
refresh_index(*vars)
|
52
|
+
scan_all({:params => {:fields => '*,_source'}}, *vars) do |batch|
|
53
|
+
batch.map!{|document| document.delete('_score'); document}
|
54
|
+
block.call(batch)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# refresh and pull the full document from the index
|
59
|
+
def dump_one(*vars)
|
60
|
+
refresh_index(*vars)
|
61
|
+
document = search_by_id({:params => {:fields => '*,_source'}}, *vars)
|
62
|
+
document.delete('_score')
|
63
|
+
document
|
64
|
+
end
|
65
|
+
|
66
|
+
# You should use Elastics.post_bulk_string if you have an already formatted bulk data-string
|
67
|
+
def post_bulk_collection(collection, options={})
|
68
|
+
raise ArgumentError, "Array expected as :collection, got #{collection.inspect}" \
|
69
|
+
unless collection.is_a?(Array)
|
70
|
+
bulk_string = ''
|
71
|
+
collection.each do |d|
|
72
|
+
bulk_string << build_bulk_string(d, options)
|
73
|
+
end
|
74
|
+
post_bulk_string(:bulk_string => bulk_string) unless bulk_string.empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
def build_bulk_string(document, options={})
|
78
|
+
case document
|
79
|
+
when Hash
|
80
|
+
bulk_string_from_hash(document, options)
|
81
|
+
when Elastics::ModelIndexer, Elastics::ActiveModel
|
82
|
+
bulk_string_from_elastics(document, options)
|
83
|
+
else
|
84
|
+
raise NotImplementedError, "Unable to convert the document #{document.inspect} to a bulk string."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def perform(*args)
|
91
|
+
Template.new(*args).setup(Elastics.elastics).render
|
92
|
+
end
|
93
|
+
|
94
|
+
def bulk_string_from_hash(document, options)
|
95
|
+
meta = Utils.slice_hash(document, '_index', '_type', '_id')
|
96
|
+
if document.has_key?('fields')
|
97
|
+
document['fields'].each do |k, v|
|
98
|
+
meta[k] = v if k[0] == '_'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
source = document['_source'] unless options[:action] == 'delete'
|
102
|
+
to_bulk_string(meta, source, options)
|
103
|
+
end
|
104
|
+
|
105
|
+
def bulk_string_from_elastics(document, options)
|
106
|
+
elastics = document.elastics
|
107
|
+
return '' unless document.elastics_indexable?
|
108
|
+
meta = { '_index' => elastics.index,
|
109
|
+
'_type' => elastics.type,
|
110
|
+
'_id' => elastics.id }
|
111
|
+
meta['_parent'] = elastics.parent if elastics.parent
|
112
|
+
meta['_routing'] = elastics.routing if elastics.routing
|
113
|
+
source = document.elastics_source unless options[:action] == 'delete'
|
114
|
+
to_bulk_string(meta, source, options)
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_bulk_string(meta, source, options)
|
118
|
+
action = options[:action] || 'index'
|
119
|
+
return '' if source.nil? || source.empty? &&! (action == 'delete')
|
120
|
+
meta['_index'] = LiveReindex.prefix_index(meta['_index']) if LiveReindex.should_prefix_index?
|
121
|
+
bulk_string = MultiJson.encode(action => meta) + "\n"
|
122
|
+
unless action == 'delete'
|
123
|
+
source_line = source.is_a?(String) ? source : MultiJson.encode(source)
|
124
|
+
return '' if source.nil? || source.empty?
|
125
|
+
bulk_string << source_line + "\n"
|
126
|
+
end
|
127
|
+
bulk_string
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def parse_source(source)
|
6
|
+
return unless source
|
7
|
+
parsed = case source
|
8
|
+
when Hash then keyfy(:to_s, source)
|
9
|
+
when /^\s*\{.+\}\s*$/m then source
|
10
|
+
when String then YAML.load(source)
|
11
|
+
else raise ArgumentError, "expected a String or Hash instance, got #{source.inspect}"
|
12
|
+
end
|
13
|
+
raise ArgumentError, "the source does not decode to an Array, Hash or String, got #{parsed.inspect}" \
|
14
|
+
unless parsed.is_a?(Hash) || parsed.is_a?(Array) || parsed.is_a?(String)
|
15
|
+
parsed
|
16
|
+
end
|
17
|
+
|
18
|
+
def erb_process(source)
|
19
|
+
varname = "_elastics_#{source.hash.to_s.tr('-', '_')}"
|
20
|
+
ERB.new(File.read(source), nil, nil, varname).result
|
21
|
+
end
|
22
|
+
|
23
|
+
def group_array_by(ary)
|
24
|
+
h = {}
|
25
|
+
ary.each do |i|
|
26
|
+
k = yield i
|
27
|
+
if h.has_key?(k)
|
28
|
+
h[k] << i
|
29
|
+
else
|
30
|
+
h[k] = [i]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
h
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_allcaps_keys(hash)
|
37
|
+
hash.keys.each { |k| hash.delete(k) if k =~ /^[A-Z_]+$/ }
|
38
|
+
hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def keyfy(to_what, obj)
|
42
|
+
case obj
|
43
|
+
when Hash
|
44
|
+
h = {}
|
45
|
+
obj.each do |k,v|
|
46
|
+
h[k.send(to_what)] = keyfy(to_what, v)
|
47
|
+
end
|
48
|
+
h
|
49
|
+
when Array
|
50
|
+
obj.map{|i| keyfy(to_what, i)}
|
51
|
+
else
|
52
|
+
obj
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def slice_hash(hash, *keys)
|
57
|
+
h = {}
|
58
|
+
keys.each{|k| h[k] = hash[k] if hash.has_key?(k)}
|
59
|
+
h
|
60
|
+
end
|
61
|
+
|
62
|
+
def env2options(*keys)
|
63
|
+
options = {}
|
64
|
+
ENV.keys.map do |k|
|
65
|
+
key = k.downcase.to_sym
|
66
|
+
options[key] = ENV[k] if keys.include?(key)
|
67
|
+
end
|
68
|
+
options
|
69
|
+
end
|
70
|
+
|
71
|
+
def define_delegation(opts)
|
72
|
+
file, line = caller.first.split(':', 2)
|
73
|
+
line = line.to_i
|
74
|
+
|
75
|
+
obj, meth, methods, to = opts[:in], opts[:by], opts[:for], opts[:to]
|
76
|
+
|
77
|
+
methods.each do |method|
|
78
|
+
obj.send meth, <<-method, file, line - 1
|
79
|
+
def #{method}(*args, &block) # def method_name(*args, &block)
|
80
|
+
if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
|
81
|
+
#{to}.__send__(:#{method}, *args, &block) # client.__send__(:name, *args, &block)
|
82
|
+
end # end
|
83
|
+
end # end
|
84
|
+
method
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
def class_name_to_type(class_name)
|
90
|
+
type = class_name.tr(':', '_')
|
91
|
+
type.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
92
|
+
type.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
93
|
+
type.downcase!
|
94
|
+
type
|
95
|
+
end
|
96
|
+
|
97
|
+
def type_to_class_name(type)
|
98
|
+
type.gsub(/__(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|