elastic_record 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Rakefile +2 -2
  4. data/elastic_record.gemspec +2 -2
  5. data/lib/elastic_record.rb +1 -0
  6. data/lib/elastic_record/callbacks.rb +13 -5
  7. data/lib/elastic_record/connection.rb +7 -8
  8. data/lib/elastic_record/errors.rb +10 -0
  9. data/lib/elastic_record/index/deferred.rb +7 -1
  10. data/lib/elastic_record/index/documents.rb +34 -17
  11. data/lib/elastic_record/index/manage.rb +8 -0
  12. data/lib/elastic_record/index/percolator.rb +16 -10
  13. data/lib/elastic_record/integration/active_record.rb +7 -0
  14. data/lib/elastic_record/integration/cassandra_object.rb +7 -0
  15. data/lib/elastic_record/model.rb +5 -10
  16. data/lib/elastic_record/railtie.rb +10 -0
  17. data/lib/elastic_record/relation.rb +26 -5
  18. data/lib/elastic_record/relation/batches.rb +4 -1
  19. data/lib/elastic_record/relation/finder_methods.rb +13 -3
  20. data/lib/elastic_record/relation/search_methods.rb +17 -0
  21. data/lib/elastic_record/relation/value_methods.rb +2 -2
  22. data/lib/elastic_record/searches_many.rb +1 -1
  23. data/lib/elastic_record/searches_many/association.rb +14 -10
  24. data/lib/elastic_record/searches_many/builder.rb +3 -3
  25. data/lib/elastic_record/searches_many/collection_proxy.rb +9 -3
  26. data/lib/elastic_record/searches_many/reflection.rb +4 -0
  27. data/test/elastic_record/callbacks_test.rb +65 -1
  28. data/test/elastic_record/config_test.rb +3 -3
  29. data/test/elastic_record/connection_test.rb +1 -1
  30. data/test/elastic_record/index/configurator_test.rb +1 -1
  31. data/test/elastic_record/index/documents_test.rb +23 -4
  32. data/test/elastic_record/index/manage_test.rb +1 -1
  33. data/test/elastic_record/index/mapping_test.rb +1 -1
  34. data/test/elastic_record/index/percolator_test.rb +1 -1
  35. data/test/elastic_record/index/settings_test.rb +1 -1
  36. data/test/elastic_record/index/warmer_test.rb +1 -1
  37. data/test/elastic_record/index_test.rb +1 -1
  38. data/test/elastic_record/integration/active_record_test.rb +39 -0
  39. data/test/elastic_record/log_subscriber_test.rb +11 -0
  40. data/test/elastic_record/lucene_test.rb +1 -1
  41. data/test/elastic_record/model_test.rb +1 -1
  42. data/test/elastic_record/railties/controller_runtime_test.rb +1 -1
  43. data/test/elastic_record/relation/admin_test.rb +13 -7
  44. data/test/elastic_record/relation/batches_test.rb +27 -1
  45. data/test/elastic_record/relation/delegation_test.rb +1 -1
  46. data/test/elastic_record/relation/finder_methods_test.rb +23 -8
  47. data/test/elastic_record/relation/merging_test.rb +1 -1
  48. data/test/elastic_record/relation/none_test.rb +1 -1
  49. data/test/elastic_record/relation/search_methods_test.rb +15 -2
  50. data/test/elastic_record/relation_test.rb +50 -3
  51. data/test/elastic_record/searches_many/association_test.rb +47 -0
  52. data/test/elastic_record/searches_many/autosave_test.rb +11 -10
  53. data/test/elastic_record/searches_many/collection_proxy_test.rb +1 -1
  54. data/test/elastic_record/searches_many/reflection_test.rb +7 -1
  55. data/test/elastic_record/searches_many_test.rb +11 -11
  56. data/test/elastic_record/searching_test.rb +1 -1
  57. data/test/helper.rb +19 -12
  58. data/test/support/models/option.rb +24 -0
  59. data/test/support/models/test_model.rb +22 -1
  60. data/test/support/models/warehouse.rb +2 -2
  61. data/test/support/models/widget.rb +4 -2
  62. data/test/support/query_counter.rb +56 -0
  63. metadata +10 -5
  64. data/lib/elastic_record/orm/active_record.rb +0 -7
