chewy 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +13 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +75 -0
  5. data/README.md +487 -92
  6. data/Rakefile +3 -2
  7. data/chewy.gemspec +2 -2
  8. data/filters +76 -0
  9. data/lib/chewy.rb +5 -3
  10. data/lib/chewy/config.rb +36 -19
  11. data/lib/chewy/fields/base.rb +5 -1
  12. data/lib/chewy/index.rb +22 -10
  13. data/lib/chewy/index/actions.rb +13 -13
  14. data/lib/chewy/index/search.rb +7 -2
  15. data/lib/chewy/query.rb +382 -64
  16. data/lib/chewy/query/context.rb +174 -0
  17. data/lib/chewy/query/criteria.rb +127 -34
  18. data/lib/chewy/query/loading.rb +9 -9
  19. data/lib/chewy/query/nodes/and.rb +25 -0
  20. data/lib/chewy/query/nodes/base.rb +17 -0
  21. data/lib/chewy/query/nodes/bool.rb +32 -0
  22. data/lib/chewy/query/nodes/equal.rb +34 -0
  23. data/lib/chewy/query/nodes/exists.rb +20 -0
  24. data/lib/chewy/query/nodes/expr.rb +28 -0
  25. data/lib/chewy/query/nodes/field.rb +106 -0
  26. data/lib/chewy/query/nodes/missing.rb +20 -0
  27. data/lib/chewy/query/nodes/not.rb +25 -0
  28. data/lib/chewy/query/nodes/or.rb +25 -0
  29. data/lib/chewy/query/nodes/prefix.rb +18 -0
  30. data/lib/chewy/query/nodes/query.rb +20 -0
  31. data/lib/chewy/query/nodes/range.rb +63 -0
  32. data/lib/chewy/query/nodes/raw.rb +15 -0
  33. data/lib/chewy/query/nodes/regexp.rb +31 -0
  34. data/lib/chewy/query/nodes/script.rb +20 -0
  35. data/lib/chewy/query/pagination.rb +28 -22
  36. data/lib/chewy/railtie.rb +23 -0
  37. data/lib/chewy/rspec/update_index.rb +20 -3
  38. data/lib/chewy/type/adapter/active_record.rb +78 -5
  39. data/lib/chewy/type/adapter/base.rb +46 -0
  40. data/lib/chewy/type/adapter/object.rb +40 -8
  41. data/lib/chewy/type/base.rb +1 -1
  42. data/lib/chewy/type/import.rb +18 -44
  43. data/lib/chewy/type/observe.rb +24 -14
  44. data/lib/chewy/version.rb +1 -1
  45. data/lib/tasks/chewy.rake +27 -0
  46. data/spec/chewy/config_spec.rb +30 -12
  47. data/spec/chewy/fields/base_spec.rb +11 -5
  48. data/spec/chewy/index/actions_spec.rb +20 -20
  49. data/spec/chewy/index/search_spec.rb +5 -5
  50. data/spec/chewy/index_spec.rb +28 -8
  51. data/spec/chewy/query/context_spec.rb +173 -0
  52. data/spec/chewy/query/criteria_spec.rb +219 -12
  53. data/spec/chewy/query/loading_spec.rb +6 -4
  54. data/spec/chewy/query/nodes/and_spec.rb +16 -0
  55. data/spec/chewy/query/nodes/bool_spec.rb +22 -0
  56. data/spec/chewy/query/nodes/equal_spec.rb +32 -0
  57. data/spec/chewy/query/nodes/exists_spec.rb +18 -0
  58. data/spec/chewy/query/nodes/missing_spec.rb +15 -0
  59. data/spec/chewy/query/nodes/not_spec.rb +16 -0
  60. data/spec/chewy/query/nodes/or_spec.rb +16 -0
  61. data/spec/chewy/query/nodes/prefix_spec.rb +16 -0
  62. data/spec/chewy/query/nodes/query_spec.rb +12 -0
  63. data/spec/chewy/query/nodes/range_spec.rb +32 -0
  64. data/spec/chewy/query/nodes/raw_spec.rb +11 -0
  65. data/spec/chewy/query/nodes/regexp_spec.rb +31 -0
  66. data/spec/chewy/query/nodes/script_spec.rb +15 -0
  67. data/spec/chewy/query/pagination_spec.rb +3 -2
  68. data/spec/chewy/query_spec.rb +83 -26
  69. data/spec/chewy/rspec/update_index_spec.rb +20 -0
  70. data/spec/chewy/type/adapter/active_record_spec.rb +102 -0
  71. data/spec/chewy/type/adapter/object_spec.rb +82 -0
  72. data/spec/chewy/type/import_spec.rb +30 -1
  73. data/spec/chewy/type/mapping_spec.rb +1 -1
  74. data/spec/chewy/type/observe_spec.rb +46 -12
  75. data/spec/spec_helper.rb +7 -6
  76. data/spec/support/class_helpers.rb +2 -2
  77. metadata +98 -48
  78. data/.rvmrc +0 -1
  79. data/lib/chewy/index/client.rb +0 -13
  80. data/spec/chewy/index/client_spec.rb +0 -18
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Query::Nodes::Range do
4
+ describe '#__render__' do
5
+ def render &block
6
+ Chewy::Query::Context.new(&block).__render__
7
+ end
8
+
9
+ specify { render { age > nil }.should == {range: {'age' => {gt: nil}}} }
10
+ specify { render { age == (nil..nil) }.should == {range: {'age' => {gt: nil, lt: nil}}} }
11
+
12
+ specify { render { age > 42 }.should == {range: {'age' => {gt: 42}}} }
13
+ specify { render { age == (42..45) }.should == {range: {'age' => {gt: 42, lt: 45}}} }
14
+ specify { render { age == [42..45] }.should == {range: {'age' => {gte: 42, lte: 45}}} }
15
+ specify { render { (age > 42) & (age <= 45) }.should == {range: {'age' => {gt: 42, lte: 45}}} }
16
+
17
+ specify { render { ~age > 42 }.should == {range: {'age' => {gt: 42}, _cache: true}} }
18
+ specify { render { ~age == (42..45) }.should == {range: {'age' => {gt: 42, lt: 45}, _cache: true}} }
19
+ specify { render { ~age == [42..45] }.should == {range: {'age' => {gte: 42, lte: 45}, _cache: true}} }
20
+ specify { render { (age > 42) & ~(age <= 45) }.should == {range: {'age' => {gt: 42, lte: 45}, _cache: true}} }
21
+ specify { render { (~age > 42) & (age <= 45) }.should == {range: {'age' => {gt: 42, lte: 45}, _cache: true}} }
22
+
23
+ specify { render { age(:i) > 42 }.should == {range: {'age' => {gt: 42}, execution: :index}} }
24
+ specify { render { age(:index) > 42 }.should == {range: {'age' => {gt: 42}, execution: :index}} }
25
+ specify { render { age(:f) > 42 }.should == {range: {'age' => {gt: 42}, execution: :fielddata}} }
26
+ specify { render { age(:fielddata) > 42 }.should == {range: {'age' => {gt: 42}, execution: :fielddata}} }
27
+ specify { render { (age(:f) > 42) & (age <= 45) }.should == {range: {'age' => {gt: 42, lte: 45}, execution: :fielddata}} }
28
+
29
+ specify { render { ~age(:f) > 42 }.should == {range: {'age' => {gt: 42}, execution: :fielddata, _cache: true}} }
30
+ specify { render { (age(:f) > 42) & (~age <= 45) }.should == {range: {'age' => {gt: 42, lte: 45}, execution: :fielddata, _cache: true}} }
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Query::Nodes::Raw do
4
+ describe '#__render__' do
5
+ def render &block
6
+ Chewy::Query::Context.new(&block).__render__
7
+ end
8
+
9
+ specify { render { r(term: {name: 'name'}) }.should == {term: {name: 'name'}} }
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Query::Nodes::Regexp do
4
+ describe '#__render__' do
5
+ def render &block
6
+ Chewy::Query::Context.new(&block).__render__
7
+ end
8
+
9
+ specify { render { names.first == /nam.*/ }.should == {regexp: {'names.first' => 'nam.*'}} }
10
+ specify { render { names.first =~ /nam.*/ }.should == {regexp: {'names.first' => 'nam.*'}} }
11
+ specify { render { name != /nam.*/ }.should == {not: {regexp: {'name' => 'nam.*'}}} }
12
+ specify { render { name !~ /nam.*/ }.should == {not: {regexp: {'name' => 'nam.*'}}} }
13
+
14
+ specify { render { names.first(flags: [:anystring, :intersection, :borogoves]) == /nam.*/ }
15
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING|INTERSECTION'}}} }
16
+ specify { render { names.first(:anystring, :intersection, :borogoves) == /nam.*/ }
17
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING|INTERSECTION'}}} }
18
+
19
+ specify { render { names.first(flags: [:anystring, :intersection, :borogoves]) =~ /nam.*/ }
20
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING|INTERSECTION'}}} }
21
+ specify { render { names.first(:anystring, :intersection, :borogoves) =~ /nam.*/ }
22
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING|INTERSECTION'}}} }
23
+
24
+ specify { render { ~names.first == /nam.*/ }.should == {regexp: {'names.first' => 'nam.*', _cache: true, _cache_key: 'nam.*'}} }
25
+ specify { render { names.first(cache: 'name') == /nam.*/ }.should == {regexp: {'names.first' => 'nam.*', _cache: true, _cache_key: 'name'}} }
26
+ specify { render { ~names.first(:anystring) =~ /nam.*/ }
27
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING'}, _cache: true, _cache_key: 'nam.*'}} }
28
+ specify { render { names.first(:anystring, cache: 'name') =~ /nam.*/ }
29
+ .should == {regexp: {'names.first' => {value: 'nam.*', flags: 'ANYSTRING'}, _cache: true, _cache_key: 'name'}} }
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Query::Nodes::Script do
4
+ describe '#__render__' do
5
+ def render &block
6
+ Chewy::Query::Context.new(&block).__render__
7
+ end
8
+
9
+ specify { render { s('var = val') }.should == {script: {script: 'var = val'}} }
10
+ specify { render { s('var = val', val: 42) }.should == {script: {script: 'var = val', params: {val: 42}}} }
11
+
12
+ specify { render { ~s('var = val') }.should == {script: {script: 'var = val', _cache: true}} }
13
+ specify { render { ~s('var = val', val: 42) }.should == {script: {script: 'var = val', params: {val: 42}, _cache: true}} }
14
+ end
15
+ end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Chewy::Query::Pagination do
4
4
  include ClassHelpers
5
+ before { Chewy::Index.client.indices.delete }
5
6
 
6
7
  before do
7
8
  stub_index(:products) do
@@ -12,10 +13,10 @@ describe Chewy::Query::Pagination do
12
13
  end
13
14
  let(:data) { 10.times.map { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
14
15
 
15
- before { ProductsIndex.types['product'].import(data.map { |h| double(h) }) }
16
+ before { ProductsIndex::Product.import(data.map { |h| double(h) }) }
16
17
  before { Kaminari.config.stub(default_per_page: 3) }
17
18
 
18
- let(:search) { ProductsIndex.search.order(:age) }
19
+ let(:search) { ProductsIndex.order(:age) }
19
20
 
20
21
  describe '#per, #page' do
21
22
  specify { search.map { |e| e.attributes.except('_score') }.should == data }
@@ -3,19 +3,39 @@ require 'spec_helper'
3
3
  describe Chewy::Query do
4
4
  include ClassHelpers
5
5
 
6
+ before { Chewy::Index.client.indices.delete }
6
7
  before do
7
8
  stub_index(:products) do
8
9
  define_type :product do
9
10
  field :name, :age
10
11
  end
12
+ define_type :city
13
+ define_type :country
11
14
  end
12
15
  end
13
16
 
14
- subject { described_class.new(ProductsIndex, type: :product) }
17
+ subject { described_class.new(ProductsIndex) }
18
+
19
+ context 'integration' do
20
+ let(:products) { 3.times.map { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
21
+ let(:cities) { 3.times.map { |i| {id: i.next.to_s}.stringify_keys! } }
22
+ let(:countries) { 3.times.map { |i| {id: i.next.to_s}.stringify_keys! } }
23
+ before { ProductsIndex::Product.import(products.map { |h| double(h) }) }
24
+ before { ProductsIndex::City.import(cities.map { |h| double(h) }) }
25
+ before { ProductsIndex::Country.import(countries.map { |h| double(h) }) }
26
+
27
+ specify { subject.count.should == 9 }
28
+ specify { subject.limit(6).count.should == 6 }
29
+ specify { subject.offset(6).count.should == 3 }
30
+ specify { subject.types(:product).count.should == 3 }
31
+ specify { subject.types(:product, :country).count.should == 6 }
32
+ specify { subject.filter(term: {age: 10}).count.should == 1 }
33
+ specify { subject.query(term: {age: 10}).count.should == 1 }
34
+ end
15
35
 
16
36
  describe '#==' do
17
37
  let(:data) { 3.times.map { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
18
- before { ProductsIndex.types['product'].import(data.map { |h| double(h) }) }
38
+ before { ProductsIndex::Product.import(data.map { |h| double(h) }) }
19
39
 
20
40
  specify { subject.query(match: 'hello').should == subject.query(match: 'hello') }
21
41
  specify { subject.query(match: 'hello').should_not == subject.query(match: 'world') }
@@ -24,39 +44,39 @@ describe Chewy::Query do
24
44
  specify { subject.limit(2).should == subject.limit(2).to_a }
25
45
  end
26
46
 
27
- describe '#_request_query' do
28
- specify { subject.send(:_request_query).should == {} }
29
- specify { subject.filter(term: {field: 'hello'}).send(:_request_query)
30
- .should == {query: {filtered: {query: {match_all: {}}, filter: {term: {field: "hello"}}}}} }
31
- specify { subject.filter(term: {field: 'hello'}).filter(term: {field: 'world'}).send(:_request_query)
32
- .should == {query: {filtered: {query: {match_all: {}}, filter: {and: [
33
- {term: {field: "hello"}}, {term: {field: "world"}}
34
- ]}}}} }
35
- specify { subject.query(term: {field: 'hello'}).send(:_request_query)
36
- .should == {query: {term: {field: "hello"}}} }
37
- specify { subject.filter(term: {field: 'hello'}).query(term: {field: 'world'}).send(:_request_query)
38
- .should == {query: {filtered: {query: {term: {field: "world"}}, filter: {term: {field: "hello"}}}}} }
47
+ describe '#query_mode' do
48
+ specify { subject.query_mode(:should).should be_a described_class }
49
+ specify { subject.query_mode(:should).should_not == subject }
50
+ specify { subject.query_mode(:should).criteria.options.should include(query_mode: :should) }
51
+ specify { expect { subject.query_mode(:should) }.not_to change { subject.criteria.options } }
52
+ end
53
+
54
+ describe '#filter_mode' do
55
+ specify { subject.filter_mode(:or).should be_a described_class }
56
+ specify { subject.filter_mode(:or).should_not == subject }
57
+ specify { subject.filter_mode(:or).criteria.options.should include(filter_mode: :or) }
58
+ specify { expect { subject.filter_mode(:or) }.not_to change { subject.criteria.options } }
39
59
  end
40
60
 
41
61
  describe '#limit' do
42
62
  specify { subject.limit(10).should be_a described_class }
43
63
  specify { subject.limit(10).should_not == subject }
44
- specify { subject.limit(10).criteria.search.should include(size: 10) }
45
- specify { expect { subject.limit(10) }.not_to change { subject.criteria.search } }
64
+ specify { subject.limit(10).criteria.options.should include(size: 10) }
65
+ specify { expect { subject.limit(10) }.not_to change { subject.criteria.options } }
46
66
  end
47
67
 
48
68
  describe '#offset' do
49
69
  specify { subject.offset(10).should be_a described_class }
50
70
  specify { subject.offset(10).should_not == subject }
51
- specify { subject.offset(10).criteria.search.should include(from: 10) }
52
- specify { expect { subject.offset(10) }.not_to change { subject.criteria.search } }
71
+ specify { subject.offset(10).criteria.options.should include(from: 10) }
72
+ specify { expect { subject.offset(10) }.not_to change { subject.criteria.options } }
53
73
  end
54
74
 
55
75
  describe '#query' do
56
76
  specify { subject.query(match: 'hello').should be_a described_class }
57
77
  specify { subject.query(match: 'hello').should_not == subject }
58
- specify { subject.query(match: 'hello').criteria.query.should include(match: 'hello') }
59
- specify { expect { subject.query(match: 'hello') }.not_to change { subject.criteria.query } }
78
+ specify { subject.query(match: 'hello').criteria.queries.should include(match: 'hello') }
79
+ specify { expect { subject.query(match: 'hello') }.not_to change { subject.criteria.queries } }
60
80
  end
61
81
 
62
82
  describe '#facets' do
@@ -69,16 +89,17 @@ describe Chewy::Query do
69
89
  describe '#filter' do
70
90
  specify { subject.filter(term: {field: 'hello'}).should be_a described_class }
71
91
  specify { subject.filter(term: {field: 'hello'}).should_not == subject }
72
- specify { subject.filter(term: {field: 'hello'}).criteria.filters.should include(term: {field: 'hello'}) }
73
- specify { subject.filter([{term: {field: 'hello'}}, {term: {field: 'world'}}]).criteria.filters.should include(term: {field: 'hello'}) }
74
- specify { subject.filter([{term: {field: 'hello'}}, {term: {field: 'world'}}]).criteria.filters.should include(term: {field: 'world'}) }
75
92
  specify { expect { subject.filter(term: {field: 'hello'}) }.not_to change { subject.criteria.filters } }
93
+ specify { subject.filter([{term: {field: 'hello'}}, {term: {field: 'world'}}]).criteria.filters
94
+ .should == [{term: {field: 'hello'}}, {term: {field: 'world'}}] }
95
+
96
+ specify { expect { subject.filter{ name == 'John' } }.not_to change { subject.criteria.filters } }
97
+ specify { subject.filter{ name == 'John' }.criteria.filters.should == [{term: {'name' => 'John'}}] }
76
98
  end
77
99
 
78
100
  describe '#order' do
79
101
  specify { subject.order(field: 'hello').should be_a described_class }
80
102
  specify { subject.order(field: 'hello').should_not == subject }
81
- specify { subject.order(field: 'hello').criteria.sort.should include(field: 'hello') }
82
103
  specify { expect { subject.order(field: 'hello') }.not_to change { subject.criteria.sort } }
83
104
 
84
105
  specify { subject.order(:field).criteria.sort.should == [:field] }
@@ -90,7 +111,6 @@ describe Chewy::Query do
90
111
  describe '#reorder' do
91
112
  specify { subject.reorder(field: 'hello').should be_a described_class }
92
113
  specify { subject.reorder(field: 'hello').should_not == subject }
93
- specify { subject.reorder(field: 'hello').criteria.sort.should include(field: 'hello') }
94
114
  specify { expect { subject.reorder(field: 'hello') }.not_to change { subject.criteria.sort } }
95
115
 
96
116
  specify { subject.order(:field1).reorder(:field2).criteria.sort.should == [:field2] }
@@ -101,10 +121,47 @@ describe Chewy::Query do
101
121
  describe '#only' do
102
122
  specify { subject.only(:field).should be_a described_class }
103
123
  specify { subject.only(:field).should_not == subject }
104
- specify { subject.only(:field).criteria.fields.should include('field') }
105
124
  specify { expect { subject.only(:field) }.not_to change { subject.criteria.fields } }
106
125
 
107
126
  specify { subject.only(:field1, :field2).criteria.fields.should =~ ['field1', 'field2'] }
108
127
  specify { subject.only([:field1, :field2]).only(:field3).criteria.fields.should =~ ['field1', 'field2', 'field3'] }
109
128
  end
129
+
130
+ describe '#only!' do
131
+ specify { subject.only!(:field).should be_a described_class }
132
+ specify { subject.only!(:field).should_not == subject }
133
+ specify { expect { subject.only!(:field) }.not_to change { subject.criteria.fields } }
134
+
135
+ specify { subject.only!(:field1, :field2).criteria.fields.should =~ ['field1', 'field2'] }
136
+ specify { subject.only!([:field1, :field2]).only!(:field3).criteria.fields.should =~ ['field3'] }
137
+ specify { subject.only([:field1, :field2]).only!(:field3).criteria.fields.should =~ ['field3'] }
138
+ end
139
+
140
+ describe '#types' do
141
+ specify { subject.types(:product).should be_a described_class }
142
+ specify { subject.types(:product).should_not == subject }
143
+ specify { expect { subject.types(:product) }.not_to change { subject.criteria.types } }
144
+
145
+ specify { subject.types(:user).criteria.types.should == ['user'] }
146
+ specify { subject.types(:product, :city).criteria.types.should =~ ['product', 'city'] }
147
+ specify { subject.types([:product, :city]).types(:country).criteria.types.should =~ ['product', 'city', 'country'] }
148
+ end
149
+
150
+ describe '#types!' do
151
+ specify { subject.types!(:product).should be_a described_class }
152
+ specify { subject.types!(:product).should_not == subject }
153
+ specify { expect { subject.types!(:product) }.not_to change { subject.criteria.types } }
154
+
155
+ specify { subject.types!(:user).criteria.types.should == ['user'] }
156
+ specify { subject.types!(:product, :city).criteria.types.should =~ ['product', 'city'] }
157
+ specify { subject.types!([:product, :city]).types!(:country).criteria.types.should =~ ['country'] }
158
+ specify { subject.types([:product, :city]).types!(:country).criteria.types.should =~ ['country'] }
159
+ end
160
+
161
+ describe '#merge' do
162
+ let(:query) { described_class.new(ProductsIndex) }
163
+
164
+ specify { subject.filter { name == 'name' }.merge(query.filter { age == 42 }).criteria.filters
165
+ .should == [{term: {'name' => 'name'}}, {term: {'age' => 42}}] }
166
+ end
110
167
  end
@@ -29,6 +29,26 @@ describe :update_index do
29
29
  specify { expectation.to fail_matching 'document id `41` (1 times)' }
30
30
  end
31
31
 
32
+ context '#only' do
33
+ specify { expect { DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] }
34
+ .to update_index(DummiesIndex.dummy).and_reindex(41, 42).only }
35
+ specify { expect { expect { DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] }
36
+ .to update_index(DummiesIndex.dummy).and_reindex(41).only }
37
+ .to fail_matching 'to update documents ["41"] only, but ["42"] was updated also' }
38
+ specify { expect { expect { DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] }
39
+ .to update_index(DummiesIndex.dummy).and_reindex(41, times: 2).only }
40
+ .to fail_matching 'to update documents ["41"] only, but ["42"] was updated also' }
41
+
42
+ specify { expect { DummiesIndex.dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] }
43
+ .to update_index(DummiesIndex.dummy).and_delete(41, 42).only }
44
+ specify { expect { expect { DummiesIndex.dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] }
45
+ .to update_index(DummiesIndex.dummy).and_delete(41).only }
46
+ .to fail_matching 'to delete documents ["41"] only, but ["42"] was deleted also' }
47
+ specify { expect { expect { DummiesIndex.dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] }
48
+ .to update_index(DummiesIndex.dummy).and_delete(41, times: 2).only }
49
+ .to fail_matching 'to delete documents ["41"] only, but ["42"] was deleted also' }
50
+ end
51
+
32
52
  context '#and_reindex' do
33
53
  specify { expect { DummiesIndex.dummy.bulk body: [{index: {_id: 42}}] }.to update_index(DummiesIndex.dummy) }
34
54
  specify { expect { DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: {}}}] }
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Type::Adapter::ActiveRecord do
4
+ before { stub_model(:city) }
5
+
6
+ describe '#name' do
7
+ specify { described_class.new(City).name.should == 'City' }
8
+ specify { described_class.new(City.order(:id)).name.should == 'City' }
9
+ specify { described_class.new(City, name: 'town').name.should == 'Town' }
10
+ end
11
+
12
+ describe '#type_name' do
13
+ specify { described_class.new(City).type_name.should == 'city' }
14
+ specify { described_class.new(City.order(:id)).type_name.should == 'city' }
15
+ specify { described_class.new(City, name: 'town').type_name.should == 'town' }
16
+ end
17
+
18
+ describe '#import' do
19
+ def import(*args)
20
+ result = []
21
+ subject.import(*args) { |data| result.push data }
22
+ result
23
+ end
24
+
25
+ specify { subject.import(3.times.map { |i| City.create! }) { |data| true }.should be_true }
26
+ specify { subject.import(3.times.map { |i| City.create! }) { |data| false }.should be_false }
27
+
28
+ context do
29
+ let!(:cities) { 3.times.map { |i| City.create! } }
30
+ let!(:deleted) { 3.times.map { |i| City.create!.tap(&:destroy!) } }
31
+ subject { described_class.new(City) }
32
+
33
+ specify { import.should == [{index: cities}] }
34
+
35
+ specify { import(City.order(:id)).should == [{index: cities}] }
36
+ specify { import(City.order(:id), batch_size: 2)
37
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
38
+
39
+ specify { import(cities).should == [{index: cities}] }
40
+ specify { import(cities, batch_size: 2)
41
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
42
+ specify { import(cities, deleted).should == [{index: cities, delete: deleted}] }
43
+ specify { import(cities, deleted, batch_size: 2).should == [
44
+ {index: cities.first(2)},
45
+ {index: cities.last(1), delete: deleted.first(1)},
46
+ {delete: deleted.last(2)}] }
47
+
48
+ specify { import(cities.map(&:id)).should == [{index: cities}] }
49
+ specify { import(cities.map(&:id), batch_size: 2)
50
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
51
+ specify { import(cities.map(&:id), deleted.map(&:id))
52
+ .should == [{index: cities}, {delete: deleted.map(&:id)}] }
53
+ specify { import(cities.map(&:id), deleted.map(&:id), batch_size: 2).should == [
54
+ {index: cities.first(2)},
55
+ {index: cities.last(1)},
56
+ {delete: deleted.first(2).map(&:id)},
57
+ {delete: deleted.last(1).map(&:id)}] }
58
+ end
59
+
60
+ context do
61
+ let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) } }
62
+ let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy!) } }
63
+ subject { described_class.new(City.where(country_id: 0)) }
64
+
65
+ specify { import.should == [{index: cities.first(2)}] }
66
+
67
+ specify { import(City.order(:id)).should == [{index: cities.first(2)}] }
68
+ specify { import(City.order(:id), batch_size: 1)
69
+ .should == [{index: [cities.first]}, {index: [cities.second]}] }
70
+
71
+ specify { import(cities).should == [{index: cities}] }
72
+ specify { import(cities, batch_size: 2)
73
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
74
+
75
+ specify { import(cities.map(&:id))
76
+ .should == [{index: cities.first(2)}, {delete: [cities.last.id]}] }
77
+ specify { import(cities.map(&:id), batch_size: 1)
78
+ .should == [{index: [cities.first]}, {index: [cities.second]}, {delete: [cities.last.id]}] }
79
+ specify { import(cities.map(&:id), deleted.map(&:id))
80
+ .should == [{index: cities.first(2)}, {delete: [cities.last.id] + deleted.map(&:id)}] }
81
+ specify { import(cities.map(&:id), deleted.map(&:id), batch_size: 2).should == [
82
+ {index: cities.first(2)},
83
+ {delete: [cities.last.id] + deleted.first(1).map(&:id)},
84
+ {delete: deleted.last(1).map(&:id)}] }
85
+ end
86
+ end
87
+
88
+ describe '#load' do
89
+ let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) } }
90
+ let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy!) } }
91
+ subject { described_class.new(City) }
92
+
93
+ specify { subject.load(cities.map { |c| double(id: c.id) }).should == cities }
94
+ specify { subject.load(cities.map { |c| double(id: c.id) }.reverse).should == cities.reverse }
95
+ specify { subject.load(deleted.map { |c| double(id: c.id) }).should == [nil, nil] }
96
+ specify { subject.load((cities + deleted).map { |c| double(id: c.id) }).should == [*cities, nil, nil] }
97
+ specify { subject.load(cities.map { |c| double(id: c.id) }, scope: ->(_) { where(country_id: 0) })
98
+ .should == cities.first(2) + [nil] }
99
+ specify { subject.load(cities.map { |c| double(id: c.id) }, scope: City.where(country_id: 1))
100
+ .should == [nil, nil] + cities.last(1) }
101
+ end
102
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Type::Adapter::Object do
4
+ before { stub_class(:product) }
5
+
6
+ describe '#name' do
7
+ specify { described_class.new('product').name.should == 'Product' }
8
+ specify { described_class.new(:products).name.should == 'Products' }
9
+ specify { described_class.new(Product).name.should == 'Product' }
10
+ specify { described_class.new(Product, name: 'house').name.should == 'House' }
11
+ end
12
+
13
+ describe '#type_name' do
14
+ specify { described_class.new('product').type_name.should == 'product' }
15
+ specify { described_class.new(:products).type_name.should == 'products' }
16
+ specify { described_class.new(Product).type_name.should == 'product' }
17
+ specify { described_class.new(Product, name: 'house').type_name.should == 'house' }
18
+ end
19
+
20
+ describe '#import' do
21
+ def import(*args)
22
+ result = []
23
+ subject.import(*args) { |data| result.push data }
24
+ result
25
+ end
26
+
27
+ specify { subject.import(3.times.map { |i| double }) { |data| true }.should be_true }
28
+ specify { subject.import(3.times.map { |i| double }) { |data| false }.should be_false }
29
+
30
+ context do
31
+ let(:objects) { 3.times.map { |i| double } }
32
+ let(:deleted) { 2.times.map { |i| double(destroyed?: true) } }
33
+ subject { described_class.new('product') }
34
+
35
+ specify { import.should == [] }
36
+ specify { import(objects).should == [{index: objects}] }
37
+ specify { import(objects, batch_size: 2)
38
+ .should == [{index: objects.first(2)}, {index: objects.last(1)}] }
39
+ specify { import(objects, deleted).should == [{index: objects, delete: deleted}] }
40
+ specify { import(objects, deleted, batch_size: 2).should == [
41
+ {index: objects.first(2)},
42
+ {index: objects.last(1), delete: deleted.first(1)},
43
+ {delete: deleted.last(1)}] }
44
+ end
45
+
46
+ context do
47
+ let(:products) { 3.times.map { |i| double.tap { |product|
48
+ product.stub(:is_a?).with(Product).and_return(true)
49
+ } } }
50
+ let(:non_product) { double }
51
+ subject { described_class.new(Product) }
52
+
53
+ specify { import(products).should == [{index: products}] }
54
+ specify { expect { import(products, non_product) {} }.to raise_error }
55
+ end
56
+ end
57
+
58
+ describe '#load' do
59
+ context do
60
+ subject { described_class.new('product') }
61
+ let(:objects) { 3.times.map { |i| double } }
62
+
63
+ specify { subject.load(objects).should == objects }
64
+ end
65
+
66
+ context do
67
+ before { Product.stub(:wrap) { |object| object.stub(wrapped?: true); object } }
68
+ subject { described_class.new(Product) }
69
+ let(:objects) { 3.times.map { |i| double(wrapped?: false) } }
70
+
71
+ specify { subject.load(objects).should satisfy { |objects| objects.all?(&:wrapped?) } }
72
+ end
73
+
74
+ context do
75
+ before { Product.stub(:wrap) { |object| nil } }
76
+ subject { described_class.new(Product) }
77
+ let(:objects) { 3.times.map { |i| double(wrapped?: false) } }
78
+
79
+ specify { subject.load(objects).should satisfy { |objects| objects.all?(&:nil?) } }
80
+ end
81
+ end
82
+ end