elasticsearch-model 0.0.1 → 0.1.0.rc1

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 (64) hide show
  1. data/.gitignore +3 -0
  2. data/LICENSE.txt +1 -1
  3. data/README.md +669 -8
  4. data/Rakefile +52 -0
  5. data/elasticsearch-model.gemspec +48 -17
  6. data/examples/activerecord_article.rb +77 -0
  7. data/examples/activerecord_associations.rb +153 -0
  8. data/examples/couchbase_article.rb +66 -0
  9. data/examples/datamapper_article.rb +71 -0
  10. data/examples/mongoid_article.rb +68 -0
  11. data/examples/ohm_article.rb +70 -0
  12. data/examples/riak_article.rb +52 -0
  13. data/gemfiles/3.gemfile +11 -0
  14. data/gemfiles/4.gemfile +11 -0
  15. data/lib/elasticsearch/model.rb +151 -1
  16. data/lib/elasticsearch/model/adapter.rb +145 -0
  17. data/lib/elasticsearch/model/adapters/active_record.rb +97 -0
  18. data/lib/elasticsearch/model/adapters/default.rb +44 -0
  19. data/lib/elasticsearch/model/adapters/mongoid.rb +90 -0
  20. data/lib/elasticsearch/model/callbacks.rb +35 -0
  21. data/lib/elasticsearch/model/client.rb +61 -0
  22. data/lib/elasticsearch/model/importing.rb +94 -0
  23. data/lib/elasticsearch/model/indexing.rb +332 -0
  24. data/lib/elasticsearch/model/naming.rb +101 -0
  25. data/lib/elasticsearch/model/proxy.rb +127 -0
  26. data/lib/elasticsearch/model/response.rb +70 -0
  27. data/lib/elasticsearch/model/response/base.rb +44 -0
  28. data/lib/elasticsearch/model/response/pagination.rb +96 -0
  29. data/lib/elasticsearch/model/response/records.rb +71 -0
  30. data/lib/elasticsearch/model/response/result.rb +50 -0
  31. data/lib/elasticsearch/model/response/results.rb +32 -0
  32. data/lib/elasticsearch/model/searching.rb +107 -0
  33. data/lib/elasticsearch/model/serializing.rb +35 -0
  34. data/lib/elasticsearch/model/support/forwardable.rb +44 -0
  35. data/lib/elasticsearch/model/version.rb +1 -1
  36. data/test/integration/active_record_associations_parent_child.rb +138 -0
  37. data/test/integration/active_record_associations_test.rb +306 -0
  38. data/test/integration/active_record_basic_test.rb +139 -0
  39. data/test/integration/active_record_import_test.rb +74 -0
  40. data/test/integration/active_record_namespaced_model_test.rb +49 -0
  41. data/test/integration/active_record_pagination_test.rb +109 -0
  42. data/test/integration/mongoid_basic_test.rb +178 -0
  43. data/test/test_helper.rb +57 -0
  44. data/test/unit/adapter_active_record_test.rb +93 -0
  45. data/test/unit/adapter_default_test.rb +31 -0
  46. data/test/unit/adapter_mongoid_test.rb +87 -0
  47. data/test/unit/adapter_test.rb +69 -0
  48. data/test/unit/callbacks_test.rb +30 -0
  49. data/test/unit/client_test.rb +27 -0
  50. data/test/unit/importing_test.rb +97 -0
  51. data/test/unit/indexing_test.rb +364 -0
  52. data/test/unit/module_test.rb +46 -0
  53. data/test/unit/naming_test.rb +76 -0
  54. data/test/unit/proxy_test.rb +88 -0
  55. data/test/unit/response_base_test.rb +40 -0
  56. data/test/unit/response_pagination_test.rb +159 -0
  57. data/test/unit/response_records_test.rb +87 -0
  58. data/test/unit/response_result_test.rb +52 -0
  59. data/test/unit/response_results_test.rb +31 -0
  60. data/test/unit/response_test.rb +57 -0
  61. data/test/unit/searching_search_request_test.rb +73 -0
  62. data/test/unit/searching_test.rb +39 -0
  63. data/test/unit/serializing_test.rb +17 -0
  64. metadata +418 -11
