elastic_record 5.5.0 → 5.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea174acd40e5c2ec841c31085fea2a2994e0d05a705042d5ddf044e8c8962376
4
- data.tar.gz: 1adb61c8170f1e966a7acb0c7c68ac95cc5f93181a81d6118f166de0cdf45a6a
3
+ metadata.gz: 0c916b053e2c2a228b4da081dad0b380dfcd7aea05c3f970134d25f68b9c9a72
4
+ data.tar.gz: 7fa353180b59dcc3fafa81a1e4d6def1d1873b1748079c1d676d38458ea7ba78
5
5
  SHA512:
6
- metadata.gz: 741a6d678b18f9c373e7bc6412e98ab7ec5b97241af2fde09c84fb5b5baebac69d5236a893886c88ed73c37d81f4e426d4182f5818659cb5f675e820494e5d06
7
- data.tar.gz: 848de3cb5054a6997aa8022721bf7dff2c40740921a127611033ec87dcda6e862945697ef19414c775b7b8c970671e19f251d6812cd193ad3eb797c52c9e95b8
6
+ metadata.gz: aadbe8c69f0a39e70d0cafc2bc77785cfabb1ffec23b5480eb6269cfd7cac955ff6761a2c3d375f68f0d1b625c4e09de89d7913ac84a347161fac3895d4242f1
7
+ data.tar.gz: 642e51f60c39f3102a4c716b60e3c0ec84c89b5d75ed5828c06f73934c61eebb9a0e19892a0ffa12bdc966b7c0b46608f2337ec522b333929d3ba574d0b49678
data/.travis.yml CHANGED
@@ -3,19 +3,19 @@ cache: bundler
3
3
  dist: xenial
4
4
 
5
5
  before_install:
6
- - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz
7
- - tar -xzf elasticsearch-${ES_VERSION}.tar.gz
6
+ - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz
7
+ - tar -xzf elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz
8
8
  - ./elasticsearch-${ES_VERSION}/bin/elasticsearch -d
9
9
 
10
10
  before_script:
11
11
  - cp test/dummy/.env.example test/dummy/.env
12
- - wget --quiet --waitretry=1 --retry-connrefused --timeout=20 -O - http://127.0.0.1:9200
12
+ - for i in 1 2 3 ; do wget --quiet --waitretry=1 --retry-connrefused --timeout=30 -O - http://127.0.0.1:9200 && break ; done
13
13
  - bundle exec rake app:db:setup
14
14
  - bundle exec rake app:index:reset
15
15
 
16
16
  env:
17
17
  global:
18
- - ES_VERSION=6.4.3
18
+ - ES_VERSION=7.1.1
19
19
 
20
20
  services:
21
21
  - postgresql
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 6.x ORM.
5
+ ElasticRecord is an Elasticsearch 6.x and 7.x ORM.
6
6
 
7
7
  ## Setup ##
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'elastic_record'
5
- s.version = '5.5.0'
5
+ s.version = '5.6.0'
6
6
  s.summary = 'An Elasticsearch querying ORM'
7
7
  s.description = 'Find your records with Elasticsearch'
8
8
 
@@ -10,42 +10,38 @@ module ElasticRecord
10
10
  end
11
11
  end
12
12
 
13
- def as_partial_update_document
14
- mapping_properties = elastic_index.mapping[:properties]
13
+ def as_partial_update_document(mapping_properties = elastic_index.mapping[:properties])
15
14
  changed_fields = respond_to?(:saved_changes) ? saved_changes.keys : changed
16
15
 
17
16
  changed_fields.each_with_object({}) do |field, result|
18
17
  if field_mapping = mapping_properties[field]
19
- result[field] = value_for_elastic_search field, field_mapping, mapping_properties
18
+ result[field] = value_for_elastic_search(field, field_mapping, mapping_properties, partial: true)
20
19
  end
21
20
  end
22
21
  end
23
22
 
