elastic_record 4.1.8 → 5.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 +5 -5
- data/.travis.yml +15 -8
- data/Gemfile +1 -1
- data/README.md +29 -21
- data/elastic_record.gemspec +1 -1
- data/lib/elastic_record.rb +25 -13
- data/lib/elastic_record/aggregation_response/aggregation.rb +19 -0
- data/lib/elastic_record/aggregation_response/bucket.rb +24 -0
- data/lib/elastic_record/aggregation_response/builder.rb +55 -0
- data/lib/elastic_record/aggregation_response/has_aggregations.rb +9 -0
- data/lib/elastic_record/aggregation_response/multi_bucket_aggregation.rb +9 -0
- data/lib/elastic_record/aggregation_response/multi_value_aggregation.rb +6 -0
- data/lib/elastic_record/aggregation_response/single_bucket_aggregation.rb +8 -0
- data/lib/elastic_record/aggregation_response/single_value_aggregation.rb +7 -0
- data/lib/elastic_record/as_document.rb +33 -16
- data/lib/elastic_record/callbacks.rb +1 -1
- data/lib/elastic_record/config.rb +3 -0
- data/lib/elastic_record/connection.rb +4 -4
- data/lib/elastic_record/errors.rb +7 -1
- data/lib/elastic_record/index.rb +6 -8
- data/lib/elastic_record/index/analyze.rb +10 -0
- data/lib/elastic_record/index/deferred.rb +2 -2
- data/lib/elastic_record/index/documents.rb +42 -29
- data/lib/elastic_record/index/manage.rb +6 -8
- data/lib/elastic_record/index/mapping.rb +16 -8
- data/lib/elastic_record/index/mapping_type.rb +16 -0
- data/lib/elastic_record/index/mapping_type_test.rb +18 -0
- data/lib/elastic_record/index/settings.rb +6 -17
- data/lib/elastic_record/model.rb +2 -14
- data/lib/elastic_record/percolator_model.rb +11 -19
- data/lib/elastic_record/relation.rb +9 -30
- data/lib/elastic_record/relation/batches.rb +5 -3
- data/lib/elastic_record/relation/delegation.rb +8 -4
- data/lib/elastic_record/relation/finder_methods.rb +8 -0
- data/lib/elastic_record/relation/hits.rb +34 -0
- data/lib/elastic_record/relation/search_methods.rb +17 -22
- data/lib/elastic_record/relation/value_methods.rb +1 -1
- data/test/dummy/app/models/project.rb +16 -4
- data/test/dummy/app/models/warehouse.rb +5 -16
- data/test/dummy/app/models/widget.rb +23 -12
- data/test/dummy/app/models/widget_query.rb +1 -0
- data/test/dummy/db/migrate/20151211225259_create_warehouses.rb +7 -0
- data/test/dummy/db/migrate/20180215140125_create_widgets.rb +11 -0
- data/test/dummy/db/schema.rb +10 -3
- data/test/elastic_record/aggregation_response/bucket_test.rb +8 -0
- data/test/elastic_record/aggregation_response/multi_bucket_aggregation_test.rb +33 -0
- data/test/elastic_record/aggregation_response/single_bucket_aggregation_test.rb +15 -0
- data/test/elastic_record/as_document_test.rb +55 -29
- data/test/elastic_record/callbacks_test.rb +7 -11
- data/test/elastic_record/connection_test.rb +3 -16
- data/test/elastic_record/index/documents_test.rb +56 -11
- data/test/elastic_record/index/manage_test.rb +0 -7
- data/test/elastic_record/index/mapping_test.rb +18 -5
- data/test/elastic_record/index/settings_test.rb +1 -7
- data/test/elastic_record/index_test.rb +0 -4
- data/test/elastic_record/integration/active_record_test.rb +6 -9
- data/test/elastic_record/model_test.rb +5 -2
- data/test/elastic_record/percolator_model_test.rb +25 -11
- data/test/elastic_record/relation/batches_test.rb +29 -47
- data/test/elastic_record/relation/delegation_test.rb +1 -1
- data/test/elastic_record/relation/finder_methods_test.rb +17 -31
- data/test/elastic_record/relation/hits_test.rb +49 -0
- data/test/elastic_record/relation/search_methods_test.rb +20 -16
- data/test/elastic_record/relation_test.rb +19 -50
- data/test/helper.rb +7 -3
- metadata +20 -9
- data/lib/elastic_record/doctype.rb +0 -43
- data/lib/elastic_record/json.rb +0 -29
- data/lib/elastic_record/name_cache.rb +0 -23
- data/test/dummy/db/migrate/20151211225259_create_projects.rb +0 -7
- data/test/elastic_record/doctype_test.rb +0 -45
- data/test/elastic_record/name_cache_test.rb +0 -16
@@ -10,35 +10,27 @@ module ElasticRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
+
DEFAULT_PERCOLATOR_MAPPING = {
|
14
|
+
properties: {
|
15
|
+
query: { type: 'percolator' }
|
16
|
+
}
|
17
|
+
}
|
13
18
|
def elastic_index
|
14
19
|
@elastic_index ||=
|
15
20
|
begin
|
16
|
-
index = ElasticRecord::Index.new(
|
21
|
+
index = ElasticRecord::Index.new(self)
|
22
|
+
index.mapping = DEFAULT_PERCOLATOR_MAPPING
|
23
|
+
index.mapping = percolates_model.elastic_index.mapping
|
24
|
+
index.analysis = percolates_model.elastic_index.analysis
|
17
25
|
index.partial_updates = false
|
18
26
|
index
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
def doctype
|
23
|
-
@doctype ||= Doctype.percolator_doctype
|
24
|
-
end
|
25
|
-
|
26
30
|
def percolate(document)
|
27
|
-
query =
|
28
|
-
"query" => {
|
29
|
-
"percolate" => {
|
30
|
-
"field" => "query",
|
31
|
-
"document_type" => percolates_model.doctype.name,
|
32
|
-
"document" => document
|
33
|
-
}
|
34
|
-
},
|
35
|
-
"size" => 1000
|
36
|
-
}
|
37
|
-
|
38
|
-
hits = elastic_index.search(query)['hits']['hits']
|
39
|
-
ids = hits.map { |hits| hits['_id'] }
|
31
|
+
query = Arelastic::Queries::Percolate.new("query", document)
|
40
32
|
|
41
|
-
|
33
|
+
elastic_search.filter(query).limit(5000)
|
42
34
|
end
|
43
35
|
end
|
44
36
|
end
|
@@ -2,19 +2,20 @@ require 'elastic_record/relation/value_methods'
|
|
2
2
|
require 'elastic_record/relation/batches'
|
3
3
|
require 'elastic_record/relation/delegation'
|
4
4
|
require 'elastic_record/relation/finder_methods'
|
5
|
+
require 'elastic_record/relation/hits'
|
5
6
|
require 'elastic_record/relation/merging'
|
6
7
|
require 'elastic_record/relation/none'
|
7
8
|
require 'elastic_record/relation/search_methods'
|
8
9
|
|
9
10
|
module ElasticRecord
|
10
11
|
class Relation
|
11
|
-
include Batches, Delegation, FinderMethods, Merging, SearchMethods
|
12
|
+
include Batches, Delegation, FinderMethods, Hits, Merging, SearchMethods
|
12
13
|
|
13
14
|
attr_reader :klass, :values
|
14
15
|
|
15
|
-
def initialize(klass)
|
16
|
+
def initialize(klass, values = {})
|
16
17
|
@klass = klass
|
17
|
-
@values =
|
18
|
+
@values = values
|
18
19
|
end
|
19
20
|
|
20
21
|
def count
|
@@ -22,7 +23,10 @@ module ElasticRecord
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def aggregations
|
25
|
-
|
26
|
+
@aggregations ||= begin
|
27
|
+
results = search_results['aggregations']
|
28
|
+
ElasticRecord::AggregationResponse::Builder.extract(results)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
def explain(id)
|
@@ -35,11 +39,7 @@ module ElasticRecord
|
|
35
39
|
end
|
36
40
|
|
37
41
|
def to_a
|
38
|
-
@records ||= load_hits
|
39
|
-
end
|
40
|
-
|
41
|
-
def to_ids
|
42
|
-
search_hits.map { |hit| hit['_id'] }
|
42
|
+
@records ||= load_hits(search_hits)
|
43
43
|
end
|
44
44
|
|
45
45
|
def delete_all
|
@@ -64,30 +64,9 @@ module ElasticRecord
|
|
64
64
|
|
65
65
|
private
|
66
66
|
|
67
|
-
def search_hits
|
68
|
-
search_results['hits']['hits']
|
69
|
-
end
|
70
|
-
|
71
67
|
def reset
|
72
68
|
@search_results = @records = nil
|
73
69
|
end
|
74
70
|
|
75
|
-
def search_results
|
76
|
-
@search_results ||= begin
|
77
|
-
options = search_type_value ? {search_type: search_type_value} : {}
|
78
|
-
search = as_elastic.update('_source' => klass.elastic_index.load_from_source)
|
79
|
-
|
80
|
-
klass.elastic_index.search(search, options)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def load_hits
|
85
|
-
if klass.elastic_index.load_from_source
|
86
|
-
search_hits.map { |hit| klass.new(hit['_source'].update('id' => hit['_id'])) }
|
87
|
-
else
|
88
|
-
scope = select_values.any? ? klass.select(select_values) : klass
|
89
|
-
scope.find(to_ids)
|
90
|
-
end
|
91
|
-
end
|
92
71
|
end
|
93
72
|
end
|
@@ -8,13 +8,15 @@ module ElasticRecord
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def find_in_batches(options = {})
|
11
|
-
|
12
|
-
yield
|
11
|
+
build_scroll_enumerator(options).each_slice do |hits|
|
12
|
+
yield load_hits(hits)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def find_ids_in_batches(options = {}, &block)
|
17
|
-
build_scroll_enumerator(options).each_slice
|
17
|
+
build_scroll_enumerator(options).each_slice do |hits|
|
18
|
+
yield map_hits_to_ids(hits)
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
def build_scroll_enumerator(options)
|
@@ -10,15 +10,19 @@ module ElasticRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
13
|
+
def respond_to_missing?(method, include_private = false)
|
14
|
+
super || klass.respond_to?(method, include_private) || Array.method_defined?(method)
|
15
|
+
end
|
16
|
+
|
13
17
|
def method_missing(method, *args, &block)
|
14
|
-
if
|
15
|
-
scoping { klass.send(method, *args, &block) }
|
16
|
-
elsif Array.method_defined?(method)
|
18
|
+
if Array.method_defined?(method)
|
17
19
|
to_a.send(method, *args, &block)
|
20
|
+
elsif klass.respond_to?(method)
|
21
|
+
scoping { klass.send(method, *args, &block) }
|
18
22
|
else
|
19
23
|
super
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
24
|
-
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ElasticRecord
|
2
|
+
class Relation
|
3
|
+
module Hits
|
4
|
+
def to_ids
|
5
|
+
map_hits_to_ids search_hits
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_hits(search_hits)
|
9
|
+
if klass.elastic_index.load_from_source
|
10
|
+
search_hits.map { |hit| klass.new(hit['_source'].update('id' => hit['_id'])) }
|
11
|
+
else
|
12
|
+
klass.find map_hits_to_ids(search_hits)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def map_hits_to_ids(hits)
|
17
|
+
hits.map { |hit| hit['_id'] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def search_hits
|
21
|
+
search_results['hits']['hits']
|
22
|
+
end
|
23
|
+
|
24
|
+
def search_results
|
25
|
+
@search_results ||= begin
|
26
|
+
options = {typed_keys: true}
|
27
|
+
options[:search_type] = search_type_value if search_type_value
|
28
|
+
|
29
|
+
klass.elastic_index.search(as_elastic, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -48,6 +48,22 @@ module ElasticRecord
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
%i(
|
52
|
+
includes
|
53
|
+
joins
|
54
|
+
select
|
55
|
+
where
|
56
|
+
).each do |ar_method|
|
57
|
+
define_method ar_method do |*args, &block|
|
58
|
+
result = klass.send(ar_method, *args, &block)
|
59
|
+
if result.is_a?(ActiveRecord::Relation)
|
60
|
+
self.class.new(result, values)
|
61
|
+
else
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
51
67
|
def query!(value)
|
52
68
|
self.query_value = value
|
53
69
|
self
|
@@ -70,14 +86,6 @@ module ElasticRecord
|
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
73
|
-
def find_by(*args)
|
74
|
-
filter(*args).first
|
75
|
-
end
|
76
|
-
|
77
|
-
def find_by!(*args)
|
78
|
-
filter(*args).first!
|
79
|
-
end
|
80
|
-
|
81
89
|
def limit!(value)
|
82
90
|
self.limit_value = value
|
83
91
|
self
|
@@ -96,19 +104,6 @@ module ElasticRecord
|
|
96
104
|
clone.offset! value
|
97
105
|
end
|
98
106
|
|
99
|
-
def select!(*args)
|
100
|
-
self.select_values += args.flatten
|
101
|
-
self
|
102
|
-
end
|
103
|
-
|
104
|
-
def select(*args, &block)
|
105
|
-
if block_given?
|
106
|
-
to_a.select(&block)
|
107
|
-
else
|
108
|
-
clone.select! *args
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
107
|
def search_options!(options)
|
113
108
|
self.search_options_value ||= {}
|
114
109
|
self.search_options_value.merge! options
|
@@ -233,7 +228,7 @@ module ElasticRecord
|
|
233
228
|
if filter.is_a?(Arelastic::Nodes::Node)
|
234
229
|
nodes << filter
|
235
230
|
elsif filter.is_a?(ElasticRecord::Relation)
|
236
|
-
nodes << Arelastic::Queries::HasChild.new(filter.elastic_index.
|
231
|
+
nodes << Arelastic::Queries::HasChild.new(filter.elastic_index.mapping_type, filter.as_elastic['query'])
|
237
232
|
else
|
238
233
|
filter.each do |field, terms|
|
239
234
|
case terms
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ElasticRecord
|
2
2
|
class Relation
|
3
|
-
MULTI_VALUE_METHODS = [:extending, :filter, :order, :
|
3
|
+
MULTI_VALUE_METHODS = [:extending, :filter, :order, :aggregation]
|
4
4
|
SINGLE_VALUE_METHODS = [:query, :limit, :offset, :search_options, :search_type, :reverse_order]
|
5
5
|
end
|
6
6
|
end
|
@@ -1,7 +1,19 @@
|
|
1
|
-
class Project
|
1
|
+
class Project
|
2
|
+
class << self
|
3
|
+
def base_class
|
4
|
+
self
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
include ActiveModel::Model
|
2
9
|
include ElasticRecord::Model
|
3
10
|
|
4
|
-
|
5
|
-
|
6
|
-
|
11
|
+
attr_accessor :id, :name
|
12
|
+
alias_method :as_json, :as_search_document
|
13
|
+
|
14
|
+
elastic_index.load_from_source = true
|
15
|
+
|
16
|
+
def as_search_document
|
17
|
+
{ name: name }
|
18
|
+
end
|
7
19
|
end
|
@@ -1,19 +1,8 @@
|
|
1
|
-
class Warehouse
|
2
|
-
class << self
|
3
|
-
def base_class
|
4
|
-
self
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
include ActiveModel::Model
|
1
|
+
class Warehouse < ActiveRecord::Base
|
9
2
|
include ElasticRecord::Model
|
10
3
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def as_search_document
|
17
|
-
{ name: name }
|
18
|
-
end
|
4
|
+
elastic_index.mapping_type = 'warehouse'
|
5
|
+
elastic_index.mapping[:properties].update(
|
6
|
+
'name' => { type: 'keyword' }
|
7
|
+
)
|
19
8
|
end
|
@@ -1,22 +1,37 @@
|
|
1
|
-
class Widget
|
2
|
-
include
|
1
|
+
class Widget < ActiveRecord::Base
|
2
|
+
include ElasticRecord::Model
|
3
|
+
self.elastic_index.partial_updates = true
|
3
4
|
|
5
|
+
belongs_to :warehouse
|
4
6
|
validates :color, format: {with: /[a-z]/}
|
5
7
|
|
6
|
-
|
8
|
+
class WidgetPart
|
9
|
+
include ElasticRecord::Model
|
10
|
+
attr_accessor :name
|
11
|
+
end
|
7
12
|
|
8
|
-
self.
|
13
|
+
self.elastic_index.mapping_type = 'widget'
|
14
|
+
self.elastic_index.mapping[:properties].update(
|
9
15
|
'name' => {
|
10
|
-
type: '
|
16
|
+
type: 'text',
|
11
17
|
fields: {
|
12
|
-
|
18
|
+
raw: { type: 'keyword' }
|
13
19
|
}
|
14
20
|
},
|
15
21
|
'color' => {
|
16
|
-
type: '
|
22
|
+
type: 'keyword'
|
17
23
|
},
|
18
24
|
'warehouse_id' => {
|
19
|
-
type: '
|
25
|
+
type: 'keyword'
|
26
|
+
},
|
27
|
+
'price' => {
|
28
|
+
type: 'long'
|
29
|
+
},
|
30
|
+
'widget_part' => {
|
31
|
+
type: 'object',
|
32
|
+
properties: {
|
33
|
+
'name' => { type: 'keyword' }
|
34
|
+
}
|
20
35
|
}
|
21
36
|
)
|
22
37
|
|
@@ -31,8 +46,4 @@ class Widget
|
|
31
46
|
end
|
32
47
|
end
|
33
48
|
end
|
34
|
-
|
35
|
-
def warehouse=(other)
|
36
|
-
self.warehouse_id = other.id
|
37
|
-
end
|
38
49
|
end
|
data/test/dummy/db/schema.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# This file is auto-generated from the current state of the database. Instead
|
3
2
|
# of editing this file, please use the migrations feature of Active Record to
|
4
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
@@ -11,13 +10,21 @@
|
|
11
10
|
#
|
12
11
|
# It's strongly recommended that you check this file into your version control system.
|
13
12
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 20180215140125) do
|
15
14
|
|
16
15
|
# These are extensions that must be enabled in order to support this database
|
17
16
|
enable_extension "plpgsql"
|
18
17
|
|
19
|
-
create_table "
|
18
|
+
create_table "warehouses", force: :cascade do |t|
|
20
19
|
t.string "name", null: false
|
21
20
|
end
|
22
21
|
|
22
|
+
create_table "widgets", force: :cascade do |t|
|
23
|
+
t.string "warehouse_id"
|
24
|
+
t.string "name"
|
25
|
+
t.string "color"
|
26
|
+
t.integer "price"
|
27
|
+
t.jsonb "widget_part"
|
28
|
+
end
|
29
|
+
|
23
30
|
end
|