esearch 0.2.0

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.
Files changed (101) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +6 -0
  3. data/.travis.yml +19 -0
  4. data/Changelog.md +27 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.devtools +60 -0
  7. data/Guardfile +18 -0
  8. data/LICENSE +20 -0
  9. data/README.md +93 -0
  10. data/Rakefile +2 -0
  11. data/TODO +3 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +3 -0
  15. data/config/mutant.yml +3 -0
  16. data/config/reek.yml +103 -0
  17. data/config/yardstick.yml +2 -0
  18. data/esearch.gemspec +26 -0
  19. data/lib/esearch.rb +43 -0
  20. data/lib/esearch/cluster.rb +85 -0
  21. data/lib/esearch/command.rb +158 -0
  22. data/lib/esearch/command/cluster.rb +28 -0
  23. data/lib/esearch/command/document.rb +111 -0
  24. data/lib/esearch/command/exist.rb +37 -0
  25. data/lib/esearch/command/index.rb +66 -0
  26. data/lib/esearch/command/search.rb +24 -0
  27. data/lib/esearch/command/status.rb +24 -0
  28. data/lib/esearch/connection.rb +36 -0
  29. data/lib/esearch/document.rb +53 -0
  30. data/lib/esearch/index.rb +61 -0
  31. data/lib/esearch/indices.rb +52 -0
  32. data/lib/esearch/mixin.rb +111 -0
  33. data/lib/esearch/presenter.rb +43 -0
  34. data/lib/esearch/presenter/aspect.rb +17 -0
  35. data/lib/esearch/presenter/aspect/range.rb +63 -0
  36. data/lib/esearch/presenter/aspect/term.rb +19 -0
  37. data/lib/esearch/presenter/cluster.rb +84 -0
  38. data/lib/esearch/presenter/document.rb +102 -0
  39. data/lib/esearch/presenter/facet.rb +72 -0
  40. data/lib/esearch/presenter/hit.rb +70 -0
  41. data/lib/esearch/presenter/hits.rb +60 -0
  42. data/lib/esearch/presenter/index.rb +23 -0
  43. data/lib/esearch/presenter/search.rb +32 -0
  44. data/lib/esearch/presenter/status.rb +18 -0
  45. data/lib/esearch/request.rb +90 -0
  46. data/lib/esearch/type.rb +40 -0
  47. data/spec/integration/esearch/spike_spec.rb +50 -0
  48. data/spec/spec_helper.rb +65 -0
  49. data/spec/support/example_group_methods.rb +7 -0
  50. data/spec/support/ice_nine_config.rb +6 -0
  51. data/spec/unit/esearch/cluster/class_methods/connect_spec.rb +16 -0
  52. data/spec/unit/esearch/cluster/health_spec.rb +10 -0
  53. data/spec/unit/esearch/cluster/index_spec.rb +11 -0
  54. data/spec/unit/esearch/cluster/indices_spec.rb +11 -0
  55. data/spec/unit/esearch/cluster/path_spec.rb +11 -0
  56. data/spec/unit/esearch/command/class_methods/run_spec.rb +16 -0
  57. data/spec/unit/esearch/command/cluster/health/run_spec.rb +14 -0
  58. data/spec/unit/esearch/command/document/delete/run_spec.rb +13 -0
  59. data/spec/unit/esearch/command/document/get/result_spec.rb +27 -0
  60. data/spec/unit/esearch/command/document/index/create/run_spec.rb +17 -0
  61. data/spec/unit/esearch/command/document/index/run_create_spec.rb +17 -0
  62. data/spec/unit/esearch/command/document/index/run_spec.rb +15 -0
  63. data/spec/unit/esearch/command/document/index/run_update_spec.rb +15 -0
  64. data/spec/unit/esearch/command/document/index/update/run_spec.rb +15 -0
  65. data/spec/unit/esearch/command/exist/result_spec.rb +39 -0
  66. data/spec/unit/esearch/command/index/create/run_spec.rb +14 -0
  67. data/spec/unit/esearch/command/index/delete/run_spec.rb +13 -0
  68. data/spec/unit/esearch/command/index/refresh/run_spec.rb +13 -0
  69. data/spec/unit/esearch/command/result_spec.rb +68 -0
  70. data/spec/unit/esearch/command/search/run_spec.rb +14 -0
  71. data/spec/unit/esearch/command/status/run_spec.rb +13 -0
  72. data/spec/unit/esearch/connection/class_methods/build_spec.rb +29 -0
  73. data/spec/unit/esearch/connection/run_spec.rb +36 -0
  74. data/spec/unit/esearch/document/connection_spec.rb +12 -0
  75. data/spec/unit/esearch/document/delete_spec.rb +12 -0
  76. data/spec/unit/esearch/document/get_spec.rb +12 -0
  77. data/spec/unit/esearch/index/create_spec.rb +12 -0
  78. data/spec/unit/esearch/index/delete_spec.rb +11 -0
  79. data/spec/unit/esearch/index/type_spec.rb +12 -0
  80. data/spec/unit/esearch/indices/all/path_spec.rb +12 -0
  81. data/spec/unit/esearch/mixin/document/index_create_spec.rb +31 -0
  82. data/spec/unit/esearch/mixin/document/index_spec.rb +31 -0
  83. data/spec/unit/esearch/mixin/document/index_update_spec.rb +31 -0
  84. data/spec/unit/esearch/mixin/exist/exist_predicate_spec.rb +16 -0
  85. data/spec/unit/esearch/mixin/index/refresh_spec.rb +16 -0
  86. data/spec/unit/esearch/mixin/index/status_spec.rb +16 -0
  87. data/spec/unit/esearch/mixin/search/search_spec.rb +18 -0
  88. data/spec/unit/esearch/presenter/aspect/range/from_spec.rb +24 -0
  89. data/spec/unit/esearch/presenter/aspect/range/to_spec.rb +24 -0
  90. data/spec/unit/esearch/presenter/class_methods/new_spec.rb +37 -0
  91. data/spec/unit/esearch/presenter/facet/build_spec.rb +26 -0
  92. data/spec/unit/esearch/presenter/facet/class_methods/build_spec.rb +26 -0
  93. data/spec/unit/esearch/presenter/hit/fields_spec.rb +24 -0
  94. data/spec/unit/esearch/presenter/hit/source_spec.rb +24 -0
  95. data/spec/unit/esearch/presenter/hits/each_spec.rb +15 -0
  96. data/spec/unit/esearch/presenter/hits/size_spec.rb +13 -0
  97. data/spec/unit/esearch/request/initialize_spec.rb +39 -0
  98. data/spec/unit/esearch/request/run_spec.rb +39 -0
  99. data/spec/unit/esearch/type/connection_spec.rb +15 -0
  100. data/spec/unit/esearch/type/document_spec.rb +12 -0
  101. 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
@@ -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
@@ -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,7 @@
1
+ module Spec
2
+ module ExampleGroupMethods
3
+ def testing_block_passing_broken?
4
+ RUBY_PLATFORM.include?('java') && JRUBY_VERSION <= '1.7.0.dev' && RUBY_VERSION >= '1.9.2'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module IceNine
2
+ class Freezer
3
+ class RSpec < NoFreeze
4
+ end
5
+ end
6
+ 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,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Esearch::Cluster, '#health' do
4
+ subject { object.health }
5
+
6
+ let(:object) { described_class.new(connection) }
7
+ let(:expected_arguments) { [{}] }
8
+
9
+ expect_to_run_command(Esearch::Command::Cluster::Health)
10
+ 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,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Esearch::Cluster, '#path' do
4
+ subject { object.path }
5
+
6
+ let(:object) { described_class.new(connection) }
7
+
8
+ let(:connection) { mock('Connection') }
9
+
10
+ it { should eql(Pathname.new('/')) }
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