@@ -25,9 +25,12 @@ module ElasticRecord
25
25
  search_type: 'scan'
26
26
  }.update(options)
27
27
 
28
- scroll_id = klass.elastic_index.search(as_elastic, options)['_scroll_id']
28
+ search_result = klass.elastic_index.search(as_elastic, options)
29
+ scroll_id = search_result['_scroll_id']
30
+ hit_count = 0
29
31
 
30
32
  while (hit_ids = get_scroll_hit_ids(scroll_id, scroll_keep_alive)).any?
33
+ hit_count += hit_ids.size
31
34
  hit_ids.each_slice(size, &block)
32
35
  end
33
36
  end
@@ -1,14 +1,24 @@
1
1
  module ElasticRecord
2
2
  class Relation
3
3
  module FinderMethods
4
- def find(id)
5
- filter(arelastic.filter.ids(id)).to_a.first || (raise ActiveRecord::RecordNotFound)
4
+ def find(*ids)
5
+ ids = ids.flatten
6
+ case ids.size
7
+ when 0; raise ActiveRecord::RecordNotFound.new('empty argument')
8
+ when 1; filter(arelastic.filter.ids(ids)).first!
9
+ else
10
+ filter(arelastic.filter.ids(ids))
11
+ end
6
12
  end
7
13
 
8
14
  def first
9
15
  find_one self
10
16
  end
11
17
 
18
+ def first!
19
+ first or raise ActiveRecord::RecordNotFound
20
+ end
21
+
12
22
  def last
13
23
  find_one reverse_order
14
24
  end
@@ -24,4 +34,4 @@ module ElasticRecord
24
34
  end
25
35
  end
26
36
  end
27
- end
37
+ end
@@ -39,6 +39,23 @@ module ElasticRecord
39
39
  clone.filter!(*args)
40
40
  end
41
41
 
42
+ def find_by(*args)
43
+ filter(*args).first
44
+ end
45
+
46
+ def find_by!(*args)
47
+ filter(*args).first!
48
+ end
49
+
50
+ def eager_load!(*args)
51
+ self.eager_load_values += args.flatten
52
+ self
53
+ end
54
+
55
+ def eager_load(*args)
56
+ clone.eager_load! *args
57
+ end
58
+
42
59
  def limit!(value)
43
60
  self.limit_value = value
44
61
  self
@@ -1,6 +1,6 @@
1
1
  module ElasticRecord
2
2
  class Relation
3
- MULTI_VALUE_METHODS = [:extending, :facet, :filter, :order, :select]
3
+ MULTI_VALUE_METHODS = [:extending, :facet, :filter, :order, :select, :eager_load]
4
4
  SINGLE_VALUE_METHODS = [:query, :limit, :offset, :reverse_order]
5
5
  end
6
- end
6
+ end
@@ -97,4 +97,4 @@ module ElasticRecord
97
97
  searches_many_cache[name.to_sym] = association
98
98
  end
99
99
  end
100
- end
100
+ end
@@ -31,7 +31,7 @@ module ElasticRecord
31
31
  owner.send("#{reflection.touch_column}=", Time.current)
32
32
  end
33
33
  end
34
-
34
+
35
35
  def reader
36
36
  CollectionProxy.new(self)
37
37
  end
@@ -69,7 +69,7 @@ module ElasticRecord
69
69
  end
70
70
 
71
71
  def scope
72
- search = klass.elastic_search.filter("#{reflection.belongs_to}_id" => owner.id).limit(1000)
72
+ search = klass.elastic_search.filter("#{reflection.belongs_to}_id" => owner.id).limit(1000000)
73
73
  if options[:as]
