elasticsearch-model 5.1.0 → 6.0.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/README.md +4 -4
- data/elasticsearch-model.gemspec +4 -4
- data/examples/activerecord_custom_analyzer.rb +135 -0
- data/examples/activerecord_mapping_completion.rb +1 -3
- data/examples/datamapper_article.rb +11 -1
- data/gemfiles/3.0.gemfile +1 -1
- data/gemfiles/4.0.gemfile +1 -1
- data/gemfiles/5.0.gemfile +1 -1
- data/lib/elasticsearch/model/adapters/active_record.rb +4 -1
- data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
- data/lib/elasticsearch/model/importing.rb +1 -1
- data/lib/elasticsearch/model/indexing.rb +1 -1
- data/lib/elasticsearch/model/naming.rb +3 -1
- data/lib/elasticsearch/model/response/aggregations.rb +1 -1
- data/lib/elasticsearch/model/response/base.rb +3 -2
- data/lib/elasticsearch/model/response/suggestions.rb +1 -1
- data/lib/elasticsearch/model/response.rb +11 -10
- data/lib/elasticsearch/model/version.rb +1 -1
- data/test/integration/active_record_associations_parent_child_test.rb +75 -34
- data/test/integration/active_record_basic_test.rb +4 -0
- data/test/integration/active_record_custom_serialization_test.rb +2 -2
- data/test/integration/active_record_import_test.rb +65 -12
- data/test/integration/active_record_namespaced_model_test.rb +2 -0
- data/test/integration/mongoid_basic_test.rb +64 -0
- data/test/test_helper.rb +1 -1
- data/test/unit/adapter_mongoid_test.rb +58 -1
- data/test/unit/importing_test.rb +21 -0
- data/test/unit/indexing_test.rb +35 -2
- data/test/unit/naming_inheritance_test.rb +2 -2
- data/test/unit/naming_test.rb +2 -2
- data/test/unit/response_results_test.rb +3 -0
- metadata +8 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 726d16096fd6ca31266bab9b42c4f878cba6942eada1649de7fbf946af2575f0
|
|
4
|
+
data.tar.gz: 8ce1faffcdda9dae38ff2beddab31f0b161853c4fd9919b699cdb5e5a27f2baa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63904bd61ec508918ee8a0d6cbe7cd089dc0b6df7c6f4d8ea1fef7538b2aea3a88962724e6a5fc486f5ec10146350e4176655b3fbeec868a66b6b3e00e767307
|
|
7
|
+
data.tar.gz: 36fccab94cf1f3cdfa599ad0a6d74cecc1b503c80da296e73381f3b3f2130244d57a9b51fabaff5e624e7f0955442e3b0e67d7006b6b94f705fdc4249252ac12
|
data/README.md
CHANGED
|
@@ -321,7 +321,7 @@ Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model
|
|
|
321
321
|
|
|
322
322
|
#### The Elasticsearch DSL
|
|
323
323
|
|
|
324
|
-
In most
|
|
324
|
+
In most situations, you'll want to pass the search definition
|
|
325
325
|
in the Elasticsearch [domain-specific language](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html) to the client:
|
|
326
326
|
|
|
327
327
|
```ruby
|
|
@@ -425,7 +425,7 @@ Article.__elasticsearch__.refresh_index!
|
|
|
425
425
|
```
|
|
426
426
|
|
|
427
427
|
By default, index name and document type will be inferred from your class name,
|
|
428
|
-
you can set it
|
|
428
|
+
you can set it explicitly, however:
|
|
429
429
|
|
|
430
430
|
```ruby
|
|
431
431
|
class Article
|
|
@@ -534,7 +534,7 @@ class Indexer
|
|
|
534
534
|
case operation.to_s
|
|
535
535
|
when /index/
|
|
536
536
|
record = Article.find(record_id)
|
|
537
|
-
Client.index index: 'articles', type: 'article', id: record.id, body: record.as_indexed_json
|
|
537
|
+
Client.index index: 'articles', type: 'article', id: record.id, body: record.__elasticsearch__.as_indexed_json
|
|
538
538
|
when /delete/
|
|
539
539
|
Client.delete index: 'articles', type: 'article', id: record_id
|
|
540
540
|
else raise ArgumentError, "Unknown operation '#{operation}'"
|
|
@@ -674,7 +674,7 @@ module DataMapperAdapter
|
|
|
674
674
|
#
|
|
675
675
|
module Records
|
|
676
676
|
def records
|
|
677
|
-
klass.all(id:
|
|
677
|
+
klass.all(id: ids)
|
|
678
678
|
end
|
|
679
679
|
|
|
680
680
|
# ...
|
data/elasticsearch-model.gemspec
CHANGED
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
|
|
24
24
|
s.required_ruby_version = ">= 1.9.3"
|
|
25
25
|
|
|
26
|
-
s.add_dependency "elasticsearch", '
|
|
26
|
+
s.add_dependency "elasticsearch", '> 1'
|
|
27
27
|
s.add_dependency "activesupport", '> 3'
|
|
28
28
|
s.add_dependency "hashie"
|
|
29
29
|
|
|
@@ -32,10 +32,10 @@ Gem::Specification.new do |s|
|
|
|
32
32
|
|
|
33
33
|
s.add_development_dependency "elasticsearch-extensions"
|
|
34
34
|
|
|
35
|
-
s.add_development_dependency "sqlite3"
|
|
35
|
+
s.add_development_dependency "sqlite3" unless defined?(JRUBY_VERSION)
|
|
36
36
|
s.add_development_dependency "activemodel", "> 3"
|
|
37
37
|
|
|
38
|
-
s.add_development_dependency "oj"
|
|
38
|
+
s.add_development_dependency "oj" unless defined?(JRUBY_VERSION)
|
|
39
39
|
s.add_development_dependency "kaminari"
|
|
40
40
|
s.add_development_dependency "will_paginate"
|
|
41
41
|
|
|
@@ -45,7 +45,7 @@ Gem::Specification.new do |s|
|
|
|
45
45
|
s.add_development_dependency "mocha"
|
|
46
46
|
s.add_development_dependency "turn"
|
|
47
47
|
s.add_development_dependency "yard"
|
|
48
|
-
s.add_development_dependency "ruby-prof"
|
|
48
|
+
s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION)
|
|
49
49
|
s.add_development_dependency "pry"
|
|
50
50
|
|
|
51
51
|
s.add_development_dependency "simplecov"
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Custom Analyzer for ActiveRecord integration with Elasticsearch
|
|
2
|
+
# ===============================================================
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
5
|
+
|
|
6
|
+
require 'ansi'
|
|
7
|
+
require 'logger'
|
|
8
|
+
|
|
9
|
+
require 'active_record'
|
|
10
|
+
require 'elasticsearch/model'
|
|
11
|
+
|
|
12
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
|
13
|
+
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
|
|
14
|
+
|
|
15
|
+
ActiveRecord::Schema.define(version: 1) do
|
|
16
|
+
create_table :articles do |t|
|
|
17
|
+
t.string :title
|
|
18
|
+
t.date :published_at
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Elasticsearch::Model.client.transport.logger = ActiveSupport::Logger.new(STDOUT)
|
|
24
|
+
Elasticsearch::Model.client.transport.logger.formatter = lambda { |s, d, p, m| "#{m.ansi(:faint)}\n" }
|
|
25
|
+
|
|
26
|
+
class Article < ActiveRecord::Base
|
|
27
|
+
include Elasticsearch::Model
|
|
28
|
+
|
|
29
|
+
settings index: {
|
|
30
|
+
number_of_shards: 1,
|
|
31
|
+
number_of_replicas: 0,
|
|
32
|
+
analysis: {
|
|
33
|
+
analyzer: {
|
|
34
|
+
pattern: {
|
|
35
|
+
type: 'pattern',
|
|
36
|
+
pattern: "\\s|_|-|\\.",
|
|
37
|
+
lowercase: true
|
|
38
|
+
},
|
|
39
|
+
trigram: {
|
|
40
|
+
tokenizer: 'trigram'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
tokenizer: {
|
|
44
|
+
trigram: {
|
|
45
|
+
type: 'ngram',
|
|
46
|
+
min_gram: 3,
|
|
47
|
+
max_gram: 3,
|
|
48
|
+
token_chars: ['letter', 'digit']
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} } do
|
|
52
|
+
mapping do
|
|
53
|
+
indexes :title, type: 'text', analyzer: 'english' do
|
|
54
|
+
indexes :keyword, analyzer: 'keyword'
|
|
55
|
+
indexes :pattern, analyzer: 'pattern'
|
|
56
|
+
indexes :trigram, analyzer: 'trigram'
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create example records
|
|
63
|
+
#
|
|
64
|
+
Article.delete_all
|
|
65
|
+
Article.create title: 'Foo'
|
|
66
|
+
Article.create title: 'Foo-Bar'
|
|
67
|
+
Article.create title: 'Foo_Bar_Bazooka'
|
|
68
|
+
Article.create title: 'Foo.Bar'
|
|
69
|
+
|
|
70
|
+
# Index records
|
|
71
|
+
#
|
|
72
|
+
errors = Article.import force: true, refresh: true, return: 'errors'
|
|
73
|
+
puts "[!] Errors importing records: #{errors.map { |d| d['index']['error'] }.join(', ')}".ansi(:red) && exit(1) unless errors.empty?
|
|
74
|
+
|
|
75
|
+
puts '', '-'*80
|
|
76
|
+
|
|
77
|
+
puts "English analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
|
|
78
|
+
"Tokens: " +
|
|
79
|
+
Article.__elasticsearch__.client.indices
|
|
80
|
+
.analyze(index: Article.index_name, body: { field: 'title', text: 'Foo_Bar_1_Bazooka' })['tokens']
|
|
81
|
+
.map { |d| "[#{d['token']}]" }.join(' '),
|
|
82
|
+
"\n"
|
|
83
|
+
|
|
84
|
+
puts "Keyword analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
|
|
85
|
+
"Tokens: " +
|
|
86
|
+
Article.__elasticsearch__.client.indices
|
|
87
|
+
.analyze(index: Article.index_name, body: { field: 'title.keyword', text: 'Foo_Bar_1_Bazooka' })['tokens']
|
|
88
|
+
.map { |d| "[#{d['token']}]" }.join(' '),
|
|
89
|
+
"\n"
|
|
90
|
+
|
|
91
|
+
puts "Pattern analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
|
|
92
|
+
"Tokens: " +
|
|
93
|
+
Article.__elasticsearch__.client.indices
|
|
94
|
+
.analyze(index: Article.index_name, body: { field: 'title.pattern', text: 'Foo_Bar_1_Bazooka' })['tokens']
|
|
95
|
+
.map { |d| "[#{d['token']}]" }.join(' '),
|
|
96
|
+
"\n"
|
|
97
|
+
|
|
98
|
+
puts "Trigram analyzer [Foo_Bar_1_Bazooka]".ansi(:bold),
|
|
99
|
+
"Tokens: " +
|
|
100
|
+
Article.__elasticsearch__.client.indices
|
|
101
|
+
.analyze(index: Article.index_name, body: { field: 'title.trigram', text: 'Foo_Bar_1_Bazooka' })['tokens']
|
|
102
|
+
.map { |d| "[#{d['token']}]" }.join(' '),
|
|
103
|
+
"\n"
|
|
104
|
+
|
|
105
|
+
puts '', '-'*80
|
|
106
|
+
|
|
107
|
+
response = Article.search query: { match: { 'title' => 'foo' } } ;
|
|
108
|
+
|
|
109
|
+
puts "English search for 'foo'".ansi(:bold),
|
|
110
|
+
"#{response.response.hits.total} matches: " +
|
|
111
|
+
response.records.map { |d| d.title }.join(', '),
|
|
112
|
+
"\n"
|
|
113
|
+
|
|
114
|
+
puts '', '-'*80
|
|
115
|
+
|
|
116
|
+
response = Article.search query: { match: { 'title.pattern' => 'foo' } } ;
|
|
117
|
+
|
|
118
|
+
puts "Pattern search for 'foo'".ansi(:bold),
|
|
119
|
+
"#{response.response.hits.total} matches: " +
|
|
120
|
+
response.records.map { |d| d.title }.join(', '),
|
|
121
|
+
"\n"
|
|
122
|
+
|
|
123
|
+
puts '', '-'*80
|
|
124
|
+
|
|
125
|
+
response = Article.search query: { match: { 'title.trigram' => 'zoo' } } ;
|
|
126
|
+
|
|
127
|
+
puts "Trigram search for 'zoo'".ansi(:bold),
|
|
128
|
+
"#{response.response.hits.total} matches: " +
|
|
129
|
+
response.records.map { |d| d.title }.join(', '),
|
|
130
|
+
"\n"
|
|
131
|
+
|
|
132
|
+
puts '', '-'*80
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
require 'pry'; binding.pry;
|
|
@@ -50,11 +50,21 @@ module DataMapperAdapter
|
|
|
50
50
|
#
|
|
51
51
|
module Records
|
|
52
52
|
def records
|
|
53
|
-
klass.all(id:
|
|
53
|
+
klass.all(id: ids)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# ...
|
|
57
57
|
end
|
|
58
|
+
|
|
59
|
+
module Callbacks
|
|
60
|
+
def self.included(model)
|
|
61
|
+
model.class_eval do
|
|
62
|
+
after(:create) { __elasticsearch__.index_document }
|
|
63
|
+
after(:save) { __elasticsearch__.update_document }
|
|
64
|
+
after(:destroy) { __elasticsearch__.delete_document }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
58
68
|
end
|
|
59
69
|
|
|
60
70
|
# Register the adapter
|
data/gemfiles/3.0.gemfile
CHANGED
data/gemfiles/4.0.gemfile
CHANGED
data/gemfiles/5.0.gemfile
CHANGED
|
@@ -56,7 +56,10 @@ module Elasticsearch
|
|
|
56
56
|
# Redefine the `to_a` method to the original one
|
|
57
57
|
#
|
|
58
58
|
sql_records.instance_exec do
|
|
59
|
-
|
|
59
|
+
ar_records_method_name = :to_a
|
|
60
|
+
ar_records_method_name = :records if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5
|
|
61
|
+
|
|
62
|
+
define_singleton_method(ar_records_method_name) do
|
|
60
63
|
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
|
|
61
64
|
self.load
|
|
62
65
|
else
|
|
@@ -63,10 +63,17 @@ module Elasticsearch
|
|
|
63
63
|
# @see https://github.com/karmi/retire/pull/724
|
|
64
64
|
#
|
|
65
65
|
def __find_in_batches(options={}, &block)
|
|
66
|
-
options[:batch_size]
|
|
66
|
+
batch_size = options[:batch_size] || 1_000
|
|
67
|
+
query = options[:query]
|
|
68
|
+
named_scope = options[:scope]
|
|
69
|
+
preprocess = options[:preprocess]
|
|
70
|
+
|
|
71
|
+
scope = all
|
|
72
|
+
scope = scope.send(named_scope) if named_scope
|
|
73
|
+
scope = query.is_a?(Proc) ? scope.class_exec(&query) : scope.where(query) if query
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
yield items
|
|
75
|
+
scope.no_timeout.each_slice(batch_size) do |items|
|
|
76
|
+
yield (preprocess ? self.__send__(preprocess, items) : items)
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
|
|
@@ -397,7 +397,7 @@ module Elasticsearch
|
|
|
397
397
|
# @see http://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Actions:update
|
|
398
398
|
#
|
|
399
399
|
def update_document(options={})
|
|
400
|
-
if attributes_in_database = self.instance_variable_get(:@__changed_model_attributes)
|
|
400
|
+
if attributes_in_database = self.instance_variable_get(:@__changed_model_attributes).presence
|
|
401
401
|
attributes = if respond_to?(:as_indexed_json)
|
|
402
402
|
self.as_indexed_json.select { |k,v| attributes_in_database.keys.map(&:to_s).include? k.to_s }
|
|
403
403
|
else
|
|
@@ -5,6 +5,8 @@ module Elasticsearch
|
|
|
5
5
|
#
|
|
6
6
|
module Naming
|
|
7
7
|
|
|
8
|
+
DEFAULT_DOC_TYPE = '_doc'.freeze
|
|
9
|
+
|
|
8
10
|
module ClassMethods
|
|
9
11
|
|
|
10
12
|
# Get or set the name of the index
|
|
@@ -90,7 +92,7 @@ module Elasticsearch
|
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
def default_document_type
|
|
93
|
-
|
|
95
|
+
DEFAULT_DOC_TYPE
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
end
|
|
@@ -4,7 +4,7 @@ module Elasticsearch
|
|
|
4
4
|
# Common funtionality for classes in the {Elasticsearch::Model::Response} module
|
|
5
5
|
#
|
|
6
6
|
module Base
|
|
7
|
-
attr_reader :klass, :response
|
|
7
|
+
attr_reader :klass, :response, :raw_response
|
|
8
8
|
|
|
9
9
|
# @param klass [Class] The name of the model class
|
|
10
10
|
# @param response [Hash] The full response returned from Elasticsearch client
|
|
@@ -12,7 +12,8 @@ module Elasticsearch
|
|
|
12
12
|
#
|
|
13
13
|
def initialize(klass, response, options={})
|
|
14
14
|
@klass = klass
|
|
15
|
-
@
|
|
15
|
+
@raw_response = response
|
|
16
|
+
@response = response
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# @abstract Implement this method in specific class
|
|
@@ -10,8 +10,7 @@ module Elasticsearch
|
|
|
10
10
|
# Implements Enumerable and forwards its methods to the {#results} object.
|
|
11
11
|
#
|
|
12
12
|
class Response
|
|
13
|
-
attr_reader :klass, :search
|
|
14
|
-
:took, :timed_out, :shards
|
|
13
|
+
attr_reader :klass, :search
|
|
15
14
|
|
|
16
15
|
include Enumerable
|
|
17
16
|
|
|
@@ -27,9 +26,7 @@ module Elasticsearch
|
|
|
27
26
|
# @return [Hash]
|
|
28
27
|
#
|
|
29
28
|
def response
|
|
30
|
-
@response ||=
|
|
31
|
-
HashWrapper.new(search.execute!)
|
|
32
|
-
end
|
|
29
|
+
@response ||= HashWrapper.new(search.execute!)
|
|
33
30
|
end
|
|
34
31
|
|
|
35
32
|
# Returns the collection of "hits" from Elasticsearch
|
|
@@ -51,31 +48,35 @@ module Elasticsearch
|
|
|
51
48
|
# Returns the "took" time
|
|
52
49
|
#
|
|
53
50
|
def took
|
|
54
|
-
|
|
51
|
+
raw_response['took']
|
|
55
52
|
end
|
|
56
53
|
|
|
57
54
|
# Returns whether the response timed out
|
|
58
55
|
#
|
|
59
56
|
def timed_out
|
|
60
|
-
|
|
57
|
+
raw_response['timed_out']
|
|
61
58
|
end
|
|
62
59
|
|
|
63
60
|
# Returns the statistics on shards
|
|
64
61
|
#
|
|
65
62
|
def shards
|
|
66
|
-
HashWrapper.new(
|
|
63
|
+
@shards ||= HashWrapper.new(raw_response['_shards'])
|
|
67
64
|
end
|
|
68
65
|
|
|
69
66
|
# Returns a Hashie::Mash of the aggregations
|
|
70
67
|
#
|
|
71
68
|
def aggregations
|
|
72
|
-
Aggregations.new(
|
|
69
|
+
@aggregations ||= Aggregations.new(raw_response['aggregations'])
|
|
73
70
|
end
|
|
74
71
|
|
|
75
72
|
# Returns a Hashie::Mash of the suggestions
|
|
76
73
|
#
|
|
77
74
|
def suggestions
|
|
78
|
-
Suggestions.new(
|
|
75
|
+
@suggestions ||= Suggestions.new(raw_response['suggest'])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def raw_response
|
|
79
|
+
@raw_response ||= search.execute!
|
|
79
80
|
end
|
|
80
81
|
end
|
|
81
82
|
end
|
|
@@ -11,7 +11,11 @@ class Question < ActiveRecord::Base
|
|
|
11
11
|
|
|
12
12
|
has_many :answers, dependent: :destroy
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
JOIN_TYPE = 'question'.freeze
|
|
15
|
+
JOIN_METADATA = { join_field: JOIN_TYPE}.freeze
|
|
16
|
+
|
|
17
|
+
index_name 'questions_and_answers'.freeze
|
|
18
|
+
document_type 'doc'.freeze
|
|
15
19
|
|
|
16
20
|
mapping do
|
|
17
21
|
indexes :title
|
|
@@ -19,6 +23,12 @@ class Question < ActiveRecord::Base
|
|
|
19
23
|
indexes :author
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
def as_indexed_json(options={})
|
|
27
|
+
# This line is necessary for differences between ActiveModel::Serializers::JSON#as_json versions
|
|
28
|
+
json = as_json(options)[JOIN_TYPE] || as_json(options)
|
|
29
|
+
json.merge(JOIN_METADATA)
|
|
30
|
+
end
|
|
31
|
+
|
|
22
32
|
after_commit lambda { __elasticsearch__.index_document }, on: :create
|
|
23
33
|
after_commit lambda { __elasticsearch__.update_document }, on: :update
|
|
24
34
|
after_commit lambda { __elasticsearch__.delete_document }, on: :destroy
|
|
@@ -29,32 +39,55 @@ class Answer < ActiveRecord::Base
|
|
|
29
39
|
|
|
30
40
|
belongs_to :question
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
JOIN_TYPE = 'answer'.freeze
|
|
43
|
+
|
|
44
|
+
index_name 'questions_and_answers'.freeze
|
|
45
|
+
document_type 'doc'.freeze
|
|
46
|
+
|
|
47
|
+
before_create :randomize_id
|
|
48
|
+
|
|
49
|
+
def randomize_id
|
|
50
|
+
begin
|
|
51
|
+
self.id = SecureRandom.random_number(1_000_000)
|
|
52
|
+
end while Answer.where(id: self.id).exists?
|
|
53
|
+
end
|
|
33
54
|
|
|
34
|
-
mapping
|
|
55
|
+
mapping do
|
|
35
56
|
indexes :text
|
|
36
57
|
indexes :author
|
|
37
58
|
end
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
def as_indexed_json(options={})
|
|
61
|
+
# This line is necessary for differences between ActiveModel::Serializers::JSON#as_json versions
|
|
62
|
+
json = as_json(options)[JOIN_TYPE] || as_json(options)
|
|
63
|
+
json.merge(join_field: { name: JOIN_TYPE, parent: question_id })
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
after_commit lambda { __elasticsearch__.index_document(routing: (question_id || 1)) }, on: :create
|
|
67
|
+
after_commit lambda { __elasticsearch__.update_document(routing: (question_id || 1)) }, on: :update
|
|
68
|
+
after_commit lambda {__elasticsearch__.delete_document(routing: (question_id || 1)) }, on: :destroy
|
|
42
69
|
end
|
|
43
70
|
|
|
44
71
|
module ParentChildSearchable
|
|
45
|
-
INDEX_NAME = 'questions_and_answers'
|
|
72
|
+
INDEX_NAME = 'questions_and_answers'.freeze
|
|
73
|
+
JOIN = 'join'.freeze
|
|
46
74
|
|
|
47
75
|
def create_index!(options={})
|
|
48
76
|
client = Question.__elasticsearch__.client
|
|
49
77
|
client.indices.delete index: INDEX_NAME rescue nil if options[:force]
|
|
50
78
|
|
|
51
79
|
settings = Question.settings.to_hash.merge Answer.settings.to_hash
|
|
52
|
-
|
|
80
|
+
mapping_properties = { join_field: { type: JOIN,
|
|
81
|
+
relations: { Question::JOIN_TYPE => Answer::JOIN_TYPE } } }
|
|
82
|
+
|
|
83
|
+
merged_properties = mapping_properties.merge(Question.mappings.to_hash[:doc][:properties]).merge(
|
|
84
|
+
Answer.mappings.to_hash[:doc][:properties])
|
|
85
|
+
mappings = { doc: { properties: merged_properties }}
|
|
53
86
|
|
|
54
87
|
client.indices.create index: INDEX_NAME,
|
|
55
88
|
body: {
|
|
56
|
-
|
|
57
|
-
|
|
89
|
+
settings: settings.to_hash,
|
|
90
|
+
mappings: mappings }
|
|
58
91
|
end
|
|
59
92
|
|
|
60
93
|
extend self
|
|
@@ -100,34 +133,34 @@ module Elasticsearch
|
|
|
100
133
|
|
|
101
134
|
should "find questions by matching answers" do
|
|
102
135
|
response = Question.search(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
136
|
+
{ query: {
|
|
137
|
+
has_child: {
|
|
138
|
+
type: 'answer',
|
|
139
|
+
query: {
|
|
140
|
+
match: {
|
|
141
|
+
author: 'john'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
})
|
|
114
147
|
|
|
115
148
|
assert_equal 'Second Question', response.records.first.title
|
|
116
149
|
end
|
|
117
150
|
|
|
118
151
|
should "find answers for matching questions" do
|
|
119
152
|
response = Answer.search(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
153
|
+
{ query: {
|
|
154
|
+
has_parent: {
|
|
155
|
+
parent_type: 'question',
|
|
156
|
+
query: {
|
|
157
|
+
match: {
|
|
158
|
+
author: 'john'
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})
|
|
131
164
|
|
|
132
165
|
assert_same_elements ['Adam', 'Ryan'], response.records.map(&:author)
|
|
133
166
|
end
|
|
@@ -136,12 +169,20 @@ module Elasticsearch
|
|
|
136
169
|
Question.where(title: 'First Question').each(&:destroy)
|
|
137
170
|
Question.__elasticsearch__.refresh_index!
|
|
138
171
|
|
|
139
|
-
response = Answer.search
|
|
172
|
+
response = Answer.search(
|
|
173
|
+
{ query: {
|
|
174
|
+
has_parent: {
|
|
175
|
+
parent_type: 'question',
|
|
176
|
+
query: {
|
|
177
|
+
match_all: {}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
})
|
|
140
182
|
|
|
141
183
|
assert_equal 1, response.results.total
|
|
142
184
|
end
|
|
143
185
|
end
|
|
144
|
-
|
|
145
186
|
end
|
|
146
187
|
end
|
|
147
188
|
end
|
|
@@ -16,6 +16,8 @@ module Elasticsearch
|
|
|
16
16
|
include Elasticsearch::Model
|
|
17
17
|
include Elasticsearch::Model::Callbacks
|
|
18
18
|
|
|
19
|
+
document_type 'article'
|
|
20
|
+
|
|
19
21
|
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
|
|
20
22
|
mapping do
|
|
21
23
|
indexes :title, type: 'text', analyzer: 'snowball'
|
|
@@ -221,8 +223,10 @@ module Elasticsearch
|
|
|
221
223
|
|
|
222
224
|
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
|
|
223
225
|
assert_equal 'Testing Coding', response.records.order(title: :desc).first.title
|
|
226
|
+
assert_equal 'Testing Coding', response.records.order(title: :desc)[0].title
|
|
224
227
|
else
|
|
225
228
|
assert_equal 'Testing Coding', response.records.order('title DESC').first.title
|
|
229
|
+
assert_equal 'Testing Coding', response.records.order('title DESC')[0].title
|
|
226
230
|
end
|
|
227
231
|
end
|
|
228
232
|
|
|
@@ -41,7 +41,7 @@ module Elasticsearch
|
|
|
41
41
|
|
|
42
42
|
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
|
|
43
43
|
index: 'article_with_custom_serializations',
|
|
44
|
-
type: '
|
|
44
|
+
type: '_doc',
|
|
45
45
|
id: '1'
|
|
46
46
|
|
|
47
47
|
assert_equal( { 'title' => 'Test' }, a['_source'] )
|
|
@@ -55,7 +55,7 @@ module Elasticsearch
|
|
|
55
55
|
|
|
56
56
|
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
|
|
57
57
|
index: 'article_with_custom_serializations',
|
|
58
|
-
type: '
|
|
58
|
+
type: '_doc',
|
|
59
59
|
id: '1'
|
|
60
60
|
|
|
61
61
|
assert_equal( { 'title' => 'UPDATED' }, a['_source'] )
|
|
@@ -10,21 +10,22 @@ module Elasticsearch
|
|
|
10
10
|
module Model
|
|
11
11
|
class ActiveRecordImportIntegrationTest < Elasticsearch::Test::IntegrationTestCase
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
context "ActiveRecord importing" do
|
|
14
|
+
setup do
|
|
15
|
+
Object.send(:remove_const, :ImportArticle) if defined?(ImportArticle)
|
|
16
|
+
class ::ImportArticle < ActiveRecord::Base
|
|
17
|
+
include Elasticsearch::Model
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
scope :popular, -> { where('views >= 50') }
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
mapping do
|
|
22
|
+
indexes :title, type: 'text'
|
|
23
|
+
indexes :views, type: 'integer'
|
|
24
|
+
indexes :numeric, type: 'integer'
|
|
25
|
+
indexes :created_at, type: 'date'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
25
28
|
|
|
26
|
-
context "ActiveRecord importing" do
|
|
27
|
-
setup do
|
|
28
29
|
ActiveRecord::Schema.define(:version => 1) do
|
|
29
30
|
create_table :import_articles do |t|
|
|
30
31
|
t.string :title
|
|
@@ -110,6 +111,58 @@ module Elasticsearch
|
|
|
110
111
|
end
|
|
111
112
|
end
|
|
112
113
|
|
|
114
|
+
context "ActiveRecord importing when the model has a default scope" do
|
|
115
|
+
|
|
116
|
+
setup do
|
|
117
|
+
Object.send(:remove_const, :ImportArticle) if defined?(ImportArticle)
|
|
118
|
+
class ::ImportArticle < ActiveRecord::Base
|
|
119
|
+
include Elasticsearch::Model
|
|
120
|
+
|
|
121
|
+
default_scope { where('views >= 8') }
|
|
122
|
+
|
|
123
|
+
mapping do
|
|
124
|
+
indexes :title, type: 'text'
|
|
125
|
+
indexes :views, type: 'integer'
|
|
126
|
+
indexes :numeric, type: 'integer'
|
|
127
|
+
indexes :created_at, type: 'date'
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
ActiveRecord::Schema.define(:version => 1) do
|
|
132
|
+
create_table :import_articles do |t|
|
|
133
|
+
t.string :title
|
|
134
|
+
t.integer :views
|
|
135
|
+
t.string :numeric # For the sake of invalid data sent to Elasticsearch
|
|
136
|
+
t.datetime :created_at, :default => 'NOW()'
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
ImportArticle.delete_all
|
|
141
|
+
ImportArticle.__elasticsearch__.delete_index! force: true
|
|
142
|
+
ImportArticle.__elasticsearch__.create_index! force: true
|
|
143
|
+
ImportArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
|
144
|
+
|
|
145
|
+
10.times { |i| ImportArticle.create! title: "Test #{i}", views: i }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
should "import only documents from the default scope" do
|
|
149
|
+
assert_equal 2, ImportArticle.count
|
|
150
|
+
|
|
151
|
+
assert_equal 0, ImportArticle.import
|
|
152
|
+
|
|
153
|
+
ImportArticle.__elasticsearch__.refresh_index!
|
|
154
|
+
assert_equal 2, ImportArticle.search('*').results.total
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
should "import only documents from a specific query combined with the default scope" do
|
|
158
|
+
assert_equal 2, ImportArticle.count
|
|
159
|
+
|
|
160
|
+
assert_equal 0, ImportArticle.import(query: -> { where("title = 'Test 9'") })
|
|
161
|
+
|
|
162
|
+
ImportArticle.__elasticsearch__.refresh_index!
|
|
163
|
+
assert_equal 1, ImportArticle.search('*').results.total
|
|
164
|
+
end
|
|
165
|
+
end
|
|
113
166
|
end
|
|
114
167
|
end
|
|
115
168
|
end
|
|
@@ -167,6 +167,70 @@ if MongoDB.available?
|
|
|
167
167
|
assert response.results.any?, "Search has not returned results: #{response.to_a}"
|
|
168
168
|
end
|
|
169
169
|
end
|
|
170
|
+
|
|
171
|
+
context "importing when the model has a default scope" do
|
|
172
|
+
class ::MongoidArticleWithDefaultScope
|
|
173
|
+
include Mongoid::Document
|
|
174
|
+
include Elasticsearch::Model
|
|
175
|
+
|
|
176
|
+
default_scope -> { where(status: 'active') }
|
|
177
|
+
|
|
178
|
+
field :id, type: String
|
|
179
|
+
field :title, type: String
|
|
180
|
+
field :status, type: String, default: 'active'
|
|
181
|
+
|
|
182
|
+
attr_accessible :title if respond_to? :attr_accessible
|
|
183
|
+
attr_accessible :status if respond_to? :attr_accessible
|
|
184
|
+
|
|
185
|
+
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
|
|
186
|
+
mapping do
|
|
187
|
+
indexes :title, type: 'text', analyzer: 'snowball'
|
|
188
|
+
indexes :status, type: 'text'
|
|
189
|
+
indexes :created_at, type: 'date'
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def as_indexed_json(options={})
|
|
194
|
+
as_json(except: [:id, :_id])
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
setup do
|
|
199
|
+
Elasticsearch::Model::Adapter.register \
|
|
200
|
+
Elasticsearch::Model::Adapter::Mongoid,
|
|
201
|
+
lambda { |klass| !!defined?(::Mongoid::Document) && klass.respond_to?(:ancestors) && klass.ancestors.include?(::Mongoid::Document) }
|
|
202
|
+
|
|
203
|
+
MongoidArticleWithDefaultScope.__elasticsearch__.create_index! force: true
|
|
204
|
+
|
|
205
|
+
MongoidArticleWithDefaultScope.delete_all
|
|
206
|
+
|
|
207
|
+
MongoidArticleWithDefaultScope.create! title: 'Test'
|
|
208
|
+
MongoidArticleWithDefaultScope.create! title: 'Testing Coding'
|
|
209
|
+
MongoidArticleWithDefaultScope.create! title: 'Coding'
|
|
210
|
+
MongoidArticleWithDefaultScope.create! title: 'Test legacy code', status: 'removed'
|
|
211
|
+
|
|
212
|
+
MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
|
|
213
|
+
MongoidArticleWithDefaultScope.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
should "import only documents from the default scope" do
|
|
217
|
+
assert_equal 3, MongoidArticleWithDefaultScope.count
|
|
218
|
+
|
|
219
|
+
assert_equal 0, MongoidArticleWithDefaultScope.import
|
|
220
|
+
|
|
221
|
+
MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
|
|
222
|
+
assert_equal 3, MongoidArticleWithDefaultScope.search('*').results.total
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
should "import only documents from a specific query combined with the default scope" do
|
|
226
|
+
assert_equal 3, MongoidArticleWithDefaultScope.count
|
|
227
|
+
|
|
228
|
+
assert_equal 0, MongoidArticleWithDefaultScope.import(query: -> { where(title: /^Test/) })
|
|
229
|
+
|
|
230
|
+
MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
|
|
231
|
+
assert_equal 2, MongoidArticleWithDefaultScope.search('*').results.total
|
|
232
|
+
end
|
|
233
|
+
end
|
|
170
234
|
end
|
|
171
235
|
|
|
172
236
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -98,7 +98,64 @@ class Elasticsearch::Model::AdapterMongoidTest < Test::Unit::TestCase
|
|
|
98
98
|
assert_equal @transform.call(model), { index: { _id: "1", data: {} } }
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
|
-
end
|
|
102
101
|
|
|
102
|
+
should "limit the relation to a specific scope" do
|
|
103
|
+
relation = mock()
|
|
104
|
+
relation.stubs(:no_timeout).returns(relation)
|
|
105
|
+
relation.expects(:published).returns(relation)
|
|
106
|
+
relation.expects(:each_slice).returns([])
|
|
107
|
+
DummyClassForMongoid.expects(:all).returns(relation)
|
|
108
|
+
|
|
109
|
+
DummyClassForMongoid.__send__ :extend, Elasticsearch::Model::Adapter::Mongoid::Importing
|
|
110
|
+
DummyClassForMongoid.__find_in_batches(scope: :published) do; end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "when limit the relation with proc" do
|
|
114
|
+
setup do
|
|
115
|
+
@query = Proc.new { where(color: "red") }
|
|
116
|
+
end
|
|
117
|
+
should "query with a specific criteria" do
|
|
118
|
+
relation = mock()
|
|
119
|
+
relation.stubs(:no_timeout).returns(relation)
|
|
120
|
+
relation.expects(:class_exec).returns(relation)
|
|
121
|
+
relation.expects(:each_slice).returns([])
|
|
122
|
+
DummyClassForMongoid.expects(:all).returns(relation)
|
|
123
|
+
|
|
124
|
+
DummyClassForMongoid.__find_in_batches(query: @query) do; end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "when limit the relation with hash" do
|
|
129
|
+
setup do
|
|
130
|
+
@query = { color: "red" }
|
|
131
|
+
end
|
|
132
|
+
should "query with a specific criteria" do
|
|
133
|
+
relation = mock()
|
|
134
|
+
relation.stubs(:no_timeout).returns(relation)
|
|
135
|
+
relation.expects(:where).with(@query).returns(relation)
|
|
136
|
+
relation.expects(:each_slice).returns([])
|
|
137
|
+
DummyClassForMongoid.expects(:all).returns(relation)
|
|
138
|
+
|
|
139
|
+
DummyClassForMongoid.__find_in_batches(query: @query) do; end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
should "preprocess the batch if option provided" do
|
|
144
|
+
class << DummyClassForMongoid
|
|
145
|
+
# Updates/transforms the batch while fetching it from the database
|
|
146
|
+
# (eg. with information from an external system)
|
|
147
|
+
#
|
|
148
|
+
def update_batch(batch)
|
|
149
|
+
batch.collect { |b| b.to_s + '!' }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
DummyClassForMongoid.expects(:__find_in_batches).returns( [:a, :b] )
|
|
154
|
+
|
|
155
|
+
DummyClassForMongoid.__find_in_batches(preprocess: :update_batch) do |batch|
|
|
156
|
+
assert_same_elements ["a!", "b!"], batch
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
103
160
|
end
|
|
104
161
|
end
|
data/test/unit/importing_test.rb
CHANGED
|
@@ -146,6 +146,27 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
+
context "with the refresh option" do
|
|
150
|
+
should "refresh the index" do
|
|
151
|
+
DummyImportingModel.expects(:__find_in_batches).with do |options|
|
|
152
|
+
assert_equal 'bar', options[:foo]
|
|
153
|
+
assert_nil options[:refresh]
|
|
154
|
+
true
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
DummyImportingModel.expects(:refresh_index!).with do |options|
|
|
158
|
+
assert_equal 'foo', options[:index]
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
DummyImportingModel.expects(:index_name).returns('foo')
|
|
163
|
+
DummyImportingModel.expects(:document_type).returns('foo')
|
|
164
|
+
DummyImportingModel.stubs(:index_exists?).returns(true)
|
|
165
|
+
|
|
166
|
+
DummyImportingModel.import refresh: true, foo: 'bar'
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
149
170
|
should "allow passing a different index / type" do
|
|
150
171
|
Elasticsearch::Model::Adapter.expects(:from_class)
|
|
151
172
|
.with(DummyImportingModel)
|
data/test/unit/indexing_test.rb
CHANGED
|
@@ -149,7 +149,7 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
|
149
149
|
should "update and return the index mappings" do
|
|
150
150
|
DummyIndexingModel.mappings foo: 'boo'
|
|
151
151
|
DummyIndexingModel.mappings bar: 'bam'
|
|
152
|
-
assert_equal( {
|
|
152
|
+
assert_equal( { _doc: { foo: "boo", bar: "bam", properties: {} } },
|
|
153
153
|
DummyIndexingModel.mappings.to_hash )
|
|
154
154
|
end
|
|
155
155
|
|
|
@@ -176,6 +176,19 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
|
176
176
|
end
|
|
177
177
|
end
|
|
178
178
|
|
|
179
|
+
class ::DummyIndexingModelWithNoChanges
|
|
180
|
+
extend Elasticsearch::Model::Indexing::ClassMethods
|
|
181
|
+
include Elasticsearch::Model::Indexing::InstanceMethods
|
|
182
|
+
|
|
183
|
+
def self.before_save(&block)
|
|
184
|
+
(@callbacks ||= {})[block.hash] = block
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def changes_to_save
|
|
188
|
+
{}
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
179
192
|
class ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson
|
|
180
193
|
extend Elasticsearch::Model::Indexing::ClassMethods
|
|
181
194
|
include Elasticsearch::Model::Indexing::InstanceMethods
|
|
@@ -393,6 +406,26 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
|
393
406
|
instance.update_document
|
|
394
407
|
end
|
|
395
408
|
|
|
409
|
+
should "index instead of update when nothing was changed" do
|
|
410
|
+
client = mock('client')
|
|
411
|
+
instance = ::DummyIndexingModelWithNoChanges.new
|
|
412
|
+
|
|
413
|
+
# Set the fake `changes` hash
|
|
414
|
+
instance.instance_variable_set(:@__changed_model_attributes, {})
|
|
415
|
+
# Overload as_indexed_json for running index
|
|
416
|
+
instance.expects(:as_indexed_json).returns({ 'foo' => 'BAR' })
|
|
417
|
+
|
|
418
|
+
client.expects(:index)
|
|
419
|
+
client.expects(:update).never
|
|
420
|
+
|
|
421
|
+
instance.expects(:client).returns(client)
|
|
422
|
+
instance.expects(:index_name).returns('foo')
|
|
423
|
+
instance.expects(:document_type).returns('bar')
|
|
424
|
+
instance.expects(:id).returns('1')
|
|
425
|
+
|
|
426
|
+
instance.update_document({})
|
|
427
|
+
end
|
|
428
|
+
|
|
396
429
|
should "update only the specific attributes" do
|
|
397
430
|
client = mock('client')
|
|
398
431
|
instance = ::DummyIndexingModelWithCallbacks.new
|
|
@@ -544,7 +577,7 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
|
544
577
|
indices.expects(:create).with do |payload|
|
|
545
578
|
assert_equal 'dummy_indexing_model_for_recreates', payload[:index]
|
|
546
579
|
assert_equal 1, payload[:body][:settings][:index][:number_of_shards]
|
|
547
|
-
assert_equal 'keyword', payload[:body][:mappings][:
|
|
580
|
+
assert_equal 'keyword', payload[:body][:mappings][:_doc][:properties][:foo][:analyzer]
|
|
548
581
|
true
|
|
549
582
|
end.returns({})
|
|
550
583
|
|
|
@@ -69,8 +69,8 @@ class Elasticsearch::Model::NamingInheritanceTest < Test::Unit::TestCase
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
should "return the default document_type" do
|
|
72
|
-
assert_equal "
|
|
73
|
-
assert_equal "
|
|
72
|
+
assert_equal "_doc", TestBase.document_type
|
|
73
|
+
assert_equal "_doc", TestBase.new.document_type
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
should "return the explicit document_type" do
|
data/test/unit/naming_test.rb
CHANGED
|
@@ -29,8 +29,8 @@ class Elasticsearch::Model::NamingTest < Test::Unit::TestCase
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
should "return the default document_type" do
|
|
32
|
-
assert_equal '
|
|
33
|
-
assert_equal '
|
|
32
|
+
assert_equal '_doc', DummyNamingModel.document_type
|
|
33
|
+
assert_equal '_doc', DummyNamingModel.new.document_type
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
should "set and return the index_name" do
|
metadata
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elasticsearch-model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Karel Minarik
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-09-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: elasticsearch
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '1'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activesupport
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -336,6 +336,7 @@ files:
|
|
|
336
336
|
- elasticsearch-model.gemspec
|
|
337
337
|
- examples/activerecord_article.rb
|
|
338
338
|
- examples/activerecord_associations.rb
|
|
339
|
+
- examples/activerecord_custom_analyzer.rb
|
|
339
340
|
- examples/activerecord_mapping_completion.rb
|
|
340
341
|
- examples/activerecord_mapping_edge_ngram.rb
|
|
341
342
|
- examples/couchbase_article.rb
|
|
@@ -432,7 +433,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
432
433
|
version: '0'
|
|
433
434
|
requirements: []
|
|
434
435
|
rubyforge_project:
|
|
435
|
-
rubygems_version: 2.7.
|
|
436
|
+
rubygems_version: 2.7.7
|
|
436
437
|
signing_key:
|
|
437
438
|
specification_version: 4
|
|
438
439
|
summary: ActiveModel/Record integrations for Elasticsearch.
|