elasticsearch-model 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/README.md +3 -5
- data/elasticsearch-model.gemspec +1 -0
- data/examples/activerecord_associations.rb +2 -0
- data/lib/elasticsearch/model/indexing.rb +7 -1
- data/lib/elasticsearch/model/response/pagination.rb +12 -8
- data/lib/elasticsearch/model/version.rb +1 -1
- data/test/integration/active_record_custom_serialization_test.rb +61 -0
- data/test/integration/active_record_import_test.rb +1 -0
- data/test/integration/active_record_pagination_test.rb +39 -16
- data/test/integration/mongoid_basic_test.rb +2 -0
- data/test/unit/indexing_test.rb +38 -0
- data/test/unit/response_pagination_test.rb +34 -6
- metadata +21 -2
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -76,10 +76,8 @@ module Searchable
|
|
76
76
|
mapping do
|
77
77
|
# ...
|
78
78
|
end
|
79
|
-
end
|
80
79
|
|
81
|
-
|
82
|
-
def search(query)
|
80
|
+
def self.search(query)
|
83
81
|
# ...
|
84
82
|
end
|
85
83
|
end
|
@@ -99,8 +97,8 @@ all its functionality. To prevent polluting your model namespace, this functiona
|
|
99
97
|
available via the `__elasticsearch__` class and instance level proxy methods;
|
100
98
|
see the `Elasticsearch::Model::Proxy` class documentation for technical information.
|
101
99
|
|
102
|
-
The module will include important methods, such as `search`, into the
|
103
|
-
|
100
|
+
The module will include important methods, such as `search`, into the class or module only
|
101
|
+
when they haven't been defined already. Following two calls are thus functionally equivalent:
|
104
102
|
|
105
103
|
```ruby
|
106
104
|
Article.__elasticsearch__.search 'fox'
|
data/elasticsearch-model.gemspec
CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
s.add_development_dependency "kaminari"
|
39
39
|
# NOTE: Do not add Mongoid here, keep only in 3/4 files
|
40
40
|
|
41
|
+
s.add_development_dependency "minitest", "~> 4.0"
|
41
42
|
s.add_development_dependency "shoulda-context"
|
42
43
|
s.add_development_dependency "mocha"
|
43
44
|
s.add_development_dependency "turn"
|
@@ -333,11 +333,17 @@ module Elasticsearch
|
|
333
333
|
#
|
334
334
|
def update_document(options={})
|
335
335
|
if changed_attributes = self.instance_variable_get(:@__changed_attributes)
|
336
|
+
attributes = if respond_to?(:as_indexed_json)
|
337
|
+
changed_attributes.select { |k,v| self.as_indexed_json.keys.include? k }
|
338
|
+
else
|
339
|
+
changed_attributes
|
340
|
+
end
|
341
|
+
|
336
342
|
client.update(
|
337
343
|
{ index: index_name,
|
338
344
|
type: document_type,
|
339
345
|
id: self.id,
|
340
|
-
body: { doc:
|
346
|
+
body: { doc: attributes } }.merge(options)
|
341
347
|
)
|
342
348
|
else
|
343
349
|
index_document(options)
|
@@ -30,8 +30,12 @@ module Elasticsearch
|
|
30
30
|
@results = nil
|
31
31
|
@records = nil
|
32
32
|
@response = nil
|
33
|
-
|
34
|
-
|
33
|
+
@page = [num.to_i, 1].max
|
34
|
+
@per_page ||= klass.default_per_page
|
35
|
+
|
36
|
+
self.search.definition.update size: @per_page,
|
37
|
+
from: @per_page * (@page - 1)
|
38
|
+
|
35
39
|
self
|
36
40
|
end
|
37
41
|
RUBY
|
@@ -41,12 +45,10 @@ module Elasticsearch
|
|
41
45
|
#
|
42
46
|
def limit_value
|
43
47
|
case
|
44
|
-
when search.definition[:body] && search.definition[:body][:size]
|
45
|
-
search.definition[:body][:size]
|
46
48
|
when search.definition[:size]
|
47
49
|
search.definition[:size]
|
48
50
|
else
|
49
|
-
|
51
|
+
search.klass.default_per_page
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
@@ -54,8 +56,6 @@ module Elasticsearch
|
|
54
56
|
#
|
55
57
|
def offset_value
|
56
58
|
case
|
57
|
-
when search.definition[:body] && search.definition[:body][:from]
|
58
|
-
search.definition[:body][:from]
|
59
59
|
when search.definition[:from]
|
60
60
|
search.definition[:from]
|
61
61
|
else
|
@@ -69,7 +69,10 @@ module Elasticsearch
|
|
69
69
|
@results = nil
|
70
70
|
@records = nil
|
71
71
|
@response = nil
|
72
|
-
|
72
|
+
@per_page = value
|
73
|
+
|
74
|
+
search.definition.update :size => @per_page
|
75
|
+
search.definition.update :from => @per_page * (@page - 1) if @page
|
73
76
|
self
|
74
77
|
end
|
75
78
|
|
@@ -79,6 +82,7 @@ module Elasticsearch
|
|
79
82
|
@results = nil
|
80
83
|
@records = nil
|
81
84
|
@response = nil
|
85
|
+
@page = nil
|
82
86
|
search.definition.update :from => value
|
83
87
|
self
|
84
88
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module Model
|
5
|
+
class ActiveRecordCustomSerializationTest < Elasticsearch::Test::IntegrationTestCase
|
6
|
+
|
7
|
+
class ::ArticleWithCustomSerialization < ActiveRecord::Base
|
8
|
+
include Elasticsearch::Model
|
9
|
+
include Elasticsearch::Model::Callbacks
|
10
|
+
|
11
|
+
mapping do
|
12
|
+
indexes :title
|
13
|
+
end
|
14
|
+
|
15
|
+
def as_indexed_json(options={})
|
16
|
+
as_json(options.merge root: false).slice('title')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "ActiveRecord model with custom JSON serialization" do
|
21
|
+
setup do
|
22
|
+
ActiveRecord::Schema.define(:version => 1) do
|
23
|
+
create_table ArticleWithCustomSerialization.table_name do |t|
|
24
|
+
t.string :title
|
25
|
+
t.string :status
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ArticleWithCustomSerialization.delete_all
|
30
|
+
ArticleWithCustomSerialization.__elasticsearch__.create_index! force: true
|
31
|
+
end
|
32
|
+
|
33
|
+
should "index only the title attribute when creating" do
|
34
|
+
ArticleWithCustomSerialization.create! title: 'Test', status: 'green'
|
35
|
+
|
36
|
+
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
|
37
|
+
index: 'article_with_custom_serializations',
|
38
|
+
type: 'article_with_custom_serialization',
|
39
|
+
id: '1'
|
40
|
+
|
41
|
+
assert_equal( { 'title' => 'Test' }, a['_source'] )
|
42
|
+
end
|
43
|
+
|
44
|
+
should "index only the title attribute when updating" do
|
45
|
+
ArticleWithCustomSerialization.create! title: 'Test', status: 'green'
|
46
|
+
|
47
|
+
article = ArticleWithCustomSerialization.first
|
48
|
+
article.update_attributes title: 'UPDATED', status: 'red'
|
49
|
+
|
50
|
+
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
|
51
|
+
index: 'article_with_custom_serializations',
|
52
|
+
type: 'article_with_custom_serialization',
|
53
|
+
id: '1'
|
54
|
+
|
55
|
+
assert_equal( { 'title' => 'UPDATED' }, a['_source'] )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -26,6 +26,7 @@ module Elasticsearch
|
|
26
26
|
|
27
27
|
ImportArticle.delete_all
|
28
28
|
ImportArticle.__elasticsearch__.create_index! force: true
|
29
|
+
ImportArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
29
30
|
|
30
31
|
100.times { |i| ImportArticle.create! title: "Test #{i}" }
|
31
32
|
end
|
@@ -4,9 +4,11 @@ module Elasticsearch
|
|
4
4
|
module Model
|
5
5
|
class ActiveRecordPaginationTest < Elasticsearch::Test::IntegrationTestCase
|
6
6
|
|
7
|
-
class ::
|
7
|
+
class ::ArticleForPagination < ActiveRecord::Base
|
8
8
|
include Elasticsearch::Model
|
9
9
|
|
10
|
+
scope :published, -> { where(published: true) }
|
11
|
+
|
10
12
|
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
|
11
13
|
mapping do
|
12
14
|
indexes :title, type: 'string', analyzer: 'snowball'
|
@@ -20,23 +22,26 @@ module Elasticsearch
|
|
20
22
|
context "ActiveRecord pagination" do
|
21
23
|
setup do
|
22
24
|
ActiveRecord::Schema.define(:version => 1) do
|
23
|
-
create_table
|
25
|
+
create_table ::ArticleForPagination.table_name do |t|
|
24
26
|
t.string :title
|
25
27
|
t.datetime :created_at, :default => 'NOW()'
|
28
|
+
t.boolean :published
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
29
|
-
|
30
|
-
|
32
|
+
ArticleForPagination.delete_all
|
33
|
+
ArticleForPagination.__elasticsearch__.create_index! force: true
|
31
34
|
|
32
|
-
68.times do |i|
|
35
|
+
68.times do |i|
|
36
|
+
::ArticleForPagination.create! title: "Test #{i}", published: (i % 2 == 0)
|
37
|
+
end
|
33
38
|
|
34
|
-
|
35
|
-
|
39
|
+
ArticleForPagination.import
|
40
|
+
ArticleForPagination.__elasticsearch__.refresh_index!
|
36
41
|
end
|
37
42
|
|
38
43
|
should "be on the first page by default" do
|
39
|
-
records =
|
44
|
+
records = ArticleForPagination.search('title:test').page(1).records
|
40
45
|
|
41
46
|
assert_equal 25, records.size
|
42
47
|
assert_equal 1, records.current_page
|
@@ -50,7 +55,7 @@ module Elasticsearch
|
|
50
55
|
end
|
51
56
|
|
52
57
|
should "load next page" do
|
53
|
-
records =
|
58
|
+
records = ArticleForPagination.search('title:test').page(2).records
|
54
59
|
|
55
60
|
assert_equal 25, records.size
|
56
61
|
assert_equal 2, records.current_page
|
@@ -64,7 +69,7 @@ module Elasticsearch
|
|
64
69
|
end
|
65
70
|
|
66
71
|
should "load last page" do
|
67
|
-
records =
|
72
|
+
records = ArticleForPagination.search('title:test').page(3).records
|
68
73
|
|
69
74
|
assert_equal 18, records.size
|
70
75
|
assert_equal 3, records.current_page
|
@@ -78,7 +83,7 @@ module Elasticsearch
|
|
78
83
|
end
|
79
84
|
|
80
85
|
should "not load invalid page" do
|
81
|
-
records =
|
86
|
+
records = ArticleForPagination.search('title:test').page(6).records
|
82
87
|
|
83
88
|
assert_equal 0, records.size
|
84
89
|
assert_equal 6, records.current_page
|
@@ -91,16 +96,34 @@ module Elasticsearch
|
|
91
96
|
assert records.out_of_range?, "Should be out of range"
|
92
97
|
end
|
93
98
|
|
99
|
+
should "be combined with scopes" do
|
100
|
+
records = ArticleForPagination.search('title:test').page(2).records.published
|
101
|
+
assert records.all? { |r| r.published? }
|
102
|
+
assert_equal 12, records.size
|
103
|
+
end
|
104
|
+
|
105
|
+
should "set the limit per request" do
|
106
|
+
records = ArticleForPagination.search('title:test').limit(50).page(2).records
|
107
|
+
|
108
|
+
assert_equal 18, records.size
|
109
|
+
assert_equal 2, records.current_page
|
110
|
+
assert_equal 1, records.prev_page
|
111
|
+
assert_equal nil, records.next_page
|
112
|
+
assert_equal 2, records.total_pages
|
113
|
+
|
114
|
+
assert records.last_page?, "Should be the last page"
|
115
|
+
end
|
116
|
+
|
94
117
|
context "with specific model settings" do
|
95
118
|
teardown do
|
96
|
-
|
119
|
+
ArticleForPagination.instance_variable_set(:@_default_per_page, nil)
|
97
120
|
end
|
98
|
-
end
|
99
121
|
|
100
|
-
|
101
|
-
|
122
|
+
should "respect paginates_per" do
|
123
|
+
ArticleForPagination.paginates_per 50
|
102
124
|
|
103
|
-
|
125
|
+
assert_equal 50, ArticleForPagination.search('*').page(1).records.size
|
126
|
+
end
|
104
127
|
end
|
105
128
|
end
|
106
129
|
|
@@ -61,6 +61,7 @@ if ENV["MONGODB_AVAILABLE"]
|
|
61
61
|
MongoidArticle.create! title: 'Coding'
|
62
62
|
|
63
63
|
MongoidArticle.__elasticsearch__.refresh_index!
|
64
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
64
65
|
end
|
65
66
|
|
66
67
|
should "index and find a document" do
|
@@ -147,6 +148,7 @@ if ENV["MONGODB_AVAILABLE"]
|
|
147
148
|
MongoidArticle.delete_all
|
148
149
|
97.times { |i| MongoidArticle.create! title: "Test #{i}" }
|
149
150
|
MongoidArticle.__elasticsearch__.create_index! force: true
|
151
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
150
152
|
end
|
151
153
|
|
152
154
|
should "import all the documents" do
|
data/test/unit/indexing_test.rb
CHANGED
@@ -129,6 +129,25 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
class ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson
|
133
|
+
extend Elasticsearch::Model::Indexing::ClassMethods
|
134
|
+
include Elasticsearch::Model::Indexing::InstanceMethods
|
135
|
+
|
136
|
+
def self.before_save(&block)
|
137
|
+
(@callbacks ||= {})[block.hash] = block
|
138
|
+
end
|
139
|
+
|
140
|
+
def changed_attributes; [:foo, :bar]; end
|
141
|
+
|
142
|
+
def changes
|
143
|
+
{:foo => ['A', 'B'], :bar => ['C', 'D']}
|
144
|
+
end
|
145
|
+
|
146
|
+
def as_indexed_json(options={})
|
147
|
+
{ :foo => 'B' }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
132
151
|
should "register before_save callback when included" do
|
133
152
|
::DummyIndexingModelWithCallbacks.expects(:before_save).returns(true)
|
134
153
|
::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
|
@@ -251,6 +270,25 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
251
270
|
|
252
271
|
instance.update_document
|
253
272
|
end
|
273
|
+
|
274
|
+
should "exclude attributes not contained in custom as_indexed_json during partial update" do
|
275
|
+
client = mock('client')
|
276
|
+
instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
|
277
|
+
|
278
|
+
# Set the fake `changes` hash
|
279
|
+
instance.instance_variable_set(:@__changed_attributes, {foo: 'B', bar: 'D' })
|
280
|
+
|
281
|
+
client.expects(:update).with do |payload|
|
282
|
+
assert_equal({foo: 'B'}, payload[:body][:doc])
|
283
|
+
end
|
284
|
+
|
285
|
+
instance.expects(:client).returns(client)
|
286
|
+
instance.expects(:index_name).returns('foo')
|
287
|
+
instance.expects(:document_type).returns('bar')
|
288
|
+
instance.expects(:id).returns('1')
|
289
|
+
|
290
|
+
instance.update_document
|
291
|
+
end
|
254
292
|
end
|
255
293
|
|
256
294
|
context "Re-creating the index" do
|
@@ -13,7 +13,7 @@ class Elasticsearch::Model::ResponsePaginationTest < Test::Unit::TestCase
|
|
13
13
|
'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
|
14
14
|
|
15
15
|
setup do
|
16
|
-
@search
|
16
|
+
@search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*'
|
17
17
|
@response = Elasticsearch::Model::Response::Response.new ModelClass, @search, RESPONSE
|
18
18
|
@response.klass.stubs(:client).returns mock('client')
|
19
19
|
end
|
@@ -62,7 +62,7 @@ class Elasticsearch::Model::ResponsePaginationTest < Test::Unit::TestCase
|
|
62
62
|
|
63
63
|
context "limit/offset readers" do
|
64
64
|
should "return the default" do
|
65
|
-
assert_equal
|
65
|
+
assert_equal Kaminari.config.default_per_page, @response.limit_value
|
66
66
|
assert_equal 0, @response.offset_value
|
67
67
|
end
|
68
68
|
|
@@ -74,12 +74,13 @@ class Elasticsearch::Model::ResponsePaginationTest < Test::Unit::TestCase
|
|
74
74
|
assert_equal 50, @response.offset_value
|
75
75
|
end
|
76
76
|
|
77
|
-
should "
|
78
|
-
search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass,
|
77
|
+
should "ignore the value from request body" do
|
78
|
+
search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass,
|
79
|
+
{ query: { match_all: {} }, from: 333, size: 999 }
|
79
80
|
@response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE
|
80
81
|
|
81
|
-
assert_equal
|
82
|
-
assert_equal
|
82
|
+
assert_equal Kaminari.config.default_per_page, @response.limit_value
|
83
|
+
assert_equal 0, @response.offset_value
|
83
84
|
end
|
84
85
|
end
|
85
86
|
|
@@ -103,6 +104,33 @@ class Elasticsearch::Model::ResponsePaginationTest < Test::Unit::TestCase
|
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
107
|
+
context "with the page() and limit() methods" do
|
108
|
+
setup do
|
109
|
+
@response.records
|
110
|
+
@response.results
|
111
|
+
end
|
112
|
+
|
113
|
+
should "set the values" do
|
114
|
+
@response.page(3).limit(35)
|
115
|
+
assert_equal 35, @response.search.definition[:size]
|
116
|
+
assert_equal 70, @response.search.definition[:from]
|
117
|
+
end
|
118
|
+
|
119
|
+
should "set the values when limit is called first" do
|
120
|
+
@response.limit(35).page(3)
|
121
|
+
assert_equal 35, @response.search.definition[:size]
|
122
|
+
assert_equal 70, @response.search.definition[:from]
|
123
|
+
end
|
124
|
+
|
125
|
+
should "reset the instance variables" do
|
126
|
+
@response.page(3).limit(35)
|
127
|
+
|
128
|
+
assert_nil @response.instance_variable_get(:@response)
|
129
|
+
assert_nil @response.instance_variable_get(:@records)
|
130
|
+
assert_nil @response.instance_variable_get(:@results)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
106
134
|
context "offset setter" do
|
107
135
|
setup do
|
108
136
|
@response.records
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-04-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: elasticsearch
|
@@ -187,6 +187,22 @@ dependencies:
|
|
187
187
|
- - ! '>='
|
188
188
|
- !ruby/object:Gem::Version
|
189
189
|
version: '0'
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: minitest
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ~>
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '4.0'
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ~>
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '4.0'
|
190
206
|
- !ruby/object:Gem::Dependency
|
191
207
|
name: shoulda-context
|
192
208
|
requirement: !ruby/object:Gem::Requirement
|
@@ -373,6 +389,7 @@ extra_rdoc_files:
|
|
373
389
|
- LICENSE.txt
|
374
390
|
files:
|
375
391
|
- .gitignore
|
392
|
+
- CHANGELOG.md
|
376
393
|
- Gemfile
|
377
394
|
- LICENSE.txt
|
378
395
|
- README.md
|
@@ -411,6 +428,7 @@ files:
|
|
411
428
|
- test/integration/active_record_associations_parent_child.rb
|
412
429
|
- test/integration/active_record_associations_test.rb
|
413
430
|
- test/integration/active_record_basic_test.rb
|
431
|
+
- test/integration/active_record_custom_serialization_test.rb
|
414
432
|
- test/integration/active_record_import_test.rb
|
415
433
|
- test/integration/active_record_namespaced_model_test.rb
|
416
434
|
- test/integration/active_record_pagination_test.rb
|
@@ -466,6 +484,7 @@ test_files:
|
|
466
484
|
- test/integration/active_record_associations_parent_child.rb
|
467
485
|
- test/integration/active_record_associations_test.rb
|
468
486
|
- test/integration/active_record_basic_test.rb
|
487
|
+
- test/integration/active_record_custom_serialization_test.rb
|
469
488
|
- test/integration/active_record_import_test.rb
|
470
489
|
- test/integration/active_record_namespaced_model_test.rb
|
471
490
|
- test/integration/active_record_pagination_test.rb
|