74
74
  search.filter! "#{reflection.belongs_to}_type" => owner.class.name
75
75
  end
@@ -85,19 +85,23 @@ module ElasticRecord
85
85
  collection
86
86
  end
87
87
 
88
+ def eager_loaded_collection(records)
89
+ unless @loaded
90
+ @collection = records
91
+ @loaded = true
92
+ end
93
+
94
+ collection
95
+ end
96
+
88
97
  private
98
+
89
99
  def load_persisted_collection?
90
100
  !loaded? || owner.new_record?
91
101
  end
92
102
 
93
103
  def persisted_collection
94
- @persisted_collection ||= begin
95
- if reflection.counter_cache_column && (owner.send(reflection.counter_cache_column).nil? || owner.send(reflection.counter_cache_column) == 0)
96
- []
97
- else
98
- scope.to_a
99
- end
100
- end
104
+ @persisted_collection ||= scope.to_a
101
105
  end
102
106
 
103
107
  def merge_collections(existing_records, new_records)
@@ -142,4 +146,4 @@ module ElasticRecord
142
146
  end
143
147
  end
144
148
  end
145
- end
149
+ end
@@ -10,14 +10,14 @@ module ElasticRecord
10
10
  @model, @name, @options = model, name, options
11
11
  end
12
12
 
13
- def build
13
+ def build
14
14
  define_writer
15
15
  define_reader
16
16
 
17
17
  reflection = ElasticRecord::SearchesMany::Reflection.new(model, name, options)
18
18
  model.searches_many_reflections = model.searches_many_reflections.merge(name => reflection)
19
19
 
20
- model.add_autosave_callbacks(reflection) # if options[:autosave]
20
+ model.add_autosave_callbacks(reflection) if options[:autosave]
21
21
  end
22
22
 
23
23
  def mixin
@@ -39,4 +39,4 @@ module ElasticRecord
39
39
  end
40
40
  end
41
41
  end
42
- end
42
+ end
@@ -3,12 +3,18 @@ module ElasticRecord
3
3
  class CollectionProxy < ElasticRecord::Relation
4
4
  def initialize(association)
5
5
  @association = association
6
- super association.klass, association.klass.arelastic
6
+ super association.klass
7
7
  merge! association.scope
8
8
  end
9
9
 
10
+ def eager_loaded(records)
11
+ @association.eager_loaded_collection(records)
12
+ end
13
+
10
14
  def to_a
11
- @association.load_collection.reject(&:destroyed?)
15
+ records = @association.load_collection.reject(&:destroyed?)
16
+ records = eager_load_associations(records) if eager_loading?
17
+ records
12
18
  end
13
19
 
14
20
  def <<(*records)
@@ -17,4 +23,4 @@ module ElasticRecord
17
23
  alias_method :push, :<<
18
24
  end
19
25
  end
20
- end
26
+ end
@@ -20,6 +20,10 @@ module ElasticRecord
20
20
  options[:as] ? options[:as].to_s : model.name.to_s.demodulize.underscore
21
21
  end
22
22
 
23
+ def foreign_key
24
+ options[:foreign_key] ? options[:foreign_key].to_s : "#{model.name.to_s.demodulize.underscore}_id"
25
+ end
26
+
23
27
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
24
28
  def define_callbacks(options)
25
29
  Hash[CALLBACKS.map { |callback_name| [callback_name, Array(options[callback_name.to_sym])] }]
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::CallbacksTest < MiniTest::Spec
3
+ class ElasticRecord::CallbacksTest < MiniTest::Unit::TestCase
4
4
  def test_added_to_index
5
5
  widget = Widget.new id: '10', color: 'green'
6
6
  refute Widget.elastic_index.record_exists?(widget.id)
@@ -43,4 +43,68 @@ class ElasticRecord::CallbacksTest < MiniTest::Spec
43
43
  assert_equal({color: false}, widget.as_search)
44
44
  end
45
45
  end