24
- def value_for_elastic_search(field, mapping, mapping_properties)
25
- value = try field
26
- return if value.nil?
27
-
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
41
-
42
- if value.present? || value == false
43
- value
23
+ def value_for_elastic_search(field, mapping, mapping_properties, partial: false)
24
+ return if (value = try(field)).nil?
25
+
26
+ case mapping[:type]&.to_sym
27
+ when :object
28
+ object_mapping_properties = mapping_properties.dig(field, :properties)
29
+ value_for_elastic_search_object(value, object_mapping_properties, partial: partial)
30
+ when :nested
31
+ return nil if value.empty?
32
+
33
+ object_mapping_properties = mapping_properties.dig(field, :properties)
34
+ value.map { |entry| value_for_elastic_search_object(entry, object_mapping_properties) }
35
+ when :integer_range, :float_range, :long_range, :double_range, :date_range
36
+ value_for_elastic_search_range(value)
37
+ else
38
+ value if value.present? || value == false
44
39
  end
45
40
  end
46
41
 
47
- def value_for_elastic_search_object(object, nested_mapping)
48
- object.respond_to?(:as_search_document) ? object.as_search_document(nested_mapping) : object
42
+ def value_for_elastic_search_object(object, nested_mapping, partial: false)
43
+ method = partial ? :as_partial_update_document : :as_search_document
44
+ object.respond_to?(method) ? object.public_send(method, nested_mapping) : object
49
45
  end
50
46
 
51
47
  def value_for_elastic_search_range(range)
@@ -3,6 +3,7 @@ require 'elastic_record/index/deferred'
3
3
  require 'elastic_record/index/documents'
4
4
  require 'elastic_record/index/manage'
5
5
  require 'elastic_record/index/mapping'
6
+ require 'elastic_record/index/search'
6
7
  require 'elastic_record/index/settings'
7
8
  require 'elastic_record/index/mapping_type'
8
9
 
@@ -27,7 +28,7 @@ module ElasticRecord
27
28
  # [update_mapping]
28
29
  # Update elastic search's mapping
29
30
  class Index
30
- include Documents
31
+ include Documents, Search
31
32
  include Manage
32
33
  include Mapping, Settings
33
34
  include Analyze
@@ -40,7 +40,7 @@ module ElasticRecord
40
40
 
41
41
  if READ_METHODS.include?(method)
42
42
  flush_deferred_actions!
43
- if method == :json_get && args.first =~ /^\/(.*)\/_search/
43
+ if method == :json_get && args.first =~ /^\/(.*)\/_m?search/
44
44
  index.real_connection.json_post("/#{$1.partition('/').first}/_refresh")
45
45
  end
46
46
 
@@ -2,62 +2,6 @@ require 'active_support/core_ext/object/to_query'
2
2
 
3
3
  module ElasticRecord
4
4
  class Index
5
- class ScrollEnumerator
6
- attr_reader :keep_alive, :batch_size, :scroll_id
7
- def initialize(elastic_index, search: nil, scroll_id: nil, keep_alive:, batch_size:)
8
- @elastic_index = elastic_index
9
- @search = search
10
- @scroll_id = scroll_id
11
- @keep_alive = keep_alive
12
- @batch_size = batch_size
13
- end
14
-
15
- def each_slice(&block)
16
- while (hits = request_more_hits.hits).any?
17
- hits.each_slice(batch_size, &block)
18
- end
19
-
20
- @elastic_index.delete_scroll(scroll_id)
21
- end
22
-
23
- def request_more_ids
24
- request_more_hits.to_ids
25
- end
26
-
27
- def request_more_hits
28
- SearchHits.from_response(@elastic_index.model, request_next_scroll)
29
- end
30
-
31
- def request_next_scroll
32
- if scroll_id
33
- response = @elastic_index.scroll(scroll_id, keep_alive)
34
-
35
- if response['_scroll_id'] != scroll_id
36
- @elastic_index.delete_scroll(scroll_id)
37
- end
38
- else
39
- response = initial_search_response
40
- end
41
-
42
- @scroll_id = response['_scroll_id']
43
-
44
- response
45
- end
46
-
47
- def total_hits
48
- initial_search_response['hits']['total']
49
- end
50
-
51
- def initial_search_response
52
- @initial_search_response ||= begin
53
- search_options = { size: batch_size, scroll: keep_alive }
54
- elastic_query = @search.reverse_merge('sort' => '_doc')
55
-
56
- @elastic_index.search(elastic_query, search_options)
57
- end
58
- end
59
- end
60
-
61
5
  module Documents
62
6
  def index_record(record, index_name: alias_name)
63
7
  unless disabled
@@ -142,42 +86,6 @@ module ElasticRecord
142
86
  end
143
87
  end
144
88
 
