elasticsearch-model-queryable 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +695 -0
- data/Rakefile +59 -0
- data/elasticsearch-model.gemspec +57 -0
- data/examples/activerecord_article.rb +77 -0
- data/examples/activerecord_associations.rb +162 -0
- data/examples/couchbase_article.rb +66 -0
- data/examples/datamapper_article.rb +71 -0
- data/examples/mongoid_article.rb +68 -0
- data/examples/ohm_article.rb +70 -0
- data/examples/riak_article.rb +52 -0
- data/gemfiles/3.0.gemfile +12 -0
- data/gemfiles/4.0.gemfile +11 -0
- data/lib/elasticsearch/model/adapter.rb +145 -0
- data/lib/elasticsearch/model/adapters/active_record.rb +104 -0
- data/lib/elasticsearch/model/adapters/default.rb +50 -0
- data/lib/elasticsearch/model/adapters/mongoid.rb +92 -0
- data/lib/elasticsearch/model/callbacks.rb +35 -0
- data/lib/elasticsearch/model/client.rb +61 -0
- data/lib/elasticsearch/model/ext/active_record.rb +14 -0
- data/lib/elasticsearch/model/hash_wrapper.rb +15 -0
- data/lib/elasticsearch/model/importing.rb +144 -0
- data/lib/elasticsearch/model/indexing.rb +472 -0
- data/lib/elasticsearch/model/naming.rb +101 -0
- data/lib/elasticsearch/model/proxy.rb +127 -0
- data/lib/elasticsearch/model/response/base.rb +44 -0
- data/lib/elasticsearch/model/response/pagination.rb +173 -0
- data/lib/elasticsearch/model/response/records.rb +69 -0
- data/lib/elasticsearch/model/response/result.rb +63 -0
- data/lib/elasticsearch/model/response/results.rb +31 -0
- data/lib/elasticsearch/model/response.rb +71 -0
- data/lib/elasticsearch/model/searching.rb +107 -0
- data/lib/elasticsearch/model/serializing.rb +35 -0
- data/lib/elasticsearch/model/version.rb +5 -0
- data/lib/elasticsearch/model.rb +157 -0
- data/test/integration/active_record_associations_parent_child.rb +139 -0
- data/test/integration/active_record_associations_test.rb +307 -0
- data/test/integration/active_record_basic_test.rb +179 -0
- data/test/integration/active_record_custom_serialization_test.rb +62 -0
- data/test/integration/active_record_import_test.rb +100 -0
- data/test/integration/active_record_namespaced_model_test.rb +49 -0
- data/test/integration/active_record_pagination_test.rb +132 -0
- data/test/integration/mongoid_basic_test.rb +193 -0
- data/test/test_helper.rb +63 -0
- data/test/unit/adapter_active_record_test.rb +140 -0
- data/test/unit/adapter_default_test.rb +41 -0
- data/test/unit/adapter_mongoid_test.rb +102 -0
- data/test/unit/adapter_test.rb +69 -0
- data/test/unit/callbacks_test.rb +31 -0
- data/test/unit/client_test.rb +27 -0
- data/test/unit/importing_test.rb +176 -0
- data/test/unit/indexing_test.rb +478 -0
- data/test/unit/module_test.rb +57 -0
- data/test/unit/naming_test.rb +76 -0
- data/test/unit/proxy_test.rb +89 -0
- data/test/unit/response_base_test.rb +40 -0
- data/test/unit/response_pagination_kaminari_test.rb +189 -0
- data/test/unit/response_pagination_will_paginate_test.rb +208 -0
- data/test/unit/response_records_test.rb +91 -0
- data/test/unit/response_result_test.rb +90 -0
- data/test/unit/response_results_test.rb +31 -0
- data/test/unit/response_test.rb +67 -0
- data/test/unit/searching_search_request_test.rb +78 -0
- data/test/unit/searching_test.rb +41 -0
- data/test/unit/serializing_test.rb +17 -0
- metadata +466 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'mongoid'
|
5
|
+
session = Moped::Connection.new("localhost", 27017, 0.5)
|
6
|
+
session.connect
|
7
|
+
ENV["MONGODB_AVAILABLE"] = 'yes'
|
8
|
+
rescue LoadError, Moped::Errors::ConnectionFailure => e
|
9
|
+
$stderr.puts "MongoDB not installed or running: #{e}"
|
10
|
+
end
|
11
|
+
|
12
|
+
if ENV["MONGODB_AVAILABLE"]
|
13
|
+
$stderr.puts "Mongoid #{Mongoid::VERSION}", '-'*80
|
14
|
+
|
15
|
+
logger = ::Logger.new($stderr)
|
16
|
+
logger.formatter = lambda { |s, d, p, m| " #{m.ansi(:faint, :cyan)}\n" }
|
17
|
+
logger.level = ::Logger::DEBUG
|
18
|
+
|
19
|
+
Mongoid.logger = logger unless ENV['QUIET']
|
20
|
+
Moped.logger = logger unless ENV['QUIET']
|
21
|
+
|
22
|
+
Mongoid.connect_to 'mongoid_articles'
|
23
|
+
|
24
|
+
module Elasticsearch
|
25
|
+
module Model
|
26
|
+
class MongoidBasicIntegrationTest < Elasticsearch::Test::IntegrationTestCase
|
27
|
+
|
28
|
+
class ::MongoidArticle
|
29
|
+
include Mongoid::Document
|
30
|
+
include Elasticsearch::Model
|
31
|
+
include Elasticsearch::Model::Callbacks
|
32
|
+
|
33
|
+
field :id, type: String
|
34
|
+
field :title, type: String
|
35
|
+
attr_accessible :title if respond_to? :attr_accessible
|
36
|
+
|
37
|
+
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
|
38
|
+
mapping do
|
39
|
+
indexes :title, type: 'string', analyzer: 'snowball'
|
40
|
+
indexes :created_at, type: 'date'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def as_indexed_json(options={})
|
45
|
+
as_json(except: [:id, :_id])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Mongoid integration" do
|
50
|
+
setup do
|
51
|
+
Elasticsearch::Model::Adapter.register \
|
52
|
+
Elasticsearch::Model::Adapter::Mongoid,
|
53
|
+
lambda { |klass| !!defined?(::Mongoid::Document) && klass.ancestors.include?(::Mongoid::Document) }
|
54
|
+
|
55
|
+
MongoidArticle.__elasticsearch__.create_index! force: true
|
56
|
+
|
57
|
+
MongoidArticle.delete_all
|
58
|
+
|
59
|
+
MongoidArticle.create! title: 'Test'
|
60
|
+
MongoidArticle.create! title: 'Testing Coding'
|
61
|
+
MongoidArticle.create! title: 'Coding'
|
62
|
+
|
63
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
64
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
65
|
+
end
|
66
|
+
|
67
|
+
should "index and find a document" do
|
68
|
+
response = MongoidArticle.search('title:test')
|
69
|
+
|
70
|
+
assert response.any?
|
71
|
+
|
72
|
+
assert_equal 2, response.results.size
|
73
|
+
assert_equal 2, response.records.size
|
74
|
+
|
75
|
+
assert_instance_of Elasticsearch::Model::Response::Result, response.results.first
|
76
|
+
assert_instance_of MongoidArticle, response.records.first
|
77
|
+
|
78
|
+
assert_equal 'Test', response.results.first.title
|
79
|
+
assert_equal 'Test', response.records.first.title
|
80
|
+
end
|
81
|
+
|
82
|
+
should "iterate over results" do
|
83
|
+
response = MongoidArticle.search('title:test')
|
84
|
+
|
85
|
+
assert_equal ['Test', 'Testing Coding'], response.results.map(&:title)
|
86
|
+
assert_equal ['Test', 'Testing Coding'], response.records.map(&:title)
|
87
|
+
end
|
88
|
+
|
89
|
+
should "access results from records" do
|
90
|
+
response = MongoidArticle.search('title:test')
|
91
|
+
|
92
|
+
response.records.each_with_hit do |r, h|
|
93
|
+
assert_not_nil h._score
|
94
|
+
assert_not_nil h._source.title
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
should "preserve the search results order for records" do
|
99
|
+
response = MongoidArticle.search('title:code')
|
100
|
+
|
101
|
+
response.records.each_with_hit do |r, h|
|
102
|
+
assert_equal h._id, r.id.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
response.records.map_with_hit do |r, h|
|
106
|
+
assert_equal h._id, r.id.to_s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
should "remove document from index on destroy" do
|
111
|
+
article = MongoidArticle.first
|
112
|
+
|
113
|
+
article.destroy
|
114
|
+
assert_equal 2, MongoidArticle.count
|
115
|
+
|
116
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
117
|
+
|
118
|
+
response = MongoidArticle.search 'title:test'
|
119
|
+
|
120
|
+
assert_equal 1, response.results.size
|
121
|
+
assert_equal 1, response.records.size
|
122
|
+
end
|
123
|
+
|
124
|
+
should "index updates to the document" do
|
125
|
+
article = MongoidArticle.first
|
126
|
+
|
127
|
+
article.title = 'Writing'
|
128
|
+
article.save
|
129
|
+
|
130
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
131
|
+
|
132
|
+
response = MongoidArticle.search 'title:write'
|
133
|
+
|
134
|
+
assert_equal 1, response.results.size
|
135
|
+
assert_equal 1, response.records.size
|
136
|
+
end
|
137
|
+
|
138
|
+
should "return results for a DSL search" do
|
139
|
+
response = MongoidArticle.search query: { match: { title: { query: 'test' } } }
|
140
|
+
|
141
|
+
assert_equal 2, response.results.size
|
142
|
+
assert_equal 2, response.records.size
|
143
|
+
end
|
144
|
+
|
145
|
+
should "return a paged collection" do
|
146
|
+
response = MongoidArticle.search query: { match: { title: { query: 'test' } } },
|
147
|
+
size: 2,
|
148
|
+
from: 1
|
149
|
+
|
150
|
+
assert_equal 1, response.results.size
|
151
|
+
assert_equal 1, response.records.size
|
152
|
+
|
153
|
+
assert_equal 'Testing Coding', response.results.first.title
|
154
|
+
assert_equal 'Testing Coding', response.records.first.title
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
context "importing" do
|
159
|
+
setup do
|
160
|
+
MongoidArticle.delete_all
|
161
|
+
97.times { |i| MongoidArticle.create! title: "Test #{i}" }
|
162
|
+
MongoidArticle.__elasticsearch__.create_index! force: true
|
163
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
164
|
+
end
|
165
|
+
|
166
|
+
should "import all the documents" do
|
167
|
+
assert_equal 97, MongoidArticle.count
|
168
|
+
|
169
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
170
|
+
assert_equal 0, MongoidArticle.search('*').results.total
|
171
|
+
|
172
|
+
batches = 0
|
173
|
+
errors = MongoidArticle.import(batch_size: 10) do |response|
|
174
|
+
batches += 1
|
175
|
+
end
|
176
|
+
|
177
|
+
assert_equal 0, errors
|
178
|
+
assert_equal 10, batches
|
179
|
+
|
180
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
181
|
+
assert_equal 97, MongoidArticle.search('*').results.total
|
182
|
+
|
183
|
+
response = MongoidArticle.search('test')
|
184
|
+
assert response.results.any?, "Search has not returned results: #{response.to_a}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
RUBY_1_8 = defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
|
2
|
+
|
3
|
+
exit(0) if RUBY_1_8
|
4
|
+
|
5
|
+
require 'simplecov' and SimpleCov.start { add_filter "/test|test_/" } if ENV["COVERAGE"]
|
6
|
+
|
7
|
+
# Register `at_exit` handler for integration tests shutdown.
|
8
|
+
# MUST be called before requiring `test/unit`.
|
9
|
+
at_exit { Elasticsearch::Test::IntegrationTestCase.__run_at_exit_hooks }
|
10
|
+
|
11
|
+
puts '-'*80
|
12
|
+
|
13
|
+
if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
|
14
|
+
require 'test-unit'
|
15
|
+
require 'mocha/test_unit'
|
16
|
+
else
|
17
|
+
require 'minitest/autorun'
|
18
|
+
require 'mocha/mini_test'
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'shoulda-context'
|
22
|
+
|
23
|
+
require 'turn' unless ENV["TM_FILEPATH"] || ENV["NOTURN"] || defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
|
24
|
+
|
25
|
+
require 'ansi'
|
26
|
+
require 'oj'
|
27
|
+
|
28
|
+
require 'active_model'
|
29
|
+
|
30
|
+
require 'kaminari'
|
31
|
+
|
32
|
+
require 'elasticsearch/model'
|
33
|
+
|
34
|
+
require 'elasticsearch/extensions/test/cluster'
|
35
|
+
require 'elasticsearch/extensions/test/startup_shutdown'
|
36
|
+
|
37
|
+
module Elasticsearch
|
38
|
+
module Test
|
39
|
+
class IntegrationTestCase < ::Test::Unit::TestCase
|
40
|
+
extend Elasticsearch::Extensions::Test::StartupShutdown
|
41
|
+
|
42
|
+
startup { Elasticsearch::Extensions::Test::Cluster.start(nodes: 1) if ENV['SERVER'] and not Elasticsearch::Extensions::Test::Cluster.running? }
|
43
|
+
shutdown { Elasticsearch::Extensions::Test::Cluster.stop if ENV['SERVER'] && started? }
|
44
|
+
context "IntegrationTest" do; should "noop on Ruby 1.8" do; end; end if RUBY_1_8
|
45
|
+
|
46
|
+
def setup
|
47
|
+
ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
|
48
|
+
logger = ::Logger.new(STDERR)
|
49
|
+
logger.formatter = lambda { |s, d, p, m| "\e[2;36m#{m}\e[0m\n" }
|
50
|
+
ActiveRecord::Base.logger = logger unless ENV['QUIET']
|
51
|
+
|
52
|
+
ActiveRecord::LogSubscriber.colorize_logging = false
|
53
|
+
ActiveRecord::Migration.verbose = false
|
54
|
+
|
55
|
+
tracer = ::Logger.new(STDERR)
|
56
|
+
tracer.formatter = lambda { |s, d, p, m| "#{m.gsub(/^.*$/) { |n| ' ' + n }.ansi(:faint)}\n" }
|
57
|
+
|
58
|
+
Elasticsearch::Model.client = Elasticsearch::Client.new host: "localhost:#{(ENV['TEST_CLUSTER_PORT'] || 9250)}",
|
59
|
+
tracer: (ENV['QUIET'] ? nil : tracer)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::AdapterActiveRecordTest < Test::Unit::TestCase
|
4
|
+
context "Adapter ActiveRecord module: " do
|
5
|
+
class ::DummyClassForActiveRecord
|
6
|
+
RESPONSE = Struct.new('DummyActiveRecordResponse') do
|
7
|
+
def response
|
8
|
+
{ 'hits' => {'hits' => [ {'_id' => 2}, {'_id' => 1} ]} }
|
9
|
+
end
|
10
|
+
end.new
|
11
|
+
|
12
|
+
def response
|
13
|
+
RESPONSE
|
14
|
+
end
|
15
|
+
|
16
|
+
def ids
|
17
|
+
[2, 1]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [] } }
|
22
|
+
|
23
|
+
setup do
|
24
|
+
@records = [ stub(id: 1, inspect: '<Model-1>'), stub(id: 2, inspect: '<Model-2>') ]
|
25
|
+
@records.stubs(:load).returns(true)
|
26
|
+
@records.stubs(:exec_queries).returns(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "have the register condition" do
|
30
|
+
assert_not_nil Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::ActiveRecord]
|
31
|
+
assert_equal false, Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::ActiveRecord].call(DummyClassForActiveRecord)
|
32
|
+
end
|
33
|
+
|
34
|
+
context "Records" do
|
35
|
+
setup do
|
36
|
+
DummyClassForActiveRecord.__send__ :include, Elasticsearch::Model::Adapter::ActiveRecord::Records
|
37
|
+
end
|
38
|
+
|
39
|
+
should "have the implementation" do
|
40
|
+
assert_instance_of Module, Elasticsearch::Model::Adapter::ActiveRecord::Records
|
41
|
+
|
42
|
+
instance = DummyClassForActiveRecord.new
|
43
|
+
instance.expects(:klass).returns(mock('class', primary_key: :some_key, where: @records)).at_least_once
|
44
|
+
|
45
|
+
assert_equal @records, instance.records
|
46
|
+
end
|
47
|
+
|
48
|
+
should "load the records" do
|
49
|
+
instance = DummyClassForActiveRecord.new
|
50
|
+
instance.expects(:records).returns(@records)
|
51
|
+
instance.load
|
52
|
+
end
|
53
|
+
|
54
|
+
should "reorder the records based on hits order" do
|
55
|
+
@records.instance_variable_set(:@records, @records)
|
56
|
+
|
57
|
+
instance = DummyClassForActiveRecord.new
|
58
|
+
instance.expects(:klass).returns(mock('class', primary_key: :some_key, where: @records)).at_least_once
|
59
|
+
|
60
|
+
assert_equal [1, 2], @records. to_a.map(&:id)
|
61
|
+
assert_equal [2, 1], instance.records.to_a.map(&:id)
|
62
|
+
end
|
63
|
+
|
64
|
+
should "not reorder records when SQL order is present" do
|
65
|
+
@records.instance_variable_set(:@records, @records)
|
66
|
+
|
67
|
+
instance = DummyClassForActiveRecord.new
|
68
|
+
instance.expects(:klass).returns(stub('class', primary_key: :some_key, where: @records)).at_least_once
|
69
|
+
instance.records.expects(:order).returns(@records)
|
70
|
+
|
71
|
+
assert_equal [2, 1], instance.records. to_a.map(&:id)
|
72
|
+
assert_equal [1, 2], instance.order(:foo).to_a.map(&:id)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "Callbacks" do
|
77
|
+
should "register hooks for automatically updating the index" do
|
78
|
+
DummyClassForActiveRecord.expects(:after_commit).times(3)
|
79
|
+
|
80
|
+
Elasticsearch::Model::Adapter::ActiveRecord::Callbacks.included(DummyClassForActiveRecord)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "Importing" do
|
85
|
+
setup do
|
86
|
+
DummyClassForActiveRecord.__send__ :extend, Elasticsearch::Model::Adapter::ActiveRecord::Importing
|
87
|
+
end
|
88
|
+
|
89
|
+
should "raise an exception when passing an invalid scope" do
|
90
|
+
assert_raise NoMethodError do
|
91
|
+
DummyClassForActiveRecord.__find_in_batches(scope: :not_found_method) do; end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
should "implement the __find_in_batches method" do
|
96
|
+
DummyClassForActiveRecord.expects(:find_in_batches).returns([])
|
97
|
+
DummyClassForActiveRecord.__find_in_batches do; end
|
98
|
+
end
|
99
|
+
|
100
|
+
should "limit the relation to a specific scope" do
|
101
|
+
DummyClassForActiveRecord.expects(:find_in_batches).returns([])
|
102
|
+
DummyClassForActiveRecord.expects(:published).returns(DummyClassForActiveRecord)
|
103
|
+
|
104
|
+
DummyClassForActiveRecord.__find_in_batches(scope: :published) do; end
|
105
|
+
end
|
106
|
+
|
107
|
+
should "preprocess the batch if option provided" do
|
108
|
+
class << DummyClassForActiveRecord
|
109
|
+
# Updates/transforms the batch while fetching it from the database
|
110
|
+
# (eg. with information from an external system)
|
111
|
+
#
|
112
|
+
def update_batch(batch)
|
113
|
+
batch.collect { |b| b.to_s + '!' }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
DummyClassForActiveRecord.expects(:__find_in_batches).returns( [:a, :b] )
|
118
|
+
|
119
|
+
DummyClassForActiveRecord.__find_in_batches(preprocess: :update_batch) do |batch|
|
120
|
+
assert_same_elements ["a!", "b!"], batch
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when transforming models" do
|
125
|
+
setup do
|
126
|
+
@transform = DummyClassForActiveRecord.__transform
|
127
|
+
end
|
128
|
+
|
129
|
+
should "provide an object that responds to #call" do
|
130
|
+
assert_respond_to @transform, :call
|
131
|
+
end
|
132
|
+
|
133
|
+
should "provide default transformation" do
|
134
|
+
model = mock("model", id: 1, __elasticsearch__: stub(as_indexed_json: {}))
|
135
|
+
assert_equal @transform.call(model), { index: { _id: 1, data: {} } }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::AdapterDefaultTest < Test::Unit::TestCase
|
4
|
+
context "Adapter default module" do
|
5
|
+
class ::DummyClassForDefaultAdapter; end
|
6
|
+
|
7
|
+
should "have the default Records implementation" do
|
8
|
+
assert_instance_of Module, Elasticsearch::Model::Adapter::Default::Records
|
9
|
+
|
10
|
+
DummyClassForDefaultAdapter.__send__ :include, Elasticsearch::Model::Adapter::Default::Records
|
11
|
+
|
12
|
+
instance = DummyClassForDefaultAdapter.new
|
13
|
+
klass = mock('class', find: [1])
|
14
|
+
instance.expects(:klass).returns(klass)
|
15
|
+
instance.records
|
16
|
+
end
|
17
|
+
|
18
|
+
should "have the default Callbacks implementation" do
|
19
|
+
assert_instance_of Module, Elasticsearch::Model::Adapter::Default::Callbacks
|
20
|
+
end
|
21
|
+
|
22
|
+
context "concerning abstract methods" do
|
23
|
+
setup do
|
24
|
+
DummyClassForDefaultAdapter.__send__ :include, Elasticsearch::Model::Adapter::Default::Importing
|
25
|
+
end
|
26
|
+
|
27
|
+
should "have the default Importing implementation" do
|
28
|
+
assert_raise Elasticsearch::Model::NotImplemented do
|
29
|
+
DummyClassForDefaultAdapter.new.__find_in_batches
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should "have the default transform implementation" do
|
34
|
+
assert_raise Elasticsearch::Model::NotImplemented do
|
35
|
+
DummyClassForDefaultAdapter.new.__transform
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::AdapterMongoidTest < Test::Unit::TestCase
|
4
|
+
context "Adapter Mongoid module: " do
|
5
|
+
class ::DummyClassForMongoid
|
6
|
+
RESPONSE = Struct.new('DummyMongoidResponse') do
|
7
|
+
def response
|
8
|
+
{ 'hits' => {'hits' => [ {'_id' => 2}, {'_id' => 1} ]} }
|
9
|
+
end
|
10
|
+
end.new
|
11
|
+
|
12
|
+
def response
|
13
|
+
RESPONSE
|
14
|
+
end
|
15
|
+
|
16
|
+
def ids
|
17
|
+
[2, 1]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
setup do
|
22
|
+
@records = [ stub(id: 1, inspect: '<Model-1>'), stub(id: 2, inspect: '<Model-2>') ]
|
23
|
+
::Symbol.any_instance.stubs(:in).returns(@records)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "have the register condition" do
|
27
|
+
assert_not_nil Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::Mongoid]
|
28
|
+
assert_equal false, Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::Mongoid].call(DummyClassForMongoid)
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Records" do
|
32
|
+
setup do
|
33
|
+
DummyClassForMongoid.__send__ :include, Elasticsearch::Model::Adapter::Mongoid::Records
|
34
|
+
end
|
35
|
+
|
36
|
+
should "have the implementation" do
|
37
|
+
assert_instance_of Module, Elasticsearch::Model::Adapter::Mongoid::Records
|
38
|
+
|
39
|
+
instance = DummyClassForMongoid.new
|
40
|
+
instance.expects(:klass).returns(mock('class', where: @records))
|
41
|
+
|
42
|
+
assert_equal @records, instance.records
|
43
|
+
end
|
44
|
+
|
45
|
+
should "reorder the records based on hits order" do
|
46
|
+
@records.instance_variable_set(:@records, @records)
|
47
|
+
|
48
|
+
instance = DummyClassForMongoid.new
|
49
|
+
instance.expects(:klass).returns(mock('class', where: @records))
|
50
|
+
|
51
|
+
assert_equal [1, 2], @records. to_a.map(&:id)
|
52
|
+
assert_equal [2, 1], instance.records.to_a.map(&:id)
|
53
|
+
end
|
54
|
+
|
55
|
+
should "not reorder records when SQL order is present" do
|
56
|
+
@records.instance_variable_set(:@records, @records)
|
57
|
+
|
58
|
+
instance = DummyClassForMongoid.new
|
59
|
+
instance.expects(:klass).returns(stub('class', where: @records)).at_least_once
|
60
|
+
instance.records.expects(:asc).returns(@records)
|
61
|
+
|
62
|
+
assert_equal [2, 1], instance.records.to_a.map(&:id)
|
63
|
+
assert_equal [1, 2], instance.asc.to_a.map(&:id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "Callbacks" do
|
68
|
+
should "register hooks for automatically updating the index" do
|
69
|
+
DummyClassForMongoid.expects(:after_create)
|
70
|
+
DummyClassForMongoid.expects(:after_update)
|
71
|
+
DummyClassForMongoid.expects(:after_destroy)
|
72
|
+
|
73
|
+
Elasticsearch::Model::Adapter::Mongoid::Callbacks.included(DummyClassForMongoid)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "Importing" do
|
78
|
+
should "implement the __find_in_batches method" do
|
79
|
+
DummyClassForMongoid.expects(:all).returns([])
|
80
|
+
|
81
|
+
DummyClassForMongoid.__send__ :extend, Elasticsearch::Model::Adapter::Mongoid::Importing
|
82
|
+
DummyClassForMongoid.__find_in_batches do; end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when transforming models" do
|
86
|
+
setup do
|
87
|
+
@transform = DummyClassForMongoid.__transform
|
88
|
+
end
|
89
|
+
|
90
|
+
should "provide an object that responds to #call" do
|
91
|
+
assert_respond_to @transform, :call
|
92
|
+
end
|
93
|
+
|
94
|
+
should "provide basic transformation" do
|
95
|
+
model = mock("model", id: 1, as_indexed_json: {})
|
96
|
+
assert_equal @transform.call(model), { index: { _id: "1", data: {} } }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::AdapterTest < Test::Unit::TestCase
|
4
|
+
context "Adapter module" do
|
5
|
+
class ::DummyAdapterClass; end
|
6
|
+
class ::DummyAdapterClassWithAdapter; end
|
7
|
+
class ::DummyAdapter
|
8
|
+
Records = Module.new
|
9
|
+
Callbacks = Module.new
|
10
|
+
Importing = Module.new
|
11
|
+
end
|
12
|
+
|
13
|
+
should "return an Adapter instance" do
|
14
|
+
assert_instance_of Elasticsearch::Model::Adapter::Adapter,
|
15
|
+
Elasticsearch::Model::Adapter.from_class(DummyAdapterClass)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return a list of adapters" do
|
19
|
+
Elasticsearch::Model::Adapter::Adapter.expects(:adapters)
|
20
|
+
Elasticsearch::Model::Adapter.adapters
|
21
|
+
end
|
22
|
+
|
23
|
+
should "register an adapter" do
|
24
|
+
begin
|
25
|
+
Elasticsearch::Model::Adapter::Adapter.expects(:register)
|
26
|
+
Elasticsearch::Model::Adapter.register(:foo, lambda { |c| false })
|
27
|
+
ensure
|
28
|
+
Elasticsearch::Model::Adapter::Adapter.instance_variable_set(:@adapters, {})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "Adapter class" do
|
34
|
+
should "register an adapter" do
|
35
|
+
begin
|
36
|
+
Elasticsearch::Model::Adapter::Adapter.register(:foo, lambda { |c| false })
|
37
|
+
assert Elasticsearch::Model::Adapter::Adapter.adapters[:foo]
|
38
|
+
ensure
|
39
|
+
Elasticsearch::Model::Adapter::Adapter.instance_variable_set(:@adapters, {})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
should "return the default adapter" do
|
44
|
+
adapter = Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClass)
|
45
|
+
assert_equal Elasticsearch::Model::Adapter::Default, adapter.adapter
|
46
|
+
end
|
47
|
+
|
48
|
+
should "return a specific adapter" do
|
49
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
50
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
51
|
+
|
52
|
+
adapter = Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
53
|
+
assert_equal DummyAdapter, adapter.adapter
|
54
|
+
end
|
55
|
+
|
56
|
+
should "return the modules" do
|
57
|
+
assert_nothing_raised do
|
58
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
59
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
60
|
+
|
61
|
+
adapter = Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
62
|
+
|
63
|
+
assert_instance_of Module, adapter.records_mixin
|
64
|
+
assert_instance_of Module, adapter.callbacks_mixin
|
65
|
+
assert_instance_of Module, adapter.importing_mixin
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::CallbacksTest < Test::Unit::TestCase
|
4
|
+
context "Callbacks module" do
|
5
|
+
class ::DummyCallbacksModel
|
6
|
+
end
|
7
|
+
|
8
|
+
module DummyCallbacksAdapter
|
9
|
+
module CallbacksMixin
|
10
|
+
end
|
11
|
+
|
12
|
+
def callbacks_mixin
|
13
|
+
CallbacksMixin
|
14
|
+
end; module_function :callbacks_mixin
|
15
|
+
end
|
16
|
+
|
17
|
+
should "include the callbacks mixin from adapter" do
|
18
|
+
Elasticsearch::Model::Adapter.expects(:from_class)
|
19
|
+
.with(DummyCallbacksModel)
|
20
|
+
.returns(DummyCallbacksAdapter)
|
21
|
+
|
22
|
+
::DummyCallbacksModel.expects(:__send__).with do |method, parameter|
|
23
|
+
assert_equal :include, method
|
24
|
+
assert_equal DummyCallbacksAdapter::CallbacksMixin, parameter
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
Elasticsearch::Model::Callbacks.included(DummyCallbacksModel)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Model::ClientTest < Test::Unit::TestCase
|
4
|
+
context "Client module" do
|
5
|
+
class ::DummyClientModel
|
6
|
+
extend Elasticsearch::Model::Client::ClassMethods
|
7
|
+
include Elasticsearch::Model::Client::InstanceMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
should "have the default client method" do
|
11
|
+
assert_instance_of Elasticsearch::Transport::Client, DummyClientModel.client
|
12
|
+
assert_instance_of Elasticsearch::Transport::Client, DummyClientModel.new.client
|
13
|
+
end
|
14
|
+
|
15
|
+
should "set the client for the model" do
|
16
|
+
DummyClientModel.client = 'foobar'
|
17
|
+
assert_equal 'foobar', DummyClientModel.client
|
18
|
+
assert_equal 'foobar', DummyClientModel.new.client
|
19
|
+
end
|
20
|
+
|
21
|
+
should "set the client for a model instance" do
|
22
|
+
instance = DummyClientModel.new
|
23
|
+
instance.client = 'moobam'
|
24
|
+
assert_equal 'moobam', instance.client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|