elasticsearch-model 0.1.0 → 0.1.1
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.
- 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
|