elastic_record 5.5.0 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
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