@@ -0,0 +1,88 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::SearchTest < Test::Unit::TestCase
4
+ context "Searching module" do
5
+ class ::DummyProxyModel
6
+ include Elasticsearch::Model::Proxy
7
+
8
+ def self.foo
9
+ 'classy foo'
10
+ end
11
+
12
+ def bar
13
+ 'insta barr'
14
+ end
15
+
16
+ def as_json(options)
17
+ {foo: 'bar'}
18
+ end
19
+ end
20
+
21
+ class ::DummyProxyModelWithCallbacks
22
+ def self.before_save(&block)
23
+ (@callbacks ||= {})[block.hash] = block
24
+ end
25
+
26
+ def changed_attributes; [:foo]; end
27
+
28
+ def changes
29
+ {:foo => ['One', 'Two']}
30
+ end
31
+ end
32
+
33
+ should "setup the class proxy method" do
34
+ assert_respond_to DummyProxyModel, :__elasticsearch__
35
+ end
36
+
37
+ should "setup the instance proxy method" do
38
+ assert_respond_to DummyProxyModel.new, :__elasticsearch__
39
+ end
40
+
41
+ should "register the hook for before_save callback" do
42
+ ::DummyProxyModelWithCallbacks.expects(:before_save).returns(true)
43
+ DummyProxyModelWithCallbacks.__send__ :include, Elasticsearch::Model::Proxy
44
+ end
45
+
46
+ should "set the @__changed_attributes variable before save" do
47
+ instance = ::DummyProxyModelWithCallbacks.new
48
+ instance.__elasticsearch__.expects(:instance_variable_set).with do |name, value|
49
+ assert_equal :@__changed_attributes, name
50
+ assert_equal({foo: 'Two'}, value)
51
+ end
52
+
53
+ ::DummyProxyModelWithCallbacks.__send__ :include, Elasticsearch::Model::Proxy
54
+
55
+ ::DummyProxyModelWithCallbacks.instance_variable_get(:@callbacks).each do |n,b|
56
+ instance.instance_eval(&b)
57
+ end
58
+ end
59
+
60
+ should "delegate methods to the target" do
61
+ assert_respond_to DummyProxyModel.__elasticsearch__, :foo
62
+ assert_respond_to DummyProxyModel.new.__elasticsearch__, :bar
63
+
64
+ assert_raise(NoMethodError) { DummyProxyModel.__elasticsearch__.xoxo }
65
+ assert_raise(NoMethodError) { DummyProxyModel.new.__elasticsearch__.xoxo }
66
+
67
+ assert_equal 'classy foo', DummyProxyModel.__elasticsearch__.foo
68
+ assert_equal 'insta barr', DummyProxyModel.new.__elasticsearch__.bar
69
+ end
70
+
71
+ should "return the proxy class from instance proxy" do
72
+ assert_equal Elasticsearch::Model::Proxy::ClassMethodsProxy, DummyProxyModel.new.__elasticsearch__.class.class
73
+ end
74
+
75
+ should "return the origin class from instance proxy" do
76
+ assert_equal DummyProxyModel, DummyProxyModel.new.__elasticsearch__.klass
77
+ end
78
+
79
+ should "delegate as_json from the proxy to target" do
80
+ assert_equal({foo: 'bar'}, DummyProxyModel.new.__elasticsearch__.as_json)
81
+ end
82
+
83
+ should "have inspect method indicating the proxy" do
84
+ assert_match /PROXY/, DummyProxyModel.__elasticsearch__.inspect
85
+ assert_match /PROXY/, DummyProxyModel.new.__elasticsearch__.inspect
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::BaseTest < Test::Unit::TestCase
4
+ context "Response base module" do
5
+ class OriginClass
6
+ def self.index_name; 'foo'; end
7
+ def self.document_type; 'bar'; end
8
+ end
9
+
10
+ class DummyBaseClass
11
+ include Elasticsearch::Model::Response::Base
12
+ end
13
+
14
+ RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [] } }
15
+
16
+ setup do
17
+ @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*'
18
+ @response = Elasticsearch::Model::Response::Response.new OriginClass, @search
19
+ @search.stubs(:execute!).returns(RESPONSE)
20
+ end
21
+
22
+ should "access klass, response, total and max_score" do
23
+ r = DummyBaseClass.new OriginClass, @response
24
+
25
+ assert_equal OriginClass, r.klass
26
+ assert_equal @response, r.response
27
+ assert_equal RESPONSE, r.response.response
28
+ assert_equal 123, r.total
29
+ assert_equal 456, r.max_score
30
+ end
31
+
32
+ should "have abstract methods results and records" do
33
+ r = DummyBaseClass.new OriginClass, @response
34
+
35
+ assert_raise(Elasticsearch::Model::NotImplemented) { |e| r.results }
36
+ assert_raise(Elasticsearch::Model::NotImplemented) { |e| r.records }
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,159 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::ResponsePaginationTest < Test::Unit::TestCase
4
+ context "Response pagination" do
5
+ class ModelClass
6
+ include ::Kaminari::ConfigurationMethods
7
+
8
+ def self.index_name; 'foo'; end
9
+ def self.document_type; 'bar'; end
10
+ end
11
+
12
+ RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'},
13
+ 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
14
+
15
+ setup do
16
+ @search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*'
17
+ @response = Elasticsearch::Model::Response::Response.new ModelClass, @search, RESPONSE
18
+ @response.klass.stubs(:client).returns mock('client')
19
+ end
20
+
21
+ should "have pagination methods" do
22
+ assert_respond_to @response, :page
23
+ assert_respond_to @response, :limit_value
24
+ assert_respond_to @response, :offset_value
25
+ assert_respond_to @response, :limit
26
+ assert_respond_to @response, :offset
27
+ assert_respond_to @response, :total_count
28
+ end
29
+
30
+ context "#page method" do
31
+ should "advance the from/size" do
32
+ @response.klass.client
33
+ .expects(:search)
34
+ .with do |definition|
35
+ assert_equal 25, definition[:from]
36
+ assert_equal 25, definition[:size]
37
+ end
38
+ .returns(RESPONSE)
39
+
40
+ assert_nil @response.search.definition[:from]
41
+ assert_nil @response.search.definition[:size]
42
+
43
+ @response.page(2).to_a
44
+ assert_equal 25, @response.search.definition[:from]
45
+ assert_equal 25, @response.search.definition[:size]
46
+ end
47
+
48
+ should "advance the from/size further" do
49
+ @response.klass.client
50
+ .expects(:search)
51
+ .with do |definition|
52
+ assert_equal 75, definition[:from]
53
+ assert_equal 25, definition[:size]
54
+ end
55
+ .returns(RESPONSE)
56
+
57
+ @response.page(4).to_a
58
+ assert_equal 75, @response.search.definition[:from]
59
+ assert_equal 25, @response.search.definition[:size]
60
+ end
61
+ end
62
+
63
+ context "limit/offset readers" do
64
+ should "return the default" do
65
+ assert_equal 0, @response.limit_value
66
+ assert_equal 0, @response.offset_value
67
+ end
68
+
69
+ should "return the value from URL parameters" do
70
+ search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*', size: 10, from: 50
71
+ @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE
72
+
73
+ assert_equal 10, @response.limit_value
74
+ assert_equal 50, @response.offset_value
75
+ end
76
+
77
+ should "return the value from body" do
78
+ search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, { query: { match_all: {} }, from: 10, size: 50 }
79
+ @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE
80
+
81
+ assert_equal 50, @response.limit_value
82
+ assert_equal 10, @response.offset_value
83
+ end
84
+ end
85
+
86
+ context "limit setter" do
87
+ setup do
88
+ @response.records
89
+ @response.results
90
+ end
91
+
92
+ should "set the values" do
93
+ @response.limit(35)
94
+ assert_equal 35, @response.search.definition[:size]
95
+ end
96
+
97
+ should "reset the variables" do
98
+ @response.limit(35)
99
+
100
+ assert_nil @response.instance_variable_get(:@response)
101
+ assert_nil @response.instance_variable_get(:@records)
102
+ assert_nil @response.instance_variable_get(:@results)
103
+ end
104
+ end
105
+
106
+ context "offset setter" do
107
+ setup do
108
+ @response.records
109
+ @response.results
110
+ end
111
+
112
+ should "set the values" do
113
+ @response.offset(15)
114
+ assert_equal 15, @response.search.definition[:from]
115
+ end
116
+
117
+ should "reset the variables" do
118
+ @response.offset(35)
119
+
120
+ assert_nil @response.instance_variable_get(:@response)
121
+ assert_nil @response.instance_variable_get(:@records)
122
+ assert_nil @response.instance_variable_get(:@results)
123
+ end
124
+ end
125
+
126
+ context "total" do
127
+ should "return the number of hits" do
128
+ @response.expects(:results).returns(mock('results', total: 100))
129
+ assert_equal 100, @response.total_count
130
+ end
131
+ end
132
+
133
+ context "results" do
134
+ setup do
135
+ @search.stubs(:execute!).returns RESPONSE
136
+ end
137
+
138
+ should "return current page and total count" do
139
+ assert_equal 1, @response.page(1).results.current_page
140
+ assert_equal 100, @response.results.total_count
141
+
142
+ assert_equal 5, @response.page(5).results.current_page
143
+ end
144
+ end
145
+
146
+ context "records" do
147
+ setup do
148
+ @search.stubs(:execute!).returns RESPONSE
149
+ end
150
+
151
+ should "return current page and total count" do
152
+ assert_equal 1, @response.page(1).records.current_page
153
+ assert_equal 100, @response.records.total_count
154
+
155
+ assert_equal 5, @response.page(5).records.current_page
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::RecordsTest < Test::Unit::TestCase
4
+ context "Response records" do
5
+ class DummyCollection
6
+ include Enumerable
7
+
8
+ def each(&block); ['FOO'].each(&block); end
9
+ def size; ['FOO'].size; end
10
+ def empty?; ['FOO'].empty?; end
11
+ def foo; 'BAR'; end
12
+ end
13
+
14
+ class DummyModel
15
+ def self.index_name; 'foo'; end
16
+ def self.document_type; 'bar'; end
17
+
18
+ def self.find(*args)
19
+ DummyCollection.new
20
+ end
21
+ end
22
+
23
+ RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'foo' => 'bar'}] } }
24
+ RESULTS = Elasticsearch::Model::Response::Results.new DummyModel, RESPONSE
25
+
26
+ setup do
27
+ search = Elasticsearch::Model::Searching::SearchRequest.new DummyModel, '*'
28
+ search.stubs(:execute!).returns RESPONSE
29
+
30
+ response = Elasticsearch::Model::Response::Response.new DummyModel, search
31
+ @records = Elasticsearch::Model::Response::Records.new DummyModel, response
32
+ end
33
+
34
+ should "access the records" do
35
+ assert_respond_to @records, :records
36
+ assert_equal 1, @records.records.size
37
+ assert_equal 'FOO', @records.records.first
38
+ end
39
+
40
+ should "delegate Enumerable methods to records" do
41
+ assert ! @records.empty?
42
+ assert_equal 'FOO', @records.first
43
+ end
44
+
45
+ should "delegate methods to records" do
46
+ assert_respond_to @records, :foo
47
+ assert_equal 'BAR', @records.foo
48
+ end
49
+
50
+ should "have each_with_hit method" do
51
+ @records.each_with_hit do |record, hit|
52
+ assert_equal 'FOO', record
53
+ assert_equal 'bar', hit.foo
54
+ end
55
+ end
56
+
57
+ should "have map_with_hit method" do
58
+ assert_equal ['FOO---bar'], @records.map_with_hit { |record, hit| "#{record}---#{hit.foo}" }
59
+ end
60
+
61
+ context "with adapter" do
62
+ module DummyAdapter
63
+ module RecordsMixin
64
+ def records
65
+ ['FOOBAR']
66
+ end
67
+ end
68
+
69
+ def records_mixin
70
+ RecordsMixin
71
+ end; module_function :records_mixin
72
+ end
73
+
74
+ should "delegate the records method to the adapter" do
75
+ Elasticsearch::Model::Adapter.expects(:from_class)
76
+ .with(DummyModel)
77
+ .returns(DummyAdapter)
78
+
79
+ @records = Elasticsearch::Model::Response::Records.new DummyModel,
80
+ RESPONSE
81
+
82
+ assert_equal ['FOOBAR'], @records.records
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::ResultTest < Test::Unit::TestCase
4
+ context "Response result" do
5
+
6
+ should "have method access to properties" do
7
+ result = Elasticsearch::Model::Response::Result.new foo: 'bar', bar: { bam: 'baz' }
8
+
9
+ assert_respond_to result, :foo
10
+ assert_respond_to result, :bar
11
+
12
+ assert_equal 'bar', result.foo
13
+ assert_equal 'baz', result.bar.bam
14
+
15
+ assert_raise(NoMethodError) { result.xoxo }
16
+ end
17
+
18
+ should "delegate method calls to `_source` when available" do
19
+ result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: 'baz' }
20
+
21
+ assert_respond_to result, :foo
22
+ assert_respond_to result, :_source
23
+ assert_respond_to result, :bar
24
+
25
+ assert_equal 'bar', result.foo
26
+ assert_equal 'baz', result._source.bar
27
+ assert_equal 'baz', result.bar
28
+ end
29
+
30
+ should "delegate methods to @result" do
31
+ result = Elasticsearch::Model::Response::Result.new foo: 'bar'
32
+
33
+ assert_equal 'bar', result.foo
34
+ assert_equal 'bar', result.fetch('foo')
35
+ assert_equal 'moo', result.fetch('NOT_EXIST', 'moo')
36
+
37
+ assert_respond_to result, :to_hash
38
+ assert_equal({'foo' => 'bar'}, result.to_hash)
39
+
40
+ assert_raise(NoMethodError) { result.does_not_exist }
41
+ end
42
+
43
+ should "delegate as_json to @result even when ActiveSupport changed half of Ruby" do
44
+ require 'active_support/json/encoding'
45
+ result = Elasticsearch::Model::Response::Result.new foo: 'bar'
46
+
47
+ result.instance_variable_get(:@result).expects(:as_json)
48
+ result.as_json(except: 'foo')
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::ResultsTest < Test::Unit::TestCase
4
+ context "Response results" do
5
+ class OriginClass
6
+ def self.index_name; 'foo'; end
7
+ def self.document_type; 'bar'; end
8
+ end
9
+
10
+ RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'foo' => 'bar'}] } }
11
+
12
+ setup do
13
+ @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*'
14
+ @response = Elasticsearch::Model::Response::Response.new OriginClass, @search
15
+ @results = Elasticsearch::Model::Response::Results.new OriginClass, @response
16
+ @search.stubs(:execute!).returns(RESPONSE)
17
+ end
18
+
19
+ should "access the results" do
20
+ assert_respond_to @results, :results
21
+ assert_equal 1, @results.results.size
22
+ assert_equal 'bar', @results.results.first.foo
23
+ end
24
+
25
+ should "delegate Enumerable methods to results" do
26
+ assert ! @results.empty?
27
+ assert_equal 'bar', @results.first.foo
28
+ end
29
+
30
+ end
31
+ end