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,43 @@
|
|
1
|
+
module Elastics
|
2
|
+
|
3
|
+
class ArgumentError < ArgumentError; end
|
4
|
+
class SourceError < StandardError; end
|
5
|
+
class MissingPartialError < StandardError; end
|
6
|
+
class DocumentMappingError < StandardError; end
|
7
|
+
class MissingIndexEntryError < StandardError; end
|
8
|
+
class ExistingIndexError < StandardError; end
|
9
|
+
class MissingHttpClientError < StandardError; end
|
10
|
+
class MissingParentError < StandardError; end
|
11
|
+
class MissingVariableError < StandardError; end
|
12
|
+
|
13
|
+
class HttpError < StandardError
|
14
|
+
|
15
|
+
attr_reader :response
|
16
|
+
|
17
|
+
def initialize(response, caller_line=nil)
|
18
|
+
@response = response
|
19
|
+
@caller_line = caller_line
|
20
|
+
end
|
21
|
+
|
22
|
+
def status
|
23
|
+
response.status
|
24
|
+
end
|
25
|
+
|
26
|
+
def body
|
27
|
+
response.body
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
log = "#@caller_line\n" if @caller_line
|
32
|
+
"#{log}#{status}: #{body}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_hash
|
36
|
+
MultiJson.decode response.body
|
37
|
+
rescue MultiJson::DecodeError
|
38
|
+
{}
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Elastics
|
2
|
+
module HttpClients
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_accessor :options, :base_uri, :raise_proc
|
6
|
+
|
7
|
+
def initialize(base_uri='http://localhost:9200', options={})
|
8
|
+
@options = options
|
9
|
+
@base_uri = base_uri
|
10
|
+
@raise_proc = proc{|response| response.status >= 400}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Elastics
|
2
|
+
module HttpClients
|
3
|
+
|
4
|
+
class Dummy
|
5
|
+
def request(*)
|
6
|
+
raise MissingHttpClientError,
|
7
|
+
'you should install the gem "patron" (recommended for performances) or "rest-client", ' +
|
8
|
+
'or provide your own http-client interface and set Elastics::Configuration.http_client'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Loader
|
13
|
+
|
14
|
+
extend self
|
15
|
+
|
16
|
+
def new_http_client
|
17
|
+
if Gem::Specification.respond_to?(:find_all_by_name)
|
18
|
+
case
|
19
|
+
# terrible way to check whether a gem is available.
|
20
|
+
# Gem.available? was just perfect: that's probably the reason it has been deprecated!
|
21
|
+
# https://github.com/rubygems/rubygems/issues/176
|
22
|
+
when Gem::Specification::find_all_by_name('patron').any? then require_patron
|
23
|
+
when Gem::Specification::find_all_by_name('rest-client').any? then require_rest_client
|
24
|
+
else Dummy.new
|
25
|
+
end
|
26
|
+
else
|
27
|
+
case
|
28
|
+
when Gem.available?('patron') then require_patron
|
29
|
+
when Gem.available?('rest-client') then require_rest_client
|
30
|
+
else Dummy.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def require_patron
|
38
|
+
require 'patron'
|
39
|
+
require 'elastics/http_clients/patron'
|
40
|
+
Patron.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def require_rest_client
|
44
|
+
require 'rest-client'
|
45
|
+
require 'elastics/http_clients/rest_client'
|
46
|
+
RestClient.new
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Elastics
|
2
|
+
module HttpClients
|
3
|
+
class Patron < Base
|
4
|
+
|
5
|
+
def request(method, path, data=nil)
|
6
|
+
# patron would raise an error for :post and :put requests with no data
|
7
|
+
# and elasticsearch ignores the data when it expects no data,
|
8
|
+
# so we silence patron by adding some dummy data
|
9
|
+
data = {} if (method == 'POST' || method == 'PUT') && data.nil?
|
10
|
+
opts = options.merge(:data => data)
|
11
|
+
session.request method.to_s.downcase.to_sym, path, {}, opts
|
12
|
+
rescue ::Patron::TimeoutError
|
13
|
+
session.request method.to_s.downcase.to_sym, path, {}, opts
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def session
|
19
|
+
Thread.current[:elastics_patron_session] ||= begin
|
20
|
+
sess = ::Patron::Session.new
|
21
|
+
sess.headers['User-Agent'] = "elastics-#{Elastics::VERSION}"
|
22
|
+
sess.base_url = base_uri
|
23
|
+
sess
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Elastics
|
2
|
+
module HttpClients
|
3
|
+
class RestClient < Base
|
4
|
+
|
5
|
+
def request(method, path, data=nil)
|
6
|
+
url = "#{base_uri}#{path}"
|
7
|
+
opts = options.merge( :method => method.to_s.downcase.to_sym,
|
8
|
+
:url => url,
|
9
|
+
:payload => data )
|
10
|
+
response = ::RestClient::Request.new( opts ).execute
|
11
|
+
extend_response(response, url)
|
12
|
+
|
13
|
+
rescue ::RestClient::ExceptionWithResponse => e
|
14
|
+
extend_response(e.response, url)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def extend_response(response, url)
|
20
|
+
response.extend ResponseExtension
|
21
|
+
response.url = url
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
module ResponseExtension
|
26
|
+
attr_accessor :url
|
27
|
+
|
28
|
+
def status
|
29
|
+
code.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Elastics
|
4
|
+
class Logger < ::Logger
|
5
|
+
|
6
|
+
|
7
|
+
attr_accessor :debug_variables, :debug_request, :debug_result, :curl_format
|
8
|
+
|
9
|
+
def initialize(*)
|
10
|
+
super
|
11
|
+
self.level = ::Logger::WARN
|
12
|
+
self.progname = 'ELASTICS'
|
13
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
14
|
+
elastics_format(severity, msg)
|
15
|
+
end
|
16
|
+
@debug_variables = true
|
17
|
+
@debug_request = true
|
18
|
+
@debug_result = true
|
19
|
+
@curl_format = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def elastics_format(severity, msg)
|
23
|
+
prefix = Dye.dye(" ELASTICS-#{severity} ", "ELASTICS-#{severity}:", :blue, :bold, :reversed) + ' '
|
24
|
+
msg.split("\n").map{|l| prefix + l}.join("\n") + "\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
# force color in console (used with jruby)
|
28
|
+
def color=(bool)
|
29
|
+
Dye.color = bool
|
30
|
+
end
|
31
|
+
|
32
|
+
def color
|
33
|
+
Dye.color?
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Elastics
|
2
|
+
class ProgBar
|
3
|
+
|
4
|
+
attr_reader :pbar, :total_count
|
5
|
+
|
6
|
+
def initialize(total_count, batch_size=nil, prefix_message=nil)
|
7
|
+
@total_count = total_count
|
8
|
+
@successful_count = 0
|
9
|
+
@failed_count = 0
|
10
|
+
@pbar = ::ProgressBar.new('processing...', total_count)
|
11
|
+
@pbar.clear
|
12
|
+
@pbar.bar_mark = '|'
|
13
|
+
puts '_' * @pbar.send(:get_term_width)
|
14
|
+
message = "#{prefix_message}Processing #{total_count} documents"
|
15
|
+
message << " in batches of #{batch_size}:" unless batch_size.nil?
|
16
|
+
puts message
|
17
|
+
@pbar.send(:show)
|
18
|
+
end
|
19
|
+
|
20
|
+
def process_result(result, inc)
|
21
|
+
unless result.nil? || result.empty?
|
22
|
+
unless result.failed.size == 0
|
23
|
+
Conf.logger.error "Failed load:\n#{result.failed.to_yaml}"
|
24
|
+
@pbar.bar_mark = 'F'
|
25
|
+
end
|
26
|
+
@failed_count += result.failed.size
|
27
|
+
@successful_count += result.successful.size
|
28
|
+
end
|
29
|
+
@pbar.inc(inc)
|
30
|
+
end
|
31
|
+
|
32
|
+
def finish
|
33
|
+
@pbar.finish
|
34
|
+
puts "Processed #@total_count. Successful #@successful_count. Skipped #{@total_count - @successful_count - @failed_count}. Failed #@failed_count."
|
35
|
+
puts 'See the log for the details about the failures.' unless @failed_count == 0
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
raise NotImplementedError, %(The "flex/rails" file has been replaced by the "elastics-rails" gem. Please, use "elastics-rails" in place of "flex" in Rails applications (change "gem 'flex', require => 'flex/rails'" to "gem 'elastics-rails'" in your Gemfile). Please, read the upgrade notes at http://elastics.github.io/elastics/doc/7-Tutorials/2-Migrate-from-0.x.html.)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Result < ::Hash
|
3
|
+
|
4
|
+
attr_reader :template, :response
|
5
|
+
attr_accessor :variables
|
6
|
+
|
7
|
+
def initialize(template, variables, response, result=nil)
|
8
|
+
@template = template
|
9
|
+
@variables = variables
|
10
|
+
@response = response
|
11
|
+
replace result || !response.body.empty? && MultiJson.decode(response.body) || return
|
12
|
+
Conf.result_extenders.each do |ext|
|
13
|
+
next if ext.respond_to?(:should_extend?) && !ext.should_extend?(self)
|
14
|
+
extend ext
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_elastics_result(force=false)
|
19
|
+
return self if variables[:context].nil? || variables[:raw_result] &&! force
|
20
|
+
variables[:context].respond_to?(:elastics_result) ? variables[:context].elastics_result(self) : self
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Result
|
3
|
+
module Bulk
|
4
|
+
|
5
|
+
# extend if result comes from a bulk url
|
6
|
+
def self.should_extend?(result)
|
7
|
+
result.response.url =~ /\b_bulk\b/
|
8
|
+
end
|
9
|
+
|
10
|
+
def failed
|
11
|
+
self['items'].reject{|i| i['index']['ok']}
|
12
|
+
end
|
13
|
+
|
14
|
+
def successful
|
15
|
+
self['items'].select{|i| i['index']['ok']}
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Result
|
3
|
+
|
4
|
+
# adds sugar to documents with the following structure (_source is optional):
|
5
|
+
#
|
6
|
+
# {
|
7
|
+
# "_index" : "twitter",
|
8
|
+
# "_type" : "tweet",
|
9
|
+
# "_id" : "1",
|
10
|
+
# "_source" : {
|
11
|
+
# "user" : "kimchy",
|
12
|
+
# "postDate" : "2009-11-15T14:12:12",
|
13
|
+
# "message" : "trying out Elastics Search"
|
14
|
+
# }
|
15
|
+
# }
|
16
|
+
|
17
|
+
module Document
|
18
|
+
|
19
|
+
# extend if result has a structure like a document
|
20
|
+
def self.should_extend?(obj)
|
21
|
+
%w[_index _type _id].all? {|k| obj.has_key?(k)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(meth, private=false)
|
25
|
+
smeth = meth.to_s
|
26
|
+
readers.has_key?(smeth) || has_key?(smeth) || has_key?("_#{smeth}") || super
|
27
|
+
end
|
28
|
+
|
29
|
+
# exposes _source and readers: automatically supply object-like reader access
|
30
|
+
# also expose meta readers like _id, _source, etc, also callable without the leading '_'
|
31
|
+
def method_missing(meth, *args, &block)
|
32
|
+
smeth = meth.to_s
|
33
|
+
case
|
34
|
+
# field name
|
35
|
+
when readers.has_key?(smeth)
|
36
|
+
readers[smeth]
|
37
|
+
# result item
|
38
|
+
when has_key?(smeth)
|
39
|
+
self[smeth]
|
40
|
+
# result item called without the '_' prefix
|
41
|
+
when has_key?("_#{smeth}")
|
42
|
+
self["_#{smeth}"]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# used to get the unprefixed (by live-reindex) index name
|
49
|
+
def index_basename
|
50
|
+
@index_basename ||= self['_index'].sub(/^\d{14}_/, '')
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def readers
|
57
|
+
@readers ||= begin
|
58
|
+
readers = (self['_source']||{}).merge(self['fields']||{})
|
59
|
+
# flattened reader for multi_readers or attachment readers
|
60
|
+
readers.keys.each{|k| readers[k.gsub('.','_')] = readers.delete(k) if k.include?('.')}
|
61
|
+
readers
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Result
|
3
|
+
module MultiGet
|
4
|
+
|
5
|
+
# extend if result comes from a search url
|
6
|
+
def self.should_extend?(result)
|
7
|
+
result.response.url =~ /\b_mget\b/ && result['docs']
|
8
|
+
end
|
9
|
+
|
10
|
+
# extend the hits results on extended
|
11
|
+
def self.extended(result)
|
12
|
+
result['docs'].each { |h| h.extend(Document) }
|
13
|
+
result['docs'].extend Struct::Paginable
|
14
|
+
result['docs'].setup(result['docs'].size, result.variables)
|
15
|
+
end
|
16
|
+
|
17
|
+
def docs
|
18
|
+
self['docs']
|
19
|
+
end
|
20
|
+
alias_method :collection, :docs
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Elastics
|
2
|
+
class Result
|
3
|
+
module Search
|
4
|
+
|
5
|
+
# extend if result comes from a search url
|
6
|
+
def self.should_extend?(result)
|
7
|
+
result.response.url =~ /\b_m?search\b/ && result['hits']
|
8
|
+
end
|
9
|
+
|
10
|
+
# extend the hits results on extended
|
11
|
+
def self.extended(result)
|
12
|
+
result['hits']['hits'].each { |h| h.extend(Document) }
|
13
|
+
result['hits']['hits'].extend Struct::Paginable
|
14
|
+
result['hits']['hits'].setup(result['hits']['total'], result.variables)
|
15
|
+
end
|
16
|
+
|
17
|
+
def collection
|
18
|
+
self['hits']['hits']
|
19
|
+
end
|
20
|
+
alias_method :documents, :collection
|
21
|
+
|
22
|
+
def facets
|
23
|
+
self['facets']
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Elastics
|
2
|
+
module Struct
|
3
|
+
class Array < ::Array
|
4
|
+
|
5
|
+
include Symbolize
|
6
|
+
|
7
|
+
def push(*vals)
|
8
|
+
super *symbolize(vals)
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(val)
|
12
|
+
super symbolize(val)
|
13
|
+
end
|
14
|
+
|
15
|
+
def insert(*vals)
|
16
|
+
super *symbolize(vals)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unshift(*vals)
|
20
|
+
super *symbolize(vals)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|