145
- def record_exists?(id)
146
- get(id)['found']
147
- end
148
-
149
- def search(elastic_query, options = {})
150
- url = "_search"
151
- if options.any?
152
- url += "?#{options.to_query}"
153
- end
154
-
155
- get url, elastic_query.update('_source' => load_from_source)
156
- end
157
-
158
- def explain(id, elastic_query)
159
- get "_explain", elastic_query
160
- end
161
-
162
- def build_scroll_enumerator(search: nil, scroll_id: nil, batch_size: 100, keep_alive: ElasticRecord::Config.scroll_keep_alive)
163
- ScrollEnumerator.new(self, search: search, scroll_id: scroll_id, batch_size: batch_size, keep_alive: keep_alive)
164
- end
165
-
166
- def scroll(scroll_id, scroll_keep_alive)
167
- options = {scroll_id: scroll_id, scroll: scroll_keep_alive}
168
- connection.json_get("/_search/scroll?#{options.to_query}")
169
- rescue ElasticRecord::ConnectionError => e
170
- case e.status_code
171
- when '400' then raise ElasticRecord::InvalidScrollError, e.message
172
- when '404' then raise ElasticRecord::ExpiredScrollError, e.message
173
- else raise e
174
- end
175
- end
176
-
177
- def delete_scroll(scroll_id)
178
- connection.json_delete('/_search/scroll', { scroll_id: scroll_id })
179
- end
180
-
181
89
  def bulk(options = {}, &block)
182
90
  if current_bulk_batch
183
91
  yield
@@ -8,12 +8,13 @@ module ElasticRecord
8
8
  end
9
9
 
10
10
  def create(index_name = new_index_name, setting_overrides: {})
