elastic_record 4.1.8 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bb2dad78eb2851499ca2b7099f7774d79c375b5a180001bd612113353dfba33c
|
4
|
+
data.tar.gz: ff1262c6527d45f1e9b0e8eb46e24dc881cdb10b5cb51b93e40f72f5a2f366dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1c4fab2b261935713156c78ebf2fd675a7a7b107ac5a415a25dd20c8364690492f566d82b4685e9c223818abafbaf21ef2083d89ad3ad1e2a60bb93ed150685
|
7
|
+
data.tar.gz: 8f9d7ad4d081fe9096c65872c8b0e4fd0c2d4a3fa2df79bd7a8a8a3975fd7a7055c48b664a5f47beb2b7fcea07020e542f64b4f64e188c125e66c27ee11ad70f
|
data/.travis.yml
CHANGED
@@ -2,17 +2,24 @@ rvm: 2.4.1
|
|
2
2
|
cache: bundler
|
3
3
|
sudo: false
|
4
4
|
dist: trusty
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
|
6
|
+
before_install:
|
7
|
+
- wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz
|
8
|
+
- tar -xzf elasticsearch-${ES_VERSION}.tar.gz
|
9
|
+
- ./elasticsearch-${ES_VERSION}/bin/elasticsearch -d
|
10
|
+
|
11
11
|
before_script:
|
12
12
|
- cp test/dummy/.env.example test/dummy/.env
|
13
|
-
-
|
13
|
+
- wget --quiet --waitretry=1 --retry-connrefused --timeout=20 -O - http://127.0.0.1:9200
|
14
14
|
- bundle exec rake app:db:setup
|
15
15
|
- bundle exec rake app:index:reset
|
16
|
+
|
17
|
+
env:
|
18
|
+
global:
|
19
|
+
- ES_VERSION=6.2.3
|
20
|
+
|
16
21
|
services:
|
17
|
-
- elasticsearch
|
18
22
|
- postgresql
|
23
|
+
|
24
|
+
addons:
|
25
|
+
postgresql: 9.6
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
[![Build Status](https://secure.travis-ci.org/data-axle/elastic_record.png?rvm=2.0.0)](http://travis-ci.org/data-axle/elastic_record)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/data-axle/elastic_record.png)](https://codeclimate.com/github/data-axle/elastic_record)
|
4
4
|
|
5
|
-
ElasticRecord is an Elasticsearch
|
5
|
+
ElasticRecord is an Elasticsearch 6.x ORM.
|
6
6
|
|
7
7
|
## Setup ##
|
8
8
|
|
@@ -68,9 +68,6 @@ search.filter(Product.arelastic[:name].prefix("Sca").negate)
|
|
68
68
|
|
69
69
|
# Size is greater than 5
|
70
70
|
search.filter(Product.arelastic[:size].gt(5))
|
71
|
-
|
72
|
-
# Name is 'hola' or name is missing
|
73
|
-
search.filter(Product.arelastic[:name].eq("hola").or(Product.arelastic[:name].missing))
|
74
71
|
```
|
75
72
|
|
76
73
|
Helpful Arel builders can be found at https://github.com/matthuhiggins/arelastic/blob/master/lib/arelastic/builders/filter.rb.
|
@@ -113,12 +110,11 @@ Aggregations are added with the aggregate method:
|
|
113
110
|
search.aggregate('popular_colors' => {'terms' => {'field' => 'color'}})
|
114
111
|
```
|
115
112
|
|
116
|
-
|
113
|
+
Results are retrieved at query time within `aggregations`:
|
117
114
|
|
118
115
|
```ruby
|
119
116
|
search = search.aggregate('popular_colors' => {'terms' => {'field' => 'color'}})
|
120
|
-
search.aggregations
|
121
|
-
#=> {"popular_colors" => {"buckets" => ...}}
|
117
|
+
search.aggregations['popular_colors'].buckets
|
122
118
|
```
|
123
119
|
|
124
120
|
### Getting Results ###
|
@@ -180,12 +176,14 @@ Use the `percolate` method to find records with queries that match.
|
|
180
176
|
|
181
177
|
## Index Configuration
|
182
178
|
|
183
|
-
To avoid elasticsearch dynamically mapping fields, you can directly configure
|
184
|
-
and
|
179
|
+
To avoid elasticsearch dynamically mapping fields, you can directly configure `elastic_index.mapping`
|
180
|
+
and `elastic_index.settings`:
|
185
181
|
|
186
182
|
```ruby
|
187
183
|
class Product
|
188
|
-
|
184
|
+
include ElasticRecord::Model
|
185
|
+
|
186
|
+
elastic_index.mapping = {
|
189
187
|
properties: {
|
190
188
|
name: {type: "text"},
|
191
189
|
status: {type: "keyword"}
|
@@ -194,6 +192,27 @@ class Product
|
|
194
192
|
end
|
195
193
|
```
|
196
194
|
|
195
|
+
Mapping types will be removed in ElasticSearch 7.x. To rename the default mapping type (`_doc`), use `elastic_index.mapping_type`:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class Product
|
199
|
+
include ElasticRecord::Model
|
200
|
+
|
201
|
+
elastic_index.mapping_type = 'product'
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
### Inheritance
|
206
|
+
|
207
|
+
When one model inherits from another, ElasticRecord makes some assumptions about how the child index should be configured. By default:
|
208
|
+
|
209
|
+
* `alias_name` - Same as parent
|
210
|
+
* `mapping` - Same as parent
|
211
|
+
* `mapping_type` - Same as parent
|
212
|
+
* `settings` (including analysis configuration) - Same as parent
|
213
|
+
|
214
|
+
These can all be overridden. For instance, it might be desirable for the child documents to be in a separate index.
|
215
|
+
|
197
216
|
### Load Documents from Source
|
198
217
|
|
199
218
|
To fetch documents without an additional request to a backing ActiveRecord database you can load the documents from `_source`.
|
@@ -242,14 +261,3 @@ Product.elastic_index.reset # Delete related indexes and deploy a n
|
|
242
261
|
Product.elastic_index.refresh # Call the refresh API
|
243
262
|
Product.elastic_index.get_mapping # Get the index mapping defined by elastic search
|
244
263
|
```
|
245
|
-
|
246
|
-
## JSON Adapter ##
|
247
|
-
|
248
|
-
By default, ElasticRecord uses ActiveSupport::JSON to serialize Elasticsearch payloads. There
|
249
|
-
is optional support for using the Oj gem. To use Oj, ensure that oj is required and set:
|
250
|
-
|
251
|
-
```ruby
|
252
|
-
ElasticRecord::JSON.parser = :oj
|
253
|
-
```
|
254
|
-
|
255
|
-
To return to the default parser, set the variable to :active_support.
|
data/elastic_record.gemspec
CHANGED
data/lib/elastic_record.rb
CHANGED
@@ -3,19 +3,31 @@ require 'active_support/concern'
|
|
3
3
|
require 'active_model'
|
4
4
|
|
5
5
|
module ElasticRecord
|
6
|
-
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :Lucene
|
14
|
-
autoload :Model
|
15
|
-
autoload :
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
autoload :AsDocument
|
8
|
+
autoload :Callbacks
|
9
|
+
autoload :Config
|
10
|
+
autoload :Connection
|
11
|
+
autoload :Doctype
|
12
|
+
autoload :Index
|
13
|
+
autoload :Lucene
|
14
|
+
autoload :Model
|
15
|
+
autoload :PercolatorModel
|
16
|
+
autoload :Relation
|
17
|
+
autoload :Searching
|
18
|
+
|
19
|
+
module AggregationResponse
|
20
|
+
extend ActiveSupport::Autoload
|
21
|
+
|
22
|
+
autoload :Aggregation
|
23
|
+
autoload :Bucket
|
24
|
+
autoload :Builder
|
25
|
+
autoload :HasAggregations
|
26
|
+
autoload :MultiBucketAggregation
|
27
|
+
autoload :MultiValueAggregation
|
28
|
+
autoload :SingleBucketAggregation
|
29
|
+
autoload :SingleValueAggregation
|
30
|
+
end
|
19
31
|
|
20
32
|
class << self
|
21
33
|
def configure
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ElasticRecord
|
2
|
+
module AggregationResponse
|
3
|
+
class Aggregation
|
4
|
+
attr_accessor :name, :results, :meta
|
5
|
+
def initialize(name, results)
|
6
|
+
@name = name
|
7
|
+
@results = results
|
8
|
+
|
9
|
+
@results.each do |key, value|
|
10
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<#{self.class} #{results}>"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ElasticRecord
|
2
|
+
module AggregationResponse
|
3
|
+
class Bucket
|
4
|
+
include HasAggregations
|
5
|
+
|
6
|
+
attr_accessor :results
|
7
|
+
def initialize(results)
|
8
|
+
@results = results
|
9
|
+
end
|
10
|
+
|
11
|
+
def key
|
12
|
+
results['key']
|
13
|
+
end
|
14
|
+
|
15
|
+
def doc_count
|
16
|
+
results['doc_count']
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"#<#{self.class} #{results}>"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ElasticRecord
|
2
|
+
module AggregationResponse
|
3
|
+
class Builder
|
4
|
+
AGGREGATION_KLASSES = {
|
5
|
+
SingleBucketAggregation => %w(
|
6
|
+
children
|
7
|
+
sampler
|
8
|
+
filter
|
9
|
+
missing
|
10
|
+
nested
|
11
|
+
reverse_nested
|
12
|
+
global
|
13
|
+
),
|
14
|
+
MultiBucketAggregation => %w(
|
15
|
+
composite
|
16
|
+
date_histogram
|
17
|
+
filters
|
18
|
+
geohash_grid
|
19
|
+
histogram
|
20
|
+
range
|
21
|
+
dterms
|
22
|
+
lterms
|
23
|
+
sterms
|
24
|
+
),
|
25
|
+
SingleValueAggregation => %w(
|
26
|
+
avg
|
27
|
+
cardinality
|
28
|
+
max
|
29
|
+
min
|
30
|
+
value_count
|
31
|
+
),
|
32
|
+
MultiValueAggregation => %w(
|
33
|
+
stats
|
34
|
+
dpercentiles
|
35
|
+
lpercentiles
|
36
|
+
spercentiles
|
37
|
+
)
|
38
|
+
}
|
39
|
+
|
40
|
+
AGGREGATIONS_BY_TYPE = AGGREGATION_KLASSES.each_with_object({}) do |(klass, types), hash|
|
41
|
+
types.each { |type| hash[type] = klass }
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.extract(hash)
|
45
|
+
hash.each_with_object({}) do |(key, results), aggs|
|
46
|
+
next unless key.include?('#')
|
47
|
+
|
48
|
+
type, name = key.split('#')
|
49
|
+
klass = AGGREGATIONS_BY_TYPE.fetch(type)
|
50
|
+
aggs[name] = klass.new(name, results)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module ElasticRecord
|
2
2
|
module AsDocument
|
3
|
-
def as_search_document
|
4
|
-
|
5
|
-
value =
|
3
|
+
def as_search_document(mapping_properties = elastic_index.mapping[:properties])
|
4
|
+
mapping_properties.each_with_object({}) do |(field, mapping), result|
|
5
|
+
value = value_for_elastic_search field, mapping, mapping_properties
|
6
6
|
|
7
7
|
unless value.nil?
|
8
8
|
result[field] = value
|
@@ -11,31 +11,48 @@ module ElasticRecord
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def as_partial_update_document
|
14
|
-
|
14
|
+
mapping_properties = elastic_index.mapping[:properties]
|
15
|
+
changed_fields = respond_to?(:saved_changes) ? saved_changes.keys : changed
|
15
16
|
|
16
|
-
|
17
|
-
if field_mapping =
|
18
|
-
result[field] =
|
17
|
+
changed_fields.each_with_object({}) do |field, result|
|
18
|
+
if field_mapping = mapping_properties[field]
|
19
|
+
result[field] = value_for_elastic_search field, field_mapping, mapping_properties
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
+
def value_for_elastic_search(field, mapping, mapping_properties)
|
24
25
|
value = try field
|
25
26
|
return if value.nil?
|
26
27
|
|
27
|
-
value =
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
value =
|
29
|
+
case mapping[:type]&.to_sym
|
30
|
+
when :object
|
31
|
+
object_mapping_properties = mapping_properties.dig(field, :properties)
|
32
|
+
value_for_elastic_search_object(value, object_mapping_properties)
|
33
|
+
when :nested
|
34
|
+
object_mapping_properties = mapping_properties.dig(field, :properties)
|
35
|
+
value.map { |entry| value_for_elastic_search_object(entry, object_mapping_properties) }
|
36
|
+
when :integer_range, :float_range, :long_range, :double_range, :date_range
|
37
|
+
value_for_elastic_search_range(value)
|
38
|
+
else
|
39
|
+
value
|
40
|
+
end
|
35
41
|
|
36
42
|
if value.present? || value == false
|
37
43
|
value
|
38
44
|
end
|
39
45
|
end
|
46
|
+
|
47
|
+
def value_for_elastic_search_object(object, nested_mapping)
|
48
|
+
object.respond_to?(:as_search_document) ? object.as_search_document(nested_mapping) : object
|
49
|
+
end
|
50
|
+
|
51
|
+
def value_for_elastic_search_range(range)
|
52
|
+
gte = range.begin unless range.begin == -Float::INFINITY
|
53
|
+
lte = range.end unless range.end == Float::INFINITY
|
54
|
+
|
55
|
+
{'gte' => gte, 'lte' => lte}
|
56
|
+
end
|
40
57
|
end
|
41
58
|
end
|
@@ -5,7 +5,7 @@ module ElasticRecord
|
|
5
5
|
|
6
6
|
base.class_eval do
|
7
7
|
after_create :create_index_document
|
8
|
-
after_update :update_index_document, if: :changed?
|
8
|
+
after_update :update_index_document, if: -> { respond_to?(:saved_changes?) ? saved_changes? : changed? }
|
9
9
|
after_destroy :delete_index_document
|
10
10
|
end
|
11
11
|
end
|