esearch 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +6 -0
- data/.travis.yml +19 -0
- data/Changelog.md +27 -0
- data/Gemfile +6 -0
- data/Gemfile.devtools +60 -0
- data/Guardfile +18 -0
- data/LICENSE +20 -0
- data/README.md +93 -0
- data/Rakefile +2 -0
- data/TODO +3 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +3 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +103 -0
- data/config/yardstick.yml +2 -0
- data/esearch.gemspec +26 -0
- data/lib/esearch.rb +43 -0
- data/lib/esearch/cluster.rb +85 -0
- data/lib/esearch/command.rb +158 -0
- data/lib/esearch/command/cluster.rb +28 -0
- data/lib/esearch/command/document.rb +111 -0
- data/lib/esearch/command/exist.rb +37 -0
- data/lib/esearch/command/index.rb +66 -0
- data/lib/esearch/command/search.rb +24 -0
- data/lib/esearch/command/status.rb +24 -0
- data/lib/esearch/connection.rb +36 -0
- data/lib/esearch/document.rb +53 -0
- data/lib/esearch/index.rb +61 -0
- data/lib/esearch/indices.rb +52 -0
- data/lib/esearch/mixin.rb +111 -0
- data/lib/esearch/presenter.rb +43 -0
- data/lib/esearch/presenter/aspect.rb +17 -0
- data/lib/esearch/presenter/aspect/range.rb +63 -0
- data/lib/esearch/presenter/aspect/term.rb +19 -0
- data/lib/esearch/presenter/cluster.rb +84 -0
- data/lib/esearch/presenter/document.rb +102 -0
- data/lib/esearch/presenter/facet.rb +72 -0
- data/lib/esearch/presenter/hit.rb +70 -0
- data/lib/esearch/presenter/hits.rb +60 -0
- data/lib/esearch/presenter/index.rb +23 -0
- data/lib/esearch/presenter/search.rb +32 -0
- data/lib/esearch/presenter/status.rb +18 -0
- data/lib/esearch/request.rb +90 -0
- data/lib/esearch/type.rb +40 -0
- data/spec/integration/esearch/spike_spec.rb +50 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/support/example_group_methods.rb +7 -0
- data/spec/support/ice_nine_config.rb +6 -0
- data/spec/unit/esearch/cluster/class_methods/connect_spec.rb +16 -0
- data/spec/unit/esearch/cluster/health_spec.rb +10 -0
- data/spec/unit/esearch/cluster/index_spec.rb +11 -0
- data/spec/unit/esearch/cluster/indices_spec.rb +11 -0
- data/spec/unit/esearch/cluster/path_spec.rb +11 -0
- data/spec/unit/esearch/command/class_methods/run_spec.rb +16 -0
- data/spec/unit/esearch/command/cluster/health/run_spec.rb +14 -0
- data/spec/unit/esearch/command/document/delete/run_spec.rb +13 -0
- data/spec/unit/esearch/command/document/get/result_spec.rb +27 -0
- data/spec/unit/esearch/command/document/index/create/run_spec.rb +17 -0
- data/spec/unit/esearch/command/document/index/run_create_spec.rb +17 -0
- data/spec/unit/esearch/command/document/index/run_spec.rb +15 -0
- data/spec/unit/esearch/command/document/index/run_update_spec.rb +15 -0
- data/spec/unit/esearch/command/document/index/update/run_spec.rb +15 -0
- data/spec/unit/esearch/command/exist/result_spec.rb +39 -0
- data/spec/unit/esearch/command/index/create/run_spec.rb +14 -0
- data/spec/unit/esearch/command/index/delete/run_spec.rb +13 -0
- data/spec/unit/esearch/command/index/refresh/run_spec.rb +13 -0
- data/spec/unit/esearch/command/result_spec.rb +68 -0
- data/spec/unit/esearch/command/search/run_spec.rb +14 -0
- data/spec/unit/esearch/command/status/run_spec.rb +13 -0
- data/spec/unit/esearch/connection/class_methods/build_spec.rb +29 -0
- data/spec/unit/esearch/connection/run_spec.rb +36 -0
- data/spec/unit/esearch/document/connection_spec.rb +12 -0
- data/spec/unit/esearch/document/delete_spec.rb +12 -0
- data/spec/unit/esearch/document/get_spec.rb +12 -0
- data/spec/unit/esearch/index/create_spec.rb +12 -0
- data/spec/unit/esearch/index/delete_spec.rb +11 -0
- data/spec/unit/esearch/index/type_spec.rb +12 -0
- data/spec/unit/esearch/indices/all/path_spec.rb +12 -0
- data/spec/unit/esearch/mixin/document/index_create_spec.rb +31 -0
- data/spec/unit/esearch/mixin/document/index_spec.rb +31 -0
- data/spec/unit/esearch/mixin/document/index_update_spec.rb +31 -0
- data/spec/unit/esearch/mixin/exist/exist_predicate_spec.rb +16 -0
- data/spec/unit/esearch/mixin/index/refresh_spec.rb +16 -0
- data/spec/unit/esearch/mixin/index/status_spec.rb +16 -0
- data/spec/unit/esearch/mixin/search/search_spec.rb +18 -0
- data/spec/unit/esearch/presenter/aspect/range/from_spec.rb +24 -0
- data/spec/unit/esearch/presenter/aspect/range/to_spec.rb +24 -0
- data/spec/unit/esearch/presenter/class_methods/new_spec.rb +37 -0
- data/spec/unit/esearch/presenter/facet/build_spec.rb +26 -0
- data/spec/unit/esearch/presenter/facet/class_methods/build_spec.rb +26 -0
- data/spec/unit/esearch/presenter/hit/fields_spec.rb +24 -0
- data/spec/unit/esearch/presenter/hit/source_spec.rb +24 -0
- data/spec/unit/esearch/presenter/hits/each_spec.rb +15 -0
- data/spec/unit/esearch/presenter/hits/size_spec.rb +13 -0
- data/spec/unit/esearch/request/initialize_spec.rb +39 -0
- data/spec/unit/esearch/request/run_spec.rb +39 -0
- data/spec/unit/esearch/type/connection_spec.rb +15 -0
- data/spec/unit/esearch/type/document_spec.rb +12 -0
- metadata +330 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Esearch
|
2
|
+
class Presenter
|
3
|
+
|
4
|
+
# Status presenter base class
|
5
|
+
class Status < self
|
6
|
+
|
7
|
+
# Status of an elasticsearch index
|
8
|
+
class Index < Presenter
|
9
|
+
|
10
|
+
expose_primitive('name')
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
expose_tagged_collection('indices', Status::Index)
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Esearch
|
2
|
+
|
3
|
+
# Request used to interface elasticsearch
|
4
|
+
class Request
|
5
|
+
include Adamantium, Equalizer.new(:verb, :path, :body, :params)
|
6
|
+
|
7
|
+
EMPTY_HASH = {}.freeze
|
8
|
+
|
9
|
+
# Return http verb
|
10
|
+
#
|
11
|
+
# @return [Symbol]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
attr_reader :verb
|
16
|
+
|
17
|
+
# Return path
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
attr_reader :path
|
24
|
+
|
25
|
+
# Return body
|
26
|
+
#
|
27
|
+
# @return [Hash]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
attr_reader :body
|
32
|
+
|
33
|
+
# Return params
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
#
|
39
|
+
attr_reader :params
|
40
|
+
|
41
|
+
# Initialize object
|
42
|
+
#
|
43
|
+
# @param [Symbol] verb
|
44
|
+
# @param [String] path
|
45
|
+
# @param [Hash] body
|
46
|
+
# @param [Hash] params
|
47
|
+
#
|
48
|
+
# @return [undefined]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
#
|
52
|
+
def initialize(verb, path, body = EMPTY_HASH, params = EMPTY_HASH)
|
53
|
+
@verb, @path, @body, @params = verb, path.to_s, body, params
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return log string
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
#
|
62
|
+
def log_string
|
63
|
+
"#{verb.upcase} #{path} : #{params} : #{body}"
|
64
|
+
end
|
65
|
+
memoize :log_string
|
66
|
+
|
67
|
+
# Run request on connection
|
68
|
+
#
|
69
|
+
# @param [Faraday::Connection]
|
70
|
+
#
|
71
|
+
# @return [Faraday::Response]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
#
|
75
|
+
def run(connection)
|
76
|
+
connection.public_send(verb, path) do |request|
|
77
|
+
request.params = params
|
78
|
+
request.headers[:content_type]=Command::JSON_CONTENT_TYPE
|
79
|
+
request.body = MultiJson.dump(body)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
[:head, :get, :post, :put, :delete].each do |verb|
|
84
|
+
define_singleton_method(verb) do |*args|
|
85
|
+
new(verb, *args)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/lib/esearch/type.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Esearch
|
2
|
+
# Interface to a specific elasticsearch type
|
3
|
+
class Type
|
4
|
+
include Adamantium::Flat, Concord.new(:es_index, :name)
|
5
|
+
include Mixin::Exist, Mixin::Document, Mixin::Search
|
6
|
+
|
7
|
+
# Return document handler
|
8
|
+
#
|
9
|
+
# @param [String] id
|
10
|
+
#
|
11
|
+
# @return [Document]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def document(id)
|
16
|
+
Document.new(self, id)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return connection
|
20
|
+
#
|
21
|
+
# @return [Faraday::Connection]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def connection
|
26
|
+
es_index.connection
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return path
|
30
|
+
#
|
31
|
+
# @return [Pathname]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
def path
|
36
|
+
es_index.path.join(name)
|
37
|
+
end
|
38
|
+
memoize :path
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
describe Esearch do
|
5
|
+
let(:uri) { ENV.fetch('ESEARCH_TEST_URI', 'http://localhost:9200') }
|
6
|
+
|
7
|
+
SINGLE_NODE_SETTINGS = IceNine.deep_freeze(
|
8
|
+
:settings => {
|
9
|
+
:number_of_shards => 1,
|
10
|
+
:number_of_replicas => 0
|
11
|
+
}
|
12
|
+
)
|
13
|
+
|
14
|
+
let(:cluster) { Esearch::Cluster.connect(uri, Logger.new($stderr, :debug)) }
|
15
|
+
|
16
|
+
it 'should work' do
|
17
|
+
cluster.all_indices.status.indices.map do |index|
|
18
|
+
cluster.index(index.name).delete
|
19
|
+
end
|
20
|
+
|
21
|
+
index_a = cluster.index('test-a')
|
22
|
+
index_a.exist?.should be(false)
|
23
|
+
index_a.create(SINGLE_NODE_SETTINGS)
|
24
|
+
index_a.exist?.should be(true)
|
25
|
+
|
26
|
+
index_b = cluster.index('test-b')
|
27
|
+
index_b.create(SINGLE_NODE_SETTINGS)
|
28
|
+
|
29
|
+
indices = cluster.indices(%w(test-a test-b))
|
30
|
+
|
31
|
+
result_a = index_a.type('type-a').index({'foo' => 'bar'})
|
32
|
+
result_b = index_b.type('type-b').index({'foo' => 'baz'})
|
33
|
+
|
34
|
+
cluster.health(:wait_for_status => :green, :timeout => '10s')
|
35
|
+
|
36
|
+
indices.refresh
|
37
|
+
|
38
|
+
result = index_a.search({:query => { :match_all => {}}})
|
39
|
+
result.hits.map(&:source).should eql([{'foo' => 'bar'}])
|
40
|
+
|
41
|
+
result = indices.search({:query => { :match_all => {}}})
|
42
|
+
|
43
|
+
result.hits.map(&:source).to_set.should eql([{'foo' => 'bar'}, {'foo' => 'baz'}].to_set)
|
44
|
+
|
45
|
+
index_a.type('type-a').document(result_a.id).delete
|
46
|
+
|
47
|
+
document = index_b.type('type-b').document(result_b.id).get
|
48
|
+
document.source.should eql('foo' => 'baz')
|
49
|
+
end
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'esearch'
|
3
|
+
require 'devtools'
|
4
|
+
Devtools.init_spec_helper
|
5
|
+
|
6
|
+
module CommandHelper
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def expect_to_run_command(command)
|
10
|
+
let(:connection) { mock('Connection') }
|
11
|
+
let(:result) { mock('Result') }
|
12
|
+
|
13
|
+
before do
|
14
|
+
command.should_receive(:run).with(object, *expected_arguments).and_return(result)
|
15
|
+
end
|
16
|
+
|
17
|
+
it { should be(result) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_connection
|
21
|
+
let(:context) { mock('Context', :connection => connection, :path => Pathname.new('/foo')) }
|
22
|
+
let(:connection) { mock('Connection') }
|
23
|
+
|
24
|
+
let(:headers) { {'content-type' => 'application/json; charset=UTF-8'} }
|
25
|
+
let(:response) { mock('Response', :frozen? => true, :status => status, :headers => headers, :body => '{}') }
|
26
|
+
|
27
|
+
let(:status) { 200 }
|
28
|
+
|
29
|
+
before do
|
30
|
+
connection.should_receive(:run).with(expected_request).and_return(response)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def expect_exception(exception)
|
35
|
+
setup_connection
|
36
|
+
|
37
|
+
yield if block_given?
|
38
|
+
|
39
|
+
it 'should raise error' do
|
40
|
+
expect { subject }.to raise_error(exception)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def expect_result(result)
|
45
|
+
setup_connection
|
46
|
+
|
47
|
+
yield if block_given?
|
48
|
+
|
49
|
+
it { should eql(result) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def expect_presenter(presenter, &block)
|
53
|
+
expect_result(presenter.new({}), &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.included(descendant)
|
58
|
+
descendant.extend(ClassMethods)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
RSpec.configure do |config|
|
64
|
+
config.include(CommandHelper)
|
65
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Cluster, '.connect' do
|
4
|
+
subject { object.connect(*arguments) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
let(:connection) { mock('Connection') }
|
8
|
+
|
9
|
+
let(:arguments) { [:foo, :bar] }
|
10
|
+
|
11
|
+
before do
|
12
|
+
Esearch::Connection.should_receive(:build).with(:foo, :bar).and_return(connection)
|
13
|
+
end
|
14
|
+
|
15
|
+
it { should eql(described_class.new(connection)) }
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Cluster, '#index' do
|
4
|
+
subject { object.index(name) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(connection) }
|
7
|
+
let(:connection) { mock('Connection') }
|
8
|
+
let(:name) { 'foo' }
|
9
|
+
|
10
|
+
it { should eql(Esearch::Index.new(connection, name)) }
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Cluster, '#indices' do
|
4
|
+
subject { object.indices(names) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(connection) }
|
7
|
+
let(:connection) { mock('Connection') }
|
8
|
+
let(:names) { %w(foo bar) }
|
9
|
+
|
10
|
+
it { should eql(Esearch::Indices.new(connection, names)) }
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Command, '.run' do
|
4
|
+
subject { object.run(*arguments) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
|
8
|
+
let(:instance) { mock('Instance', :result => result) }
|
9
|
+
let(:result) { mock('Result') }
|
10
|
+
let(:arguments) { [:foo, :bar] }
|
11
|
+
|
12
|
+
it 'should instantiate object and return result' do
|
13
|
+
described_class.should_receive(:new).with(*arguments).and_return(instance)
|
14
|
+
should be(result)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Command::Cluster::Health, '.run' do
|
4
|
+
subject { object.run(context, options) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
let(:options) { mock('Options') }
|
8
|
+
|
9
|
+
let(:expected_request) do
|
10
|
+
Esearch::Request.new(:get, '/_cluster/health', {}, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
expect_presenter(Esearch::Presenter::Cluster::Health)
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Command::Document::Delete, '.run' do
|
4
|
+
subject { object.run(context) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
|
8
|
+
let(:expected_request) do
|
9
|
+
Esearch::Request.new(:delete, '/foo', {}, {})
|
10
|
+
end
|
11
|
+
|
12
|
+
expect_presenter(Esearch::Presenter::Document::Operation::Delete)
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Command::Document::Get, '.run' do
|
4
|
+
subject { object.run(context) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
|
8
|
+
let(:expected_request) do
|
9
|
+
Esearch::Request.new(:get, '/foo', {}, {})
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with status 200' do
|
13
|
+
expect_presenter(Esearch::Presenter::Document::Get)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with status 404' do
|
17
|
+
expect_result(nil) do
|
18
|
+
let(:status) { 404 }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with status 500' do
|
23
|
+
expect_exception(Esearch::ProtocolError) do
|
24
|
+
let(:status) { 500 }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Esearch::Command::Document::Index::Create, '.run' do
|
4
|
+
subject { object.run(context, document, options) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
let(:document) { mock('Document') }
|
8
|
+
let(:options) { { :foo => :bar } }
|
9
|
+
|
10
|
+
let(:expected_request) do
|
11
|
+
Esearch::Request.new(:post, '/foo', document, { :foo => :bar, :op_type => :create })
|
12
|
+
end
|
13
|
+
|
14
|
+
expect_presenter(Esearch::Presenter::Document::Operation::Index) do
|
15
|
+
let(:status) { 201 }
|
16
|
+
end
|
17
|
+
end
|