11
- connection.json_put "/#{index_name}", {
12
- "mappings" => {
13
- mapping_type => mapping
14
- },
11
+ mapping_params = {
12
+ "mappings" => (custom_mapping_type_name? ? { mapping_type => mapping } : mapping),
15
13
  "settings" => settings.merge(setting_overrides)
16
14
  }
15
+
16
+ # TODO: Remove include_type_name when ES8 support is added
17
+ connection.json_put "/#{index_name}?include_type_name=#{custom_mapping_type_name?}", mapping_params
17
18
  index_name
18
19
  end
19
20
 
@@ -16,11 +16,11 @@ module ElasticRecord
16
16
  end
17
17
 
18
18
  def update_mapping(index_name = alias_name)
19
- connection.json_put "/#{index_name}/_mapping/#{mapping_type}", mapping
19
+ connection.json_put "/#{index_name}/_mapping?include_type_name=false", mapping
20
20
  end
21
21
 
22
22
  def get_mapping(index_name = alias_name)
23
- json = connection.json_get "/#{index_name}/_mapping/#{mapping_type}"
23
+ json = connection.json_get "/#{index_name}/_mapping?include_type_name=false"
24
24
 
25
25
  unless json.empty?
26
26
  json.values.first['mappings']
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ElasticRecord
2
4
  class Index
3
5
  # This module facilitates the removal of multiple mapping types from ElasticSearch.
@@ -6,10 +8,16 @@ module ElasticRecord
6
8
  # * 6.x - Type defaults to _doc, but any type can be specified
7
9
  # * 7.x - Only _doc will be supported, effectively removing the type concept.
8
10
  module MappingType
9
- attr_accessor :mapping_type
11
+ attr_writer :mapping_type
12
+
13
+ DEFAULT_MAPPING_TYPE = '_doc'
10
14
 
11
15
  def mapping_type
12
- @mapping_type || '_doc'
16
+ @mapping_type || DEFAULT_MAPPING_TYPE
17
+ end
18
+
19
+ def custom_mapping_type_name?
20
+ mapping_type != MappingType::DEFAULT_MAPPING_TYPE
13
21
  end
14
22
  end
15
23
  end
@@ -0,0 +1,103 @@
1
+ require 'active_support/core_ext/object/to_query'
2
+
3
+ module ElasticRecord
4
+ class Index
5
+ class ScrollEnumerator
6
+ attr_reader :keep_alive, :batch_size, :scroll_id
7
+ def initialize(elastic_index, search: nil, scroll_id: nil, keep_alive:, batch_size:)
8
+ @elastic_index = elastic_index
9
+ @search = search
10
+ @scroll_id = scroll_id
11
+ @keep_alive = keep_alive
12
+ @batch_size = batch_size
13
+ end
14
+
15
+ def each_slice(&block)
16
+ while (hits = request_more_hits.hits).any?
17
+ hits.each_slice(batch_size, &block)
18
+ end
19
+
20
+ @elastic_index.delete_scroll(scroll_id)
21
+ end
22
+
23
+ def request_more_ids
24
+ request_more_hits.to_ids
25
+ end
26
+
27
+ def request_more_hits
28
+ SearchHits.from_response(request_next_scroll)
29
+ end
30
+
31
+ def request_next_scroll
32
+ if scroll_id
33
+ response = @elastic_index.scroll(scroll_id, keep_alive)
34
+
35
+ if response['_scroll_id'] != scroll_id
36
+ @elastic_index.delete_scroll(scroll_id)
37
+ end
38
+ else
39
+ response = initial_search_response
40
+ end
41
+
42
+ @scroll_id = response['_scroll_id']
43
+
44
+ response
45
+ end
46
+
47
+ def total_hits
48
+ SearchHits.from_response(initial_search_response).total
49
+ end
50
+
51
+ def initial_search_response
52
+ @initial_search_response ||= begin
53
+ search_options = { size: batch_size, scroll: keep_alive }
54
+ elastic_query = @search.reverse_merge('sort' => '_doc')
55
+
56
+ @elastic_index.search(elastic_query, search_options)
57
+ end
58
+ end
59
+ end
60
+
61
+ module Search
62
+ def record_exists?(id)
63
+ get(id)['found']
64
+ end
65
+
66
+ def search(elastic_query, options = {})
67
+ url = "_search"
68
+ url += "?#{options.to_query}" if options.any?
69
+
70
+ get url, elastic_query
71
+ end
72
+
73
+ def multi_search(headers_and_bodies)
74
+ queries = headers_and_bodies.flat_map { |header, body| [header.to_json, body.to_json] }
75
+ queries = queries.join("\n") + "\n"
76
+ get "_msearch", queries
77
+ end
78
+
79
+ def explain(id, elastic_query)
80
+ get "_explain", elastic_query
81
+ end
82
+
83
+ def build_scroll_enumerator(search: nil, scroll_id: nil, batch_size: 100, keep_alive: ElasticRecord::Config.scroll_keep_alive)
84
+ ScrollEnumerator.new(self, search: search, scroll_id: scroll_id, batch_size: batch_size, keep_alive: keep_alive)
85
+ end
86
+
87
+ def scroll(scroll_id, scroll_keep_alive)
88
+ options = {scroll_id: scroll_id, scroll: scroll_keep_alive}
89
+ connection.json_get("/_search/scroll?#{options.to_query}")
90
+ rescue ElasticRecord::ConnectionError => e
91
+ case e.status_code
92
+ when '400' then raise ElasticRecord::InvalidScrollError, e.message
93
+ when '404' then raise ElasticRecord::ExpiredScrollError, e.message
94
+ else raise e
95
+ end
96
+ end
97
+
98
+ def delete_scroll(scroll_id)
99
+ connection.json_delete('/_search/scroll', { scroll_id: scroll_id })
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,18 +1,16 @@
1
1
  module ElasticRecord
2
2
  module Model
3
- def self.included(base)
4
- base.class_eval do
5
- extend Searching
6
- extend ClassMethods
7
- extend FromSearchHit
8
- include Callbacks
9
- include AsDocument
10
-
11
- class_attribute :elastic_connection
12
- self.elastic_connection = ElasticRecord::Connection.new(ElasticRecord::Config.servers, ElasticRecord::Config.connection_options)
13
-
14
- singleton_class.delegate :query, :filter, :aggregate, to: :elastic_search
15
- end
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend Searching
7
+ extend ClassMethods
8
+ extend FromSearchHit
9
+ include Callbacks
10
+ include AsDocument
11
+
12
+ singleton_class.delegate :query, :filter, :aggregate, to: :elastic_search
13
+ mattr_accessor :elastic_connection_cache, instance_writer: false
16
14
  end
17
15
 
18
16
  module ClassMethods
@@ -37,6 +35,10 @@ module ElasticRecord
37
35
  def elastic_index=(index)
38
36
  @elastic_index = index
39
37
  end
38
+
39
+ def elastic_connection
40
+ self.elastic_connection_cache ||= ElasticRecord::Connection.new(ElasticRecord::Config.servers, ElasticRecord::Config.connection_options)
41
+ end
40
42
  end
41
43
 
42
44
  def index_to_elasticsearch
@@ -14,13 +14,25 @@ module ElasticRecord
14
14
 
15
15
  attr_reader :klass, :values
16
16
 
17
- def initialize(klass, values = {})
17
+ def initialize(klass, values: {})
18
18
  @klass = klass
19
19
  @values = values
20
20
  end
21
21
 
22
+ def initialize_copy(other)
23
+ @values = @values.dup
24
+ reset
25
+ end
26
+
27
+ def becomes(klass)
28
+ became = klass.allocate
29
+ became.instance_variable_set(:@klass, @klass)
30
+ became.instance_variable_set(:@values, @values.dup)
31
+ became
32
+ end
33
+
22
34
  def count
23
- search_results['hits']['total']
35
+ search_hits.total
24
36
  end
25
37
 
26
38
  def aggregations
@@ -34,13 +46,16 @@ module ElasticRecord
34
46
  klass.elastic_index.explain(id, as_elastic)
35
47
  end
36
48
 
37
- def initialize_copy(other)
38
- @values = @values.dup
39
- reset
49
+ def to_a
50
+ @records ||= find_hits(search_hits)
40
51
  end
41
52
 
42
- def to_a
43
- @records ||= search_hits.to_records
53
+ def find_hits(search_hits)
54
+ if klass.elastic_index.load_from_source
55
+ search_hits.hits.map { |hit| klass.from_search_hit(hit) }
56
+ else
57
+ klass.find search_hits.to_ids
58
+ end
44
59
  end
45
60
 
46
61
  def delete_all
@@ -9,7 +9,7 @@ module ElasticRecord
9
9
 
10
10
  def find_in_batches(options = {})
11
11
  find_hits_in_batches(options) do |hits|
12
- yield hits.to_records
12
+ yield find_hits(hits)
13
13
  end
14
14
  end
15
15
 
@@ -21,7 +21,7 @@ module ElasticRecord
21
21
 
22
22
  def find_hits_in_batches(options = {})
23
23
  build_scroll_enumerator(options).each_slice do |hits|
24
- yield SearchHits.new(klass, hits)
24
+ yield SearchHits.new(hits)
25
25
  end
26
26
  end
27
27
 
@@ -1,18 +1,21 @@
1
1
  module ElasticRecord
2
2
  class Relation
3
3
  module Hits
4
+ extend ActiveSupport::Concern
5
+
4
6
  def to_ids
5
7
  search_hits.to_ids
6
8
  end
7
9
 
8
10
  def search_hits
9
- SearchHits.from_response(klass, search_results)
11
+ SearchHits.from_response(search_results)
10
12
  end
11
13
 
12
14
  def search_results
13
15
  @search_results ||= begin
14
16
  options = { typed_keys: true }
15
17
  options[:search_type] = search_type_value if search_type_value
18
+ options[:_source] = klass.elastic_index.load_from_source
16
19
 
17
20
  klass.elastic_index.search(as_elastic, options)
18
21
  end
@@ -57,7 +57,7 @@ module ElasticRecord
57
57
  define_method ar_method do |*args, &block|
58
58
  result = klass.send(ar_method, *args, &block)
59
59
  if result.is_a?(ActiveRecord::Relation)
60
- self.class.new(result, values)
60
+ self.class.new(result, values: values)
61
61
  else
62
62
  result
63
63
  end
@@ -1,28 +1,20 @@
1
1
  module ElasticRecord
2
2
  class SearchHits
3
- attr_accessor :hits, :model
3
+ attr_accessor :hits, :model, :total
4
4
 
5
5
  class << self
6
- def from_response(model, response)
7
- new(model, response['hits']['hits'])
6
+ def from_response(response)
7
+ new(response['hits']['hits'], total: response['hits']['total'])
8
8
  end
9
9
  end
10
10
 
11
- def initialize(model, hits)
12
- @model = model
11
+ def initialize(hits, total: nil)
13
12
  @hits = hits
13
+ @total = total.is_a?(Hash) ? total['value'] : total
14
14
  end
15
15
 
16
16
  def to_ids
17
17
  hits.map { |hit| hit['_id'] }
18
18
  end
19
-
20
- def to_records
21
- if model.elastic_index.load_from_source
22
- hits.map { |hit| model.from_search_hit(hit) }
23
- else
24
- model.find to_ids
25
- end
26
- end
27
19
  end
28
20
  end
@@ -34,16 +34,4 @@ class Widget < ActiveRecord::Base
34
34
  }