46
+
47
+ class SpecialFieldsModel
48
+ include TestModel
49
+
50
+ class Author
51
+ def as_search
52
+ {name: 'Jonny'}
53
+ end
54
+ end
55
+
56
+ self.elastic_index.mapping[:properties].update(
57
+ author: {
58
+ type: :object
59
+ },
60
+ commenters: {
61
+ type: :nested
62
+ }
63
+ )
64
+
65
+ def author
66
+ Author.new
67
+ end
68
+
69
+ def commenters
70
+ [Author.new, Author.new]
71
+ end
72
+ end
73
+
74
+ def test_as_search_with_special_fields
75
+ doc = SpecialFieldsModel.new.as_search
76
+
77
+ assert_equal({name: 'Jonny'}, doc[:author])
78
+ assert_equal([{name: 'Jonny'}, {name: 'Jonny'}], doc[:commenters])
79
+ end
80
+
81
+ class DisablingModel
82
+ include TestModel
83
+
84
+ attr_accessor :called_as_search
85
+
86
+ define_attributes [:height]
87
+
88
+ self.elastic_index.mapping[:properties].update(
89
+ height: {
90
+ type: 'string', index: 'not_analyzed'
91
+ }
92
+ )
93
+
94
+ def as_search
95
+ @called_as_search = true
96
+ super
97
+ end
98
+
99
+ end
100
+
101
+ def test_disabled_skip_document
102
+ DisablingModel.elastic_index.disable!
103
+
104
+ model = DisablingModel.new id: '5', height: '9 feets'
105
+ model.save
106
+
107
+ refute Widget.elastic_index.record_exists?(model.id)
108
+ refute model.called_as_search
109
+ end
46
110
  end
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::ConfigTest < MiniTest::Spec
3
+ class ElasticRecord::ConfigTest < MiniTest::Unit::TestCase
4
4
  def test_defaults
5
5
  assert_equal '5m', ElasticRecord::Config.scroll_keep_alive
6
6
  end
@@ -8,6 +8,6 @@ class ElasticRecord::ConfigTest < MiniTest::Spec
8
8
  def test_models
9
9
  ElasticRecord::Config.model_names = %w(Widget)
10
10
 
11
- assert_equal [Warehouse, Widget], ElasticRecord::Config.models
11
+ assert_equal [Warehouse, Widget, Option], ElasticRecord::Config.models
12
12
  end
13
- end
13
+ end
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::ConnectionTest < MiniTest::Spec
3
+ class ElasticRecord::ConnectionTest < MiniTest::Unit::TestCase
4
4
  def test_servers
5
5
  assert_equal ['foo', 'bar'], ElasticRecord::Connection.new('foo,bar').servers
6
6
  assert_equal ['foo', 'bar'], ElasticRecord::Connection.new(['foo', 'bar']).servers
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::ConfiguratorTest < MiniTest::Spec
3
+ class ElasticRecord::Index::ConfiguratorTest < MiniTest::Unit::TestCase
4
4
  def test_property
5
5
  configurator.property :am_i_cool, type: "boolean"
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
3
+ class ElasticRecord::Index::DocumentsTest < MiniTest::Unit::TestCase
4
4
  class InheritedWidget < Widget
5
5
  def self.base_class
6
6
  Widget
@@ -13,6 +13,15 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
13
13
  index.reset
14
14
  end
15
15
 
16
+ def test_index_record
17
+ record = Widget.new(id: '5', color: 'red')
18
+
19
+ index.index_record(record)
20
+
21
+ assert index.record_exists?('5')
22
+ refute index.record_exists?('7')
23
+ end
24
+
16
25
  def test_index_document
17
26
  index.index_document('abc', color: 'red')
18
27
 
@@ -59,10 +68,20 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
59
68
  {color: "green"},
60
69
  {delete: {_index: "widgets", _type: "widget", _id: "3"}}
61
70
  ]
62
- assert_equal expected, index.instance_variable_get(:@_batch)
71
+ assert_equal expected, index.current_bulk_batch
63
72
  end
64
73
 
