elasticsearch-model 6.0.0 → 6.1.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.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +14 -7
- data/Rakefile +27 -36
- data/elasticsearch-model.gemspec +1 -1
- data/examples/activerecord_mapping_completion.rb +2 -15
- data/gemfiles/3.0.gemfile +6 -1
- data/gemfiles/4.0.gemfile +7 -1
- data/gemfiles/5.0.gemfile +6 -0
- data/lib/elasticsearch/model.rb +15 -8
- data/lib/elasticsearch/model/adapters/active_record.rb +7 -26
- data/lib/elasticsearch/model/indexing.rb +5 -3
- data/lib/elasticsearch/model/naming.rb +6 -1
- data/lib/elasticsearch/model/response.rb +2 -2
- data/lib/elasticsearch/model/response/pagination.rb +2 -192
- data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
- data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
- data/lib/elasticsearch/model/response/result.rb +1 -1
- data/lib/elasticsearch/model/version.rb +1 -1
- data/spec/elasticsearch/model/adapter_spec.rb +119 -0
- data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
- data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
- data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
- data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
- data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
- data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
- data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
- data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
- data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
- data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
- data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
- data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
- data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
- data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
- data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
- data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
- data/spec/elasticsearch/model/client_spec.rb +66 -0
- data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
- data/spec/elasticsearch/model/importing_spec.rb +214 -0
- data/spec/elasticsearch/model/indexing_spec.rb +918 -0
- data/spec/elasticsearch/model/module_spec.rb +101 -0
- data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
- data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
- data/spec/elasticsearch/model/naming_spec.rb +186 -0
- data/spec/elasticsearch/model/proxy_spec.rb +107 -0
- data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
- data/spec/elasticsearch/model/response/base_spec.rb +90 -0
- data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
- data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
- data/spec/elasticsearch/model/response/records_spec.rb +118 -0
- data/spec/elasticsearch/model/response/response_spec.rb +131 -0
- data/spec/elasticsearch/model/response/result_spec.rb +122 -0
- data/spec/elasticsearch/model/response/results_spec.rb +56 -0
- data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
- data/spec/elasticsearch/model/searching_spec.rb +49 -0
- data/spec/elasticsearch/model/serializing_spec.rb +22 -0
- data/spec/spec_helper.rb +161 -0
- data/spec/support/app.rb +21 -0
- data/spec/support/app/answer.rb +33 -0
- data/spec/support/app/article.rb +22 -0
- data/spec/support/app/article_for_pagination.rb +12 -0
- data/spec/support/app/article_with_custom_serialization.rb +13 -0
- data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
- data/spec/support/app/author.rb +9 -0
- data/spec/support/app/authorship.rb +4 -0
- data/spec/support/app/category.rb +3 -0
- data/spec/support/app/comment.rb +3 -0
- data/spec/support/app/episode.rb +11 -0
- data/spec/support/app/image.rb +19 -0
- data/spec/support/app/import_article.rb +12 -0
- data/spec/support/app/mongoid_article.rb +21 -0
- data/spec/support/app/namespaced_book.rb +10 -0
- data/spec/support/app/parent_and_child_searchable.rb +24 -0
- data/spec/support/app/post.rb +14 -0
- data/spec/support/app/question.rb +27 -0
- data/spec/support/app/searchable.rb +48 -0
- data/spec/support/app/series.rb +11 -0
- data/spec/support/model.json +1 -0
- data/{test → spec}/support/model.yml +0 -0
- metadata +129 -86
- data/test/integration/active_record_associations_parent_child_test.rb +0 -188
- data/test/integration/active_record_associations_test.rb +0 -339
- data/test/integration/active_record_basic_test.rb +0 -255
- data/test/integration/active_record_custom_serialization_test.rb +0 -67
- data/test/integration/active_record_import_test.rb +0 -168
- data/test/integration/active_record_namespaced_model_test.rb +0 -56
- data/test/integration/active_record_pagination_test.rb +0 -149
- data/test/integration/dynamic_index_name_test.rb +0 -52
- data/test/integration/mongoid_basic_test.rb +0 -240
- data/test/integration/multiple_models_test.rb +0 -176
- data/test/support/model.json +0 -1
- data/test/test_helper.rb +0 -92
- data/test/unit/adapter_active_record_test.rb +0 -157
- data/test/unit/adapter_default_test.rb +0 -41
- data/test/unit/adapter_mongoid_test.rb +0 -161
- data/test/unit/adapter_multiple_test.rb +0 -106
- data/test/unit/adapter_test.rb +0 -69
- data/test/unit/callbacks_test.rb +0 -31
- data/test/unit/client_test.rb +0 -27
- data/test/unit/hash_wrapper_test.rb +0 -13
- data/test/unit/importing_test.rb +0 -224
- data/test/unit/indexing_test.rb +0 -720
- data/test/unit/module_test.rb +0 -68
- data/test/unit/multimodel_test.rb +0 -38
- data/test/unit/naming_inheritance_test.rb +0 -94
- data/test/unit/naming_test.rb +0 -103
- data/test/unit/proxy_test.rb +0 -98
- data/test/unit/response_aggregations_test.rb +0 -46
- data/test/unit/response_base_test.rb +0 -40
- data/test/unit/response_pagination_kaminari_test.rb +0 -433
- data/test/unit/response_pagination_will_paginate_test.rb +0 -398
- data/test/unit/response_records_test.rb +0 -91
- data/test/unit/response_result_test.rb +0 -90
- data/test/unit/response_results_test.rb +0 -34
- data/test/unit/response_test.rb +0 -104
- data/test/unit/searching_search_request_test.rb +0 -78
- data/test/unit/searching_test.rb +0 -41
- data/test/unit/serializing_test.rb +0 -17
@@ -0,0 +1,109 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
3
|
+
module Response
|
4
|
+
|
5
|
+
# Pagination for search results/records
|
6
|
+
#
|
7
|
+
module Pagination
|
8
|
+
# Allow models to be paginated with the "kaminari" gem [https://github.com/amatsuda/kaminari]
|
9
|
+
#
|
10
|
+
module Kaminari
|
11
|
+
def self.included(base)
|
12
|
+
# Include the Kaminari configuration and paging method in response
|
13
|
+
#
|
14
|
+
base.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods
|
15
|
+
base.__send__ :include, ::Kaminari::PageScopeMethods
|
16
|
+
|
17
|
+
# Include the Kaminari paging methods in results and records
|
18
|
+
#
|
19
|
+
Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods
|
20
|
+
Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::PageScopeMethods
|
21
|
+
Elasticsearch::Model::Response::Records.__send__ :include, ::Kaminari::PageScopeMethods
|
22
|
+
|
23
|
+
Elasticsearch::Model::Response::Results.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response
|
24
|
+
Elasticsearch::Model::Response::Records.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response
|
25
|
+
|
26
|
+
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
27
|
+
# Define the `page` Kaminari method
|
28
|
+
#
|
29
|
+
def #{::Kaminari.config.page_method_name}(num=nil)
|
30
|
+
@results = nil
|
31
|
+
@records = nil
|
32
|
+
@response = nil
|
33
|
+
@page = [num.to_i, 1].max
|
34
|
+
@per_page ||= __default_per_page
|
35
|
+
|
36
|
+
self.search.definition.update size: @per_page,
|
37
|
+
from: @per_page * (@page - 1)
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
RUBY
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the current "limit" (`size`) value
|
45
|
+
#
|
46
|
+
def limit_value
|
47
|
+
case
|
48
|
+
when search.definition[:size]
|
49
|
+
search.definition[:size]
|
50
|
+
else
|
51
|
+
__default_per_page
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the current "offset" (`from`) value
|
56
|
+
#
|
57
|
+
def offset_value
|
58
|
+
case
|
59
|
+
when search.definition[:from]
|
60
|
+
search.definition[:from]
|
61
|
+
else
|
62
|
+
0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set the "limit" (`size`) value
|
67
|
+
#
|
68
|
+
def limit(value)
|
69
|
+
return self if value.to_i <= 0
|
70
|
+
@results = nil
|
71
|
+
@records = nil
|
72
|
+
@response = nil
|
73
|
+
@per_page = value.to_i
|
74
|
+
|
75
|
+
search.definition.update :size => @per_page
|
76
|
+
search.definition.update :from => @per_page * (@page - 1) if @page
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Set the "offset" (`from`) value
|
81
|
+
#
|
82
|
+
def offset(value)
|
83
|
+
return self if value.to_i < 0
|
84
|
+
@results = nil
|
85
|
+
@records = nil
|
86
|
+
@response = nil
|
87
|
+
@page = nil
|
88
|
+
search.definition.update :from => value.to_i
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the total number of results
|
93
|
+
#
|
94
|
+
def total_count
|
95
|
+
results.total
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the models's `per_page` value or the default
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
#
|
102
|
+
def __default_per_page
|
103
|
+
klass.respond_to?(:default_per_page) && klass.default_per_page || ::Kaminari.config.default_per_page
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
3
|
+
module Response
|
4
|
+
|
5
|
+
# Pagination for search results/records
|
6
|
+
#
|
7
|
+
module Pagination
|
8
|
+
|
9
|
+
|
10
|
+
# Allow models to be paginated with the "will_paginate" gem [https://github.com/mislav/will_paginate]
|
11
|
+
#
|
12
|
+
module WillPaginate
|
13
|
+
def self.included(base)
|
14
|
+
base.__send__ :include, ::WillPaginate::CollectionMethods
|
15
|
+
|
16
|
+
# Include the paging methods in results and records
|
17
|
+
#
|
18
|
+
methods = [:current_page, :offset, :length, :per_page, :total_entries, :total_pages, :previous_page, :next_page, :out_of_bounds?]
|
19
|
+
Elasticsearch::Model::Response::Results.__send__ :delegate, *methods, to: :response
|
20
|
+
Elasticsearch::Model::Response::Records.__send__ :delegate, *methods, to: :response
|
21
|
+
end
|
22
|
+
|
23
|
+
def offset
|
24
|
+
(current_page - 1) * per_page
|
25
|
+
end
|
26
|
+
|
27
|
+
def length
|
28
|
+
search.definition[:size]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Main pagination method
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
#
|
35
|
+
# Article.search('foo').paginate(page: 1, per_page: 30)
|
36
|
+
#
|
37
|
+
def paginate(options)
|
38
|
+
param_name = options[:param_name] || :page
|
39
|
+
page = [options[param_name].to_i, 1].max
|
40
|
+
per_page = (options[:per_page] || __default_per_page).to_i
|
41
|
+
|
42
|
+
search.definition.update size: per_page,
|
43
|
+
from: (page - 1) * per_page
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the current page
|
48
|
+
#
|
49
|
+
def current_page
|
50
|
+
search.definition[:from] / per_page + 1 if search.definition[:from] && per_page
|
51
|
+
end
|
52
|
+
|
53
|
+
# Pagination method
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
#
|
57
|
+
# Article.search('foo').page(2)
|
58
|
+
#
|
59
|
+
def page(num)
|
60
|
+
paginate(page: num, per_page: per_page) # shorthand
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return or set the "size" value
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
#
|
67
|
+
# Article.search('foo').per_page(15).page(2)
|
68
|
+
#
|
69
|
+
def per_page(num = nil)
|
70
|
+
if num.nil?
|
71
|
+
search.definition[:size]
|
72
|
+
else
|
73
|
+
paginate(page: current_page, per_page: num) # shorthand
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the total number of results
|
78
|
+
#
|
79
|
+
def total_entries
|
80
|
+
results.total
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the models's `per_page` value or the default
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
#
|
87
|
+
def __default_per_page
|
88
|
+
klass.respond_to?(:per_page) && klass.per_page || ::WillPaginate.per_page
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -46,7 +46,7 @@ module Elasticsearch
|
|
46
46
|
|
47
47
|
# Respond to methods from `@result` or `@result._source`
|
48
48
|
#
|
49
|
-
def
|
49
|
+
def respond_to_missing?(method_name, include_private = false)
|
50
50
|
@result.respond_to?(method_name.to_sym) || \
|
51
51
|
@result._source && @result._source.respond_to?(method_name.to_sym) || \
|
52
52
|
super
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Elasticsearch::Model::Adapter do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
class ::DummyAdapterClass; end
|
7
|
+
class ::DummyAdapterClassWithAdapter; end
|
8
|
+
class ::DummyAdapter
|
9
|
+
Records = Module.new
|
10
|
+
Callbacks = Module.new
|
11
|
+
Importing = Module.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
[DummyAdapterClassWithAdapter, DummyAdapterClass, DummyAdapter].each do |adapter|
|
17
|
+
Elasticsearch::Model::Adapter::Adapter.adapters.delete(adapter)
|
18
|
+
end
|
19
|
+
remove_classes(DummyAdapterClass, DummyAdapterClassWithAdapter, DummyAdapter)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#from_class' do
|
23
|
+
|
24
|
+
it 'should return an Adapter instance' do
|
25
|
+
expect(Elasticsearch::Model::Adapter.from_class(DummyAdapterClass)).to be_a(Elasticsearch::Model::Adapter::Adapter)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'register' do
|
30
|
+
|
31
|
+
before do
|
32
|
+
expect(Elasticsearch::Model::Adapter::Adapter).to receive(:register).and_call_original
|
33
|
+
Elasticsearch::Model::Adapter.register(:foo, lambda { |c| false })
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should register an adapter' do
|
37
|
+
expect(Elasticsearch::Model::Adapter::Adapter.adapters[:foo]).to be_a(Proc)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when a specific adapter class is set' do
|
41
|
+
|
42
|
+
before do
|
43
|
+
expect(Elasticsearch::Model::Adapter::Adapter).to receive(:register).and_call_original
|
44
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
45
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:adapter) do
|
49
|
+
Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should register the adapter' do
|
53
|
+
expect(adapter.adapter).to eq(DummyAdapter)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'default adapter' do
|
59
|
+
|
60
|
+
let(:adapter) do
|
61
|
+
Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClass)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'sets a default adapter' do
|
65
|
+
expect(adapter.adapter).to eq(Elasticsearch::Model::Adapter::Default)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#records_mixin' do
|
70
|
+
|
71
|
+
before do
|
72
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
73
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:adapter) do
|
78
|
+
Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns a Module' do
|
82
|
+
expect(adapter.records_mixin).to be_a(Module)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#callbacks_mixin' do
|
87
|
+
|
88
|
+
before do
|
89
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
90
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
let(:adapter) do
|
95
|
+
Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns a Module' do
|
99
|
+
expect(adapter.callbacks_mixin).to be_a(Module)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#importing_mixin' do
|
104
|
+
|
105
|
+
before do
|
106
|
+
Elasticsearch::Model::Adapter::Adapter.register(DummyAdapter,
|
107
|
+
lambda { |c| c == DummyAdapterClassWithAdapter })
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
let(:adapter) do
|
112
|
+
Elasticsearch::Model::Adapter::Adapter.new(DummyAdapterClassWithAdapter)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns a Module' do
|
116
|
+
expect(adapter.importing_mixin).to be_a(Module)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Elasticsearch::Model::Adapter::ActiveRecord Associations' do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Schema.define(version: 1) do
|
7
|
+
create_table :categories do |t|
|
8
|
+
t.string :title
|
9
|
+
t.timestamps null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :categories_posts do |t|
|
13
|
+
t.references :post, :category
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :authors do |t|
|
17
|
+
t.string :first_name, :last_name
|
18
|
+
t.timestamps null: false
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table :authorships do |t|
|
22
|
+
t.string :first_name, :last_name
|
23
|
+
t.references :post
|
24
|
+
t.references :author
|
25
|
+
t.timestamps null: false
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :comments do |t|
|
29
|
+
t.string :text
|
30
|
+
t.string :author
|
31
|
+
t.references :post
|
32
|
+
t.timestamps null: false
|
33
|
+
end
|
34
|
+
|
35
|
+
add_index(:comments, :post_id) unless index_exists?(:comments, :post_id)
|
36
|
+
|
37
|
+
create_table :posts do |t|
|
38
|
+
t.string :title
|
39
|
+
t.text :text
|
40
|
+
t.boolean :published
|
41
|
+
t.timestamps null: false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Comment.__send__ :include, Elasticsearch::Model
|
46
|
+
Comment.__send__ :include, Elasticsearch::Model::Callbacks
|
47
|
+
end
|
48
|
+
|
49
|
+
before do
|
50
|
+
clear_tables(:categories, :categories_posts, :authors, :authorships, :comments, :posts)
|
51
|
+
clear_indices(Post)
|
52
|
+
Post.__elasticsearch__.create_index!(force: true)
|
53
|
+
Comment.__elasticsearch__.create_index!(force: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
clear_tables(Post, Category)
|
58
|
+
clear_indices(Post)
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when a document is created' do
|
62
|
+
|
63
|
+
before do
|
64
|
+
Post.create!(title: 'Test')
|
65
|
+
Post.create!(title: 'Testing Coding')
|
66
|
+
Post.create!(title: 'Coding')
|
67
|
+
Post.__elasticsearch__.refresh_index!
|
68
|
+
end
|
69
|
+
|
70
|
+
let(:search_result) do
|
71
|
+
Post.search('title:test')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'indexes the document' do
|
75
|
+
expect(search_result.results.size).to eq(2)
|
76
|
+
expect(search_result.results.first.title).to eq('Test')
|
77
|
+
expect(search_result.records.size).to eq(2)
|
78
|
+
expect(search_result.records.first.title).to eq('Test')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'has_many_and_belongs_to association' do
|
83
|
+
|
84
|
+
context 'when an association is updated' do
|
85
|
+
|
86
|
+
before do
|
87
|
+
post.categories = [category_a, category_b]
|
88
|
+
Post.__elasticsearch__.refresh_index!
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:category_a) do
|
92
|
+
Category.where(title: "One").first_or_create!
|
93
|
+
end
|
94
|
+
|
95
|
+
let(:category_b) do
|
96
|
+
Category.where(title: "Two").first_or_create!
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:post) do
|
100
|
+
Post.create! title: "First Post", text: "This is the first post..."
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:search_result) do
|
104
|
+
Post.search(query: {
|
105
|
+
bool: {
|
106
|
+
must: {
|
107
|
+
multi_match: {
|
108
|
+
fields: ['title'],
|
109
|
+
query: 'first'
|
110
|
+
}
|
111
|
+
},
|
112
|
+
filter: {
|
113
|
+
terms: {
|
114
|
+
categories: ['One']
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
} )
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'applies the update with' do
|
122
|
+
expect(search_result.results.size).to eq(1)
|
123
|
+
expect(search_result.results.first.title).to eq('First Post')
|
124
|
+
expect(search_result.records.size).to eq(1)
|
125
|
+
expect(search_result.records.first.title).to eq('First Post')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when an association is deleted' do
|
130
|
+
|
131
|
+
before do
|
132
|
+
post.categories = [category_a, category_b]
|
133
|
+
post.categories = [category_b]
|
134
|
+
Post.__elasticsearch__.refresh_index!
|
135
|
+
end
|
136
|
+
|
137
|
+
let(:category_a) do
|
138
|
+
Category.where(title: "One").first_or_create!
|
139
|
+
end
|
140
|
+
|
141
|
+
let(:category_b) do
|
142
|
+
Category.where(title: "Two").first_or_create!
|
143
|
+
end
|
144
|
+
|
145
|
+
let(:post) do
|
146
|
+
Post.create! title: "First Post", text: "This is the first post..."
|
147
|
+
end
|
148
|
+
|
149
|
+
let(:search_result) do
|
150
|
+
Post.search(query: {
|
151
|
+
bool: {
|
152
|
+
must: {
|
153
|
+
multi_match: {
|
154
|
+
fields: ['title'],
|
155
|
+
query: 'first'
|
156
|
+
}
|
157
|
+
},
|
158
|
+
filter: {
|
159
|
+
terms: {
|
160
|
+
categories: ['One']
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
} )
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'applies the update with a reindex' do
|
168
|
+
expect(search_result.results.size).to eq(0)
|
169
|
+
expect(search_result.records.size).to eq(0)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'has_many through association' do
|
175
|
+
|
176
|
+
context 'when the association is updated' do
|
177
|
+
|
178
|
+
before do
|
179
|
+
author_a = Author.where(first_name: "John", last_name: "Smith").first_or_create!
|
180
|
+
author_b = Author.where(first_name: "Mary", last_name: "Smith").first_or_create!
|
181
|
+
author_c = Author.where(first_name: "Kobe", last_name: "Griss").first_or_create!
|
182
|
+
|
183
|
+
# Create posts
|
184
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
185
|
+
post_2 = Post.create!(title: "Second Post", text: "This is the second post...")
|
186
|
+
post_3 = Post.create!(title: "Third Post", text: "This is the third post...")
|
187
|
+
|
188
|
+
# Assign authors
|
189
|
+
post_1.authors = [author_a, author_b]
|
190
|
+
post_2.authors = [author_a]
|
191
|
+
post_3.authors = [author_c]
|
192
|
+
|
193
|
+
Post.__elasticsearch__.refresh_index!
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'if active record is at least 4' do
|
197
|
+
|
198
|
+
let(:search_result) do
|
199
|
+
Post.search('authors.full_name:john')
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'applies the update', if: active_record_at_least_4? do
|
203
|
+
expect(search_result.results.size).to eq(2)
|
204
|
+
expect(search_result.records.size).to eq(2)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'if active record is less than 4' do
|
209
|
+
|
210
|
+
let(:search_result) do
|
211
|
+
Post.search('authors.author.full_name:john')
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'applies the update', if: !active_record_at_least_4? do
|
215
|
+
expect(search_result.results.size).to eq(2)
|
216
|
+
expect(search_result.records.size).to eq(2)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when an association is added', if: active_record_at_least_4? do
|
222
|
+
|
223
|
+
before do
|
224
|
+
author_a = Author.where(first_name: "John", last_name: "Smith").first_or_create!
|
225
|
+
author_b = Author.where(first_name: "Mary", last_name: "Smith").first_or_create!
|
226
|
+
|
227
|
+
# Create posts
|
228
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
229
|
+
|
230
|
+
# Assign authors
|
231
|
+
post_1.authors = [author_a]
|
232
|
+
post_1.authors << author_b
|
233
|
+
Post.__elasticsearch__.refresh_index!
|
234
|
+
end
|
235
|
+
|
236
|
+
let(:search_result) do
|
237
|
+
Post.search('authors.full_name:john')
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'adds the association' do
|
241
|
+
expect(search_result.results.size).to eq(1)
|
242
|
+
expect(search_result.records.size).to eq(1)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe 'has_many association' do
|
248
|
+
|
249
|
+
context 'when an association is added', if: active_record_at_least_4? do
|
250
|
+
|
251
|
+
before do
|
252
|
+
# Create posts
|
253
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
254
|
+
post_2 = Post.create!(title: "Second Post", text: "This is the second post...")
|
255
|
+
|
256
|
+
# Add comments
|
257
|
+
post_1.comments.create!(author: 'John', text: 'Excellent')
|
258
|
+
post_1.comments.create!(author: 'Abby', text: 'Good')
|
259
|
+
|
260
|
+
post_2.comments.create!(author: 'John', text: 'Terrible')
|
261
|
+
|
262
|
+
post_1.comments.create!(author: 'John', text: 'Or rather just good...')
|
263
|
+
Post.__elasticsearch__.refresh_index!
|
264
|
+
end
|
265
|
+
|
266
|
+
let(:search_result) do
|
267
|
+
Post.search(query: {
|
268
|
+
nested: {
|
269
|
+
path: 'comments',
|
270
|
+
query: {
|
271
|
+
bool: {
|
272
|
+
must: [
|
273
|
+
{ match: { 'comments.author' => 'john' } },
|
274
|
+
{ match: { 'comments.text' => 'good' } }
|
275
|
+
]
|
276
|
+
}
|
277
|
+
}
|
278
|
+
}
|
279
|
+
})
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'adds the association' do
|
283
|
+
expect(search_result.results.size).to eq(1)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe '#touch' do
|
289
|
+
|
290
|
+
context 'when a touch callback is defined on the model' do
|
291
|
+
|
292
|
+
before do
|
293
|
+
# Create categories
|
294
|
+
category_a = Category.where(title: "One").first_or_create!
|
295
|
+
|
296
|
+
# Create post
|
297
|
+
post = Post.create!(title: "First Post", text: "This is the first post...")
|
298
|
+
|
299
|
+
# Assign category
|
300
|
+
post.categories << category_a
|
301
|
+
category_a.update_attribute(:title, "Updated")
|
302
|
+
category_a.posts.each { |p| p.touch }
|
303
|
+
|
304
|
+
Post.__elasticsearch__.refresh_index!
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'executes the callback after #touch' do
|
308
|
+
expect(Post.search('categories:One').size).to eq(0)
|
309
|
+
expect(Post.search('categories:Updated').size).to eq(1)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe '#includes' do
|
315
|
+
|
316
|
+
before do
|
317
|
+
post_1 = Post.create(title: 'One')
|
318
|
+
post_2 = Post.create(title: 'Two')
|
319
|
+
post_1.comments.create(text: 'First comment')
|
320
|
+
post_2.comments.create(text: 'Second comment')
|
321
|
+
|
322
|
+
Comment.__elasticsearch__.refresh_index!
|
323
|
+
end
|
324
|
+
|
325
|
+
let(:search_result) do
|
326
|
+
Comment.search('first').records(includes: :post)
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'eager loads associations' do
|
330
|
+
expect(search_result.first.association(:post)).to be_loaded
|
331
|
+
expect(search_result.first.post.title).to eq('One')
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|