35
35
  }
36
36
  )
37
-
38
- class << self
39
- def anon(&block)
40
- Class.new(self) do
41
- def self.name
42
- 'Widget'
43
- end
44
-
45
- instance_eval(&block)
46
- end
47
- end
48
- end
49
37
  end
@@ -21,6 +21,11 @@ class ElasticRecord::AsDocumentTest < MiniTest::Test
21
21
  assert_equal 1, Widget.elastic_search.filter(color: 'grey').count
22
22
  assert_equal 1, Widget.elastic_search.filter('widget_part.name' => 'Doohicky').count
23
23
  assert_equal 0, Widget.elastic_search.filter(name: 'elmo').count
24
+
25
+ widget.widget_part = { name: nil }
26
+ widget.save!
27
+
28
+ assert_equal 1, Widget.elastic_search.filter('widget_part.name' => nil).count
24
29
  end
25
30
 
26
31
  class SpecialFieldsModel
@@ -17,14 +17,14 @@ class ElasticRecord::FromSearchHitsTest < MiniTest::Test
17
17
  end
18
18
 
19
19
  def test_ranges
20
- document = Project.elastic_relation.search_hits.to_records.first
20
+ document = Project.elastic_relation.first
21
21
 
22
22
  assert_equal 'foo', document.name