65
- assert_nil index.instance_variable_get(:@_batch)
74
+ assert_nil index.current_bulk_batch
75
+ end
76
+
77
+ def test_bulk_error
78
+ index.bulk do
79
+ index.index_document '5', color: 'green'
80
+ index.index_document '3', color: {'bad' => 'stuff'}
81
+ end
82
+ assert false, 'Expected ElasticRecord::BulkError'
83
+ rescue => e
84
+ assert_match '[{"index"', e.message
66
85
  end
67
86
 
68
87
  def test_bulk_inheritence
@@ -73,7 +92,7 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
73
92
  {index: {_index: "widgets", _type: "widget", _id: "5"}},
74
93
  {color: "green"}
75
94
  ]
76
- assert_equal expected, index.instance_variable_get(:@_batch)
95
+ assert_equal expected, index.current_bulk_batch
77
96
  end
78
97
  end
79
98
 
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::ManageTest < MiniTest::Spec
3
+ class ElasticRecord::Index::ManageTest < MiniTest::Unit::TestCase
4
4
  class Felon
5
5
  include TestModel
6
6
  end
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::MappingTest < MiniTest::Spec
3
+ class ElasticRecord::Index::MappingTest < MiniTest::Unit::TestCase
4
4
  def test_delete_mapping
5
5
  index_name = index.create
6
6
  index.get_mapping(index_name)
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::PercolatorTest < MiniTest::Spec
3
+ class ElasticRecord::Index::PercolatorTest < MiniTest::Unit::TestCase
4
4
  def test_create_percolator
5
5
  index.disable_deferring!
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::SettingsTest < MiniTest::Spec
3
+ class ElasticRecord::Index::SettingsTest < MiniTest::Unit::TestCase
4
4
  def test_default_settings
5
5
  expected = {}
6
6
  assert_equal expected, ElasticRecord::Index.new(Widget).settings
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::Index::WarmerTest < MiniTest::Spec
3
+ class ElasticRecord::Index::WarmerTest < MiniTest::Unit::TestCase
4
4
  def test_create_warmer
5
5
  index.delete_warmer('green') if index.warmer_exists?('green')
6
6
  refute index.warmer_exists?('green')
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- class ElasticRecord::IndexTest < MiniTest::Spec
3
+ class ElasticRecord::IndexTest < MiniTest::Unit::TestCase
4
4
  def test_copy
5
5
  copied = index.dup
6
6
 
@@ -0,0 +1,39 @@
1
+ require 'helper'
2
+
3
+ require 'active_record'
4
+ require 'mysql2'
5
+
6
+ ActiveSupport.on_load :active_record do
7
+ ActiveRecord::Base.establish_connection(
8
+ adapter: 'mysql2',
9
+ host: "localhost",
10
+ database: "elastic_record_test",
11
+ username: "root"
12
+ )
13
+
14
+ `mysqladmin -u root -f drop elastic_record_test`
15
+ `mysqladmin -u root create elastic_record_test`
16
+
17
+ ActiveRecord::Migration.create_table :projects do |t|
18
+ t.string :name, null: false
19
+ end
20
+ end
21
+
22
+ require 'elastic_record/integration/active_record'
23
+ class Project < ActiveRecord::Base
24
+ include ElasticRecord::Model
25
+
26
+ self.elastic_index.mapping[:properties].update(
27
+ name: { type: 'string', index: 'not_analyzed' }
28
+ )
29
+ end
30
+
31
+ class ElasticRecord::ActiveRecordTest < MiniTest::Unit::TestCase
32
+ def test_load_elastic_record_hits
33
+ poo_product = Project.create! name: "Poo"
34
+ bear_product = Project.create! name: "Bear"
35
+
36
+ assert_equal [poo_product, bear_product], Project.load_elastic_record_hits([poo_product.id, bear_product.id])
37
+ assert_equal [bear_product, poo_product], Project.load_elastic_record_hits([bear_product.id, poo_product.id])
38
+ end
39
+ end