23
23
  assert_equal @project.estimated_start_date, document.estimated_start_date
24
24
  end
25
25
 
26
26
  def test_nested_ranges
27
- document = Project.elastic_relation.search_hits.to_records.first
27
+ document = Project.elastic_relation.first
28
28
  team_members = document.team_members.sort_by { |member| member['name'] }
29
29
 
30
30
  assert_equal 26..29, team_members.first['estimated_age']
@@ -32,7 +32,7 @@ class ElasticRecord::FromSearchHitsTest < MiniTest::Test
32
32
  end
33
33
 
34
34
  def test_object_ranges
35
- document = Project.elastic_relation.search_hits.to_records.first
35
+ document = Project.elastic_relation.first
36
36
 
37
37
  assert_equal 25..30, document.manager['estimated_age']
38
38
  end
@@ -78,57 +78,6 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
78
78
  assert index.record_exists?('joe')
79
79
  end
80
80
 
81
- def test_build_scroll_enumerator
82
- index.index_document('bob', name: 'bob')
83
- index.index_document('joe', name: 'joe')
84
-
85
- scroll_enumerator = index.build_scroll_enumerator(search: {'query' => {query_string: {query: 'name:bob'}}})
86
-
87
- assert_equal 1, scroll_enumerator.total_hits
88
- assert_equal 1, scroll_enumerator.request_more_ids.size
89
- end
90
-
91
- def test_expired_scroll_error
92
- index.index_document('bob', name: 'bob')
93
- index.index_document('bobs', name: 'bob')
94
-
95
- scroll_enumerator = index.build_scroll_enumerator(
96
- search: { 'query' => { query_string: { query: 'name:bob' } } },
97
- batch_size: 1,
98
- keep_alive: '1ms'
99
- )
100
-
101
- scroll_enumerator.request_more_hits
102
- index.delete_scroll(scroll_enumerator.scroll_id)
103
- assert_raises ElasticRecord::ExpiredScrollError do
104
- scroll_enumerator.request_more_hits
105
- end
106
- end
107
-
108
- def test_each_slice
109
- 10.times { |i| index.index_document("bob#{i}", color: 'red') }
110
- batches = []
111
-
112
- scroll_enumerator = index.build_scroll_enumerator(search: {'query' => {query_string: {query: 'color:red'}}}, batch_size: 1)
113
-
114
- scroll_enumerator.each_slice do |slice|
115
- batches << slice
116
- end
117
-
118
- assert_equal 10, batches.size
119
-
120
- # Assert context was removed
121
- assert_raises ElasticRecord::ExpiredScrollError do
122
- scroll_enumerator.request_more_hits
123
- end
124
- end
125
-
126
- def test_invalid_scroll_error
127
- assert_raises ElasticRecord::InvalidScrollError do
128
- index.scroll('invalid', '1m')
129
- end
130
- end
131
-
132
81
  def test_bulk
133
82
  assert_nil index.current_bulk_batch
134
83
 
@@ -3,23 +3,21 @@ require 'helper'
3
3
  class ElasticRecord::Index::MappingTest < MiniTest::Test
4
4
  def test_get_mapping
5
5
  expected = {
6
- "widget" => {
7
- "properties" => {
8
- "color" => { "type" => "keyword" },
9
- "name" => {
10
- "type" => "text",
11
- "fields" => {
12
- "raw" => { "type" => "keyword" }
13
- }
14
- },
15
- "price" => {
16
- "type" => "long"
17
- },
18
- "warehouse_id" => { "type" => "keyword" },
19
- "widget_part" => {
20
- "properties" => {
21
- "name" => { "type" => "keyword" }
22
- }
6
+ "properties" => {
7
+ "color" => { "type" => "keyword" },
8
+ "name" => {
9
+ "type" => "text",
10
+ "fields" => {
11
+ "raw" => { "type" => "keyword" }
12
+ }
13
+ },
14
+ "price" => {
15
+ "type" => "long"
16
+ },
17
+ "warehouse_id" => { "type" => "keyword" },
18
+ "widget_part" => {
19
+ "properties" => {
20
+ "name" => { "type" => "keyword" }
23
21
  }
24
22
  }
25
23
  }
@@ -0,0 +1,60 @@
1
+ require "helper"
2
+
3
+ class ElasticRecord::Index::SearchTest < MiniTest::Test
4
+ def test_build_scroll_enumerator
5
+ index.index_document('bob', name: 'bob')
6
+ index.index_document('joe', name: 'joe')
7
+
8
+ scroll_enumerator = index.build_scroll_enumerator(search: {'query' => {query_string: {query: 'name:bob'}}})
9
+
10
+ assert_equal 1, scroll_enumerator.total_hits
11
+ assert_equal 1, scroll_enumerator.request_more_ids.size
12
+ end
13
+
14
+ def test_expired_scroll_error
15
+ index.index_document('bob', name: 'bob')
16
+ index.index_document('bobs', name: 'bob')
17
+
18
+ scroll_enumerator = index.build_scroll_enumerator(
19
+ search: { 'query' => { query_string: { query: 'name:bob' } } },
20
+ batch_size: 1,
21
+ keep_alive: '1ms'
22
+ )
23
+
24
+ scroll_enumerator.request_more_hits
25
+ index.delete_scroll(scroll_enumerator.scroll_id)
26
+ assert_raises ElasticRecord::ExpiredScrollError do
27
+ scroll_enumerator.request_more_hits
28
+ end
29
+ end
30
+
31
+ def test_each_slice
32
+ 10.times { |i| index.index_document("bob#{i}", color: 'red') }
33
+ batches = []
34
+
35
+ scroll_enumerator = index.build_scroll_enumerator(search: {'query' => {query_string: {query: 'color:red'}}}, batch_size: 1)
36
+
37
+ scroll_enumerator.each_slice do |slice|
38
+ batches << slice
39
+ end
40
+
41
+ assert_equal 10, batches.size
42
+
43
+ # Assert context was removed
44
+ assert_raises ElasticRecord::ExpiredScrollError do
45
+ scroll_enumerator.request_more_hits
46
+ end
47
+ end
48
+
49
+ def test_invalid_scroll_error
50
+ assert_raises ElasticRecord::InvalidScrollError do
51
+ index.scroll('invalid', '1m')
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def index
58
+ @index ||= Widget.elastic_index
59
+ end
60
+ end
@@ -5,22 +5,20 @@ class ElasticRecord::PercolatorModelTest < MiniTest::Test
5
5
  index = WidgetQuery.elastic_index
6
6
 
7
7
  expected_mapping = {
8
- "widget"=> {
9
- "properties"=> {
10
- "color"=> {"type" => "keyword" },
11
- "name"=> {
12
- "type" => "text",
13
- "fields" => {
14
- "raw" => { "type" => "keyword" }
15
- }
16
- },
17
- "price" => { "type" => "long" },
18
- "query" => { "type" => "percolator" },
19
- "warehouse_id" => { "type" => "keyword" },
20
- "widget_part" => {
21
- "properties" => {
22
- "name" => { "type" => "keyword" }
23
- }
8
+ "properties"=> {
9
+ "color"=> {"type" => "keyword" },
10
+ "name"=> {
11
+ "type" => "text",
12
+ "fields" => {
13
+ "raw" => { "type" => "keyword" }
14
+ }
15
+ },
16
+ "price" => { "type" => "long" },
17
+ "query" => { "type" => "percolator" },
18
+ "warehouse_id" => { "type" => "keyword" },
19
+ "widget_part" => {
20
+ "properties" => {
21
+ "name" => { "type" => "keyword" }
24
22
  }
25
23
  }
26
24
  }
@@ -16,14 +16,6 @@ class ElasticRecord::Relation::BatchesTest < MiniTest::Test
16
16
  # assert_equal [@red_widget, @blue_widget, @green_widget].to_set, results.to_set
17
17
  # end
18
18
 
19
- def test_find_hits_in_batches
20
- results = []
21
- Widget.elastic_relation.find_hits_in_batches do |hits|
22
- results << hits
23
- end
24
- assert_equal [[@red_widget, @blue_widget, @green_widget].to_set], results.map(&:to_records).map(&:to_set)
25
- end
26
-
27
19
  def test_find_ids_in_batches
28
20
  results = []
29
21
  Widget.elastic_relation.find_ids_in_batches do |ids|
@@ -1,6 +1,19 @@
1
1
  require 'helper'
2
2
 
3
3
  class ElasticRecord::RelationTest < MiniTest::Test
4
+ class SpecialRelation < ElasticRecord::Relation
5
+ end
6
+
7
+ def test_becomes
8
+ parent_relation = Widget.elastic_relation.filter(color: 'red')
9
+ became_relation = parent_relation.becomes(SpecialRelation)
10
+
11
+ assert_kind_of SpecialRelation, became_relation
12
+ assert_equal Widget, became_relation.klass
13
+ assert_equal parent_relation.values, became_relation.values
14
+ refute_equal parent_relation.values.object_id, became_relation.values.object_id
15
+ end
16
+
4
17
  def test_count
5
18
  original_count = Widget.elastic_relation.count
6
19
  Widget.create(color: 'red')
@@ -14,18 +14,18 @@ class ElasticRecord::SearchingTest < MiniTest::Test
14
14
  assert_equal widget, Widget.es.filter(color: 'red').first
15
15
  end
16
16
 
17
- def test_elastic_scope
18
- model = Widget.anon do
19
- elastic_scope :by_color, ->(color) { elastic_search.filter(color: color) } do
20
- def negative_offset
21
- -offset_value
22
- end
17
+ class ScopedWidget < Widget
18
+ elastic_scope :by_color, ->(color) { elastic_search.filter(color: color) } do
19
+ def negative_offset
20
+ -offset_value
23
21
  end
24
22
  end
23
+ end
25
24
 
26
- relation = model.by_color('blue')
25
+ def test_elastic_scope
26
+ relation = ScopedWidget.by_color('blue')
27
27
 
28
- assert_equal model.elastic_relation.filter(color: 'blue'), relation
28
+ assert_equal ScopedWidget.elastic_relation.filter(color: 'blue'), relation
29
29
  assert_equal -5, relation.offset(5).negative_offset
30
30
  end
31
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Infogroup
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-19 00:00:00.000000000 Z
12
+ date: 2019-08-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: arelastic
@@ -75,6 +75,7 @@ files:
75
75
  - lib/elastic_record/index/manage.rb
76
76
  - lib/elastic_record/index/mapping.rb
77
77
  - lib/elastic_record/index/mapping_type.rb
78
+ - lib/elastic_record/index/search.rb
78
79
  - lib/elastic_record/index/settings.rb
79
80
  - lib/elastic_record/log_subscriber.rb
80
81
  - lib/elastic_record/lucene.rb
@@ -163,6 +164,7 @@ files:
163
164
  - test/elastic_record/index/manage_test.rb
164
165
  - test/elastic_record/index/mapping_test.rb
165
166
  - test/elastic_record/index/mapping_type_test.rb
167
+ - test/elastic_record/index/search_test.rb
166
168
  - test/elastic_record/index/settings_test.rb
167
169
  - test/elastic_record/index_test.rb
168
170
  - test/elastic_record/integration/active_record_test.rb
@@ -201,7 +203,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
203
  - !ruby/object:Gem::Version
202
204
  version: 1.8.11
203
205
  requirements: []
204
- rubygems_version: 3.0.3
206
+ rubyforge_project:
207
+ rubygems_version: 2.7.6
205
208
  signing_key:
206
209
  specification_version: 4
207
210
  summary: An Elasticsearch querying ORM