mongoid-scroll 1.0.1 → 2.0.1
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 +4 -4
- data/.github/workflows/ci.yml +26 -7
- data/.github/workflows/danger-comment.yml +9 -0
- data/.github/workflows/danger.yml +9 -15
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +153 -35
- data/CHANGELOG.md +22 -5
- data/Dangerfile +5 -1
- data/Gemfile +16 -8
- data/README.md +27 -38
- data/RELEASING.md +31 -50
- data/Rakefile +3 -3
- data/UPGRADING.md +21 -1
- data/examples/{mongoid_scroll_feed.rb → feed.rb} +4 -3
- data/lib/config/locales/en.yml +4 -1
- data/lib/mongoid/criteria/scrollable/fields.rb +6 -0
- data/lib/mongoid/criteria/scrollable/iterator.rb +19 -0
- data/lib/mongoid/criteria/scrollable.rb +29 -9
- data/lib/mongoid/scroll/base64_encoded_cursor.rb +8 -6
- data/lib/mongoid/scroll/base_cursor.rb +12 -11
- data/lib/mongoid/scroll/cursor.rb +5 -5
- data/lib/mongoid/scroll/errors/base.rb +4 -4
- data/lib/mongoid/scroll/errors/mismatched_sort_fields_error.rb +1 -3
- data/lib/mongoid/scroll/errors/multiple_sort_fields_error.rb +1 -3
- data/lib/mongoid/scroll/errors/unsupported_type_error.rb +11 -0
- data/lib/mongoid/scroll/errors.rb +1 -0
- data/lib/mongoid/scroll/version.rb +1 -1
- data/lib/mongoid-scroll.rb +1 -2
- data/mongoid-scroll.gemspec +3 -3
- data/spec/mongoid/base64_encoded_cursor_spec.rb +148 -77
- data/spec/mongoid/criteria_spec.rb +139 -37
- data/spec/mongoid/cursor_spec.rb +112 -63
- data/spec/mongoid/scroll_spec.rb +1 -1
- data/spec/spec_helper.rb +14 -3
- data/spec/support/feed/embedded_item.rb +2 -0
- data/spec/support/feed/item.rb +1 -1
- metadata +13 -26
- data/examples/mongo_ruby_driver_scroll_feed.rb +0 -45
- data/lib/mongo/scrollable.rb +0 -39
- data/spec/mongo/collection_view_spec.rb +0 -143
|
@@ -7,19 +7,23 @@ describe Mongoid::Criteria do
|
|
|
7
7
|
subject do
|
|
8
8
|
Feed::Item.desc(:name).asc(:value)
|
|
9
9
|
end
|
|
10
|
+
|
|
10
11
|
it ':scroll' do
|
|
11
12
|
expect(subject).to respond_to(:scroll)
|
|
12
13
|
end
|
|
14
|
+
|
|
13
15
|
it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
|
|
14
16
|
expect do
|
|
15
17
|
subject.scroll(cursor_type)
|
|
16
18
|
end.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError, /You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
|
|
17
19
|
end
|
|
18
20
|
end
|
|
21
|
+
|
|
19
22
|
context 'with different sort fields between the cursor and the criteria' do
|
|
20
23
|
subject do
|
|
21
24
|
Feed::Item.desc(:name)
|
|
22
25
|
end
|
|
26
|
+
|
|
23
27
|
it 'raises Mongoid::Scroll::Errors::MismatchedSortFieldsError' do
|
|
24
28
|
record = Feed::Item.create!
|
|
25
29
|
cursor = cursor_type.from_record(record, field: record.fields['a_string'])
|
|
@@ -27,16 +31,50 @@ describe Mongoid::Criteria do
|
|
|
27
31
|
expect { subject.scroll(cursor) }.to raise_error Mongoid::Scroll::Errors::MismatchedSortFieldsError, error_string
|
|
28
32
|
end
|
|
29
33
|
end
|
|
34
|
+
|
|
30
35
|
context 'with no sort' do
|
|
31
36
|
subject do
|
|
32
37
|
Feed::Item.all
|
|
33
38
|
end
|
|
39
|
+
|
|
34
40
|
it 'adds a default sort by _id' do
|
|
35
41
|
expect(subject.scroll(cursor_type).options[:sort]).to eq('_id' => 1)
|
|
36
42
|
end
|
|
37
43
|
end
|
|
44
|
+
|
|
45
|
+
context 'with a missing field' do
|
|
46
|
+
it 'rejects a sort field that is not defined on the model' do
|
|
47
|
+
expect do
|
|
48
|
+
Feed::Item.asc(:removed_field).scroll(cursor_type)
|
|
49
|
+
end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'rejects a cursor whose field is no longer defined on the model' do
|
|
53
|
+
cursor = cursor_type.new(nil, field_name: 'removed_field', field_type: String)
|
|
54
|
+
expect do
|
|
55
|
+
Feed::Item.asc(:name).scroll(cursor)
|
|
56
|
+
end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'does not confuse a nil field value with a missing field' do
|
|
60
|
+
record = Feed::Item.create!
|
|
61
|
+
cursor = cursor_type.from_record(record, field: record.fields['name'])
|
|
62
|
+
expect { Feed::Item.asc(:name).scroll(cursor) }.not_to raise_error
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if cursor_type == Mongoid::Scroll::Base64EncodedCursor
|
|
66
|
+
it 'rejects a serialized cursor whose field is no longer defined on the model' do
|
|
67
|
+
cursor = cursor_type.new(nil, field_name: 'removed_field', field_type: String)
|
|
68
|
+
saved_cursor = cursor_type.new(cursor.to_s)
|
|
69
|
+
expect do
|
|
70
|
+
Feed::Item.asc(:name).scroll(saved_cursor)
|
|
71
|
+
end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
38
76
|
context 'with data' do
|
|
39
|
-
before
|
|
77
|
+
before do
|
|
40
78
|
10.times do |i|
|
|
41
79
|
Feed::Item.create!(
|
|
42
80
|
name: i.to_s,
|
|
@@ -48,30 +86,33 @@ describe Mongoid::Criteria do
|
|
|
48
86
|
)
|
|
49
87
|
end
|
|
50
88
|
end
|
|
89
|
+
|
|
51
90
|
context 'default' do
|
|
52
91
|
it 'scrolls all' do
|
|
53
92
|
records = []
|
|
54
|
-
Feed::Item.all.scroll(cursor_type) do |record,
|
|
93
|
+
Feed::Item.all.scroll(cursor_type) do |record, _iterator|
|
|
55
94
|
records << record
|
|
56
95
|
end
|
|
57
96
|
expect(records.size).to eq 10
|
|
58
97
|
expect(records).to eq Feed::Item.all.to_a
|
|
59
98
|
end
|
|
99
|
+
|
|
60
100
|
it 'does not change original criteria' do
|
|
61
101
|
criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
|
|
62
102
|
original_criteria = criteria.dup
|
|
63
103
|
criteria.limit(2).scroll(cursor_type)
|
|
64
104
|
expect(criteria).to eq original_criteria
|
|
65
105
|
cursor = nil
|
|
66
|
-
criteria.limit(2).scroll(cursor) do |_record,
|
|
67
|
-
cursor = next_cursor
|
|
106
|
+
criteria.limit(2).scroll(cursor) do |_record, iterator|
|
|
107
|
+
cursor = iterator.next_cursor
|
|
68
108
|
end
|
|
69
|
-
criteria.scroll(cursor) do |_record,
|
|
70
|
-
cursor = next_cursor
|
|
109
|
+
criteria.scroll(cursor) do |_record, iterator|
|
|
110
|
+
cursor = iterator.next_cursor
|
|
71
111
|
end
|
|
72
112
|
expect(criteria).to eq original_criteria
|
|
73
113
|
end
|
|
74
114
|
end
|
|
115
|
+
|
|
75
116
|
context 'with a foreign key' do
|
|
76
117
|
it 'sorts by object id' do
|
|
77
118
|
records = []
|
|
@@ -79,92 +120,144 @@ describe Mongoid::Criteria do
|
|
|
79
120
|
expect(records).not_to be_empty
|
|
80
121
|
end
|
|
81
122
|
end
|
|
123
|
+
|
|
82
124
|
{ a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
|
|
83
125
|
context field_type do
|
|
84
126
|
it 'scrolls all with a block' do
|
|
85
127
|
records = []
|
|
86
|
-
Feed::Item.asc(field_name).scroll(cursor_type) do |record,
|
|
128
|
+
Feed::Item.asc(field_name).scroll(cursor_type) do |record, _iterator|
|
|
87
129
|
records << record
|
|
88
130
|
end
|
|
89
131
|
expect(records.size).to eq 10
|
|
90
132
|
expect(records).to eq Feed::Item.all.to_a
|
|
91
133
|
end
|
|
134
|
+
|
|
92
135
|
it 'scrolls all with a break' do
|
|
93
136
|
records = []
|
|
94
137
|
cursor = nil
|
|
95
|
-
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record,
|
|
138
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
|
|
96
139
|
records << record
|
|
97
|
-
cursor = next_cursor
|
|
140
|
+
cursor = iterator.next_cursor
|
|
98
141
|
end
|
|
99
142
|
expect(records.size).to eq 5
|
|
100
|
-
Feed::Item.asc(field_name).scroll(cursor) do |record,
|
|
143
|
+
Feed::Item.asc(field_name).scroll(cursor) do |record, iterator|
|
|
101
144
|
records << record
|
|
102
|
-
cursor = next_cursor
|
|
145
|
+
cursor = iterator.next_cursor
|
|
103
146
|
end
|
|
104
147
|
expect(records.size).to eq 10
|
|
105
148
|
expect(records).to eq Feed::Item.all.to_a
|
|
106
149
|
end
|
|
150
|
+
|
|
107
151
|
it 'scrolls from a cursor' do
|
|
108
152
|
last_record = nil
|
|
109
153
|
cursor = nil
|
|
110
|
-
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record,
|
|
154
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
|
|
111
155
|
last_record = record
|
|
112
|
-
cursor = next_cursor
|
|
156
|
+
cursor = iterator.next_cursor
|
|
113
157
|
end
|
|
114
158
|
sixth_item = Feed::Item.asc(field_name).to_a[5]
|
|
115
159
|
from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
|
|
116
160
|
expect(from_item).to eq sixth_item
|
|
117
161
|
end
|
|
162
|
+
|
|
118
163
|
it 'includes the current record when Mongoid::Scroll::Cursor#include_current is true' do
|
|
119
164
|
last_record = nil
|
|
120
165
|
cursor = nil
|
|
121
|
-
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record,
|
|
166
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
|
|
122
167
|
last_record = record
|
|
123
|
-
cursor = next_cursor
|
|
168
|
+
cursor = iterator.next_cursor
|
|
124
169
|
end
|
|
125
170
|
fifth_item = last_record
|
|
126
171
|
cursor.include_current = true
|
|
127
172
|
from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
|
|
128
173
|
expect(from_item).to eq fifth_item
|
|
129
174
|
end
|
|
175
|
+
|
|
130
176
|
it 'scrolls in descending order' do
|
|
131
177
|
records = []
|
|
132
|
-
Feed::Item.desc(field_name).limit(3).scroll(cursor_type) do |record,
|
|
178
|
+
Feed::Item.desc(field_name).limit(3).scroll(cursor_type) do |record, _iterator|
|
|
133
179
|
records << record
|
|
134
180
|
end
|
|
135
181
|
expect(records.size).to eq 3
|
|
136
182
|
expect(records).to eq Feed::Item.desc(field_name).limit(3).to_a
|
|
137
183
|
end
|
|
184
|
+
|
|
138
185
|
it 'map' do
|
|
139
186
|
record = Feed::Item.desc(field_name).limit(3).scroll(cursor_type).map { |r| r }.last
|
|
140
|
-
expect(record).
|
|
187
|
+
expect(record).not_to be_nil
|
|
141
188
|
cursor = cursor_type.from_record(record, field_type: field_type, field_name: field_name)
|
|
142
|
-
expect(cursor).
|
|
189
|
+
expect(cursor).not_to be_nil
|
|
143
190
|
expect(cursor.tiebreak_id).to eq record.id
|
|
144
191
|
expect(cursor.value).to eq record.send(field_name)
|
|
145
192
|
end
|
|
193
|
+
|
|
146
194
|
it 'can be reused' do
|
|
147
195
|
ids = Feed::Item.asc(field_name).limit(2).map(&:id)
|
|
148
|
-
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_,
|
|
149
|
-
|
|
150
|
-
expect(Feed::Item.asc(field_name).limit(2).scroll(
|
|
196
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
|
|
197
|
+
iterator.next_cursor.include_current = true
|
|
198
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(iterator.next_cursor).pluck(:id)).to eq ids
|
|
151
199
|
break
|
|
152
200
|
end
|
|
153
201
|
end
|
|
202
|
+
|
|
154
203
|
it 'can be re-created and reused' do
|
|
155
204
|
ids = Feed::Item.asc(field_name).limit(2).map(&:id)
|
|
156
|
-
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_,
|
|
157
|
-
new_cursor = cursor_type.new(
|
|
205
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
|
|
206
|
+
new_cursor = cursor_type.new(iterator.next_cursor.to_s, field_type: field_type, field_name: field_name)
|
|
158
207
|
new_cursor.include_current = true
|
|
159
208
|
expect(Feed::Item.asc(field_name).limit(2).scroll(new_cursor).pluck(:id)).to eq ids
|
|
160
209
|
break
|
|
161
210
|
end
|
|
162
211
|
end
|
|
212
|
+
|
|
213
|
+
it 'can scroll back with the previous cursor' do
|
|
214
|
+
first_iterator = nil
|
|
215
|
+
second_iterator = nil
|
|
216
|
+
third_iterator = nil
|
|
217
|
+
|
|
218
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
|
|
219
|
+
first_iterator = iterator
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
Feed::Item.asc(field_name).limit(2).scroll(first_iterator.next_cursor) do |_, iterator|
|
|
223
|
+
second_iterator = iterator
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
Feed::Item.asc(field_name).limit(2).scroll(second_iterator.next_cursor) do |_, iterator|
|
|
227
|
+
third_iterator = iterator
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
records = Feed::Item.asc(field_name)
|
|
231
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(second_iterator.previous_cursor)).to eq(records.limit(2))
|
|
232
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(third_iterator.previous_cursor)).to eq(records.skip(2).limit(2))
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'can loop over the same records with the current cursor' do
|
|
236
|
+
current_cursor = nil
|
|
237
|
+
cursor = cursor_type.from_record Feed::Item.skip(4).first, field_name: field_name, field_type: field_type, include_current: true
|
|
238
|
+
|
|
239
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor) do |_, iterator|
|
|
240
|
+
current_cursor = iterator.current_cursor
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(current_cursor).to_a).to eq(Feed::Item.asc(field_name).skip(4).limit(2).to_a)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it 'can loop over the first records with the first page cursor' do
|
|
247
|
+
first_cursor = nil
|
|
248
|
+
|
|
249
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, it|
|
|
250
|
+
first_cursor = it.first_cursor
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(first_cursor).to_a).to eq(Feed::Item.asc(field_name).limit(2).to_a)
|
|
254
|
+
end
|
|
163
255
|
end
|
|
164
256
|
end
|
|
165
257
|
end
|
|
258
|
+
|
|
166
259
|
context 'with logic in initial criteria' do
|
|
167
|
-
before
|
|
260
|
+
before do
|
|
168
261
|
3.times do |i|
|
|
169
262
|
Feed::Item.create!(
|
|
170
263
|
name: "Feed Item #{i}",
|
|
@@ -184,63 +277,69 @@ describe Mongoid::Criteria do
|
|
|
184
277
|
a_time: Time.new(2014, 2, 2, 1, 2, 3)
|
|
185
278
|
)
|
|
186
279
|
end
|
|
280
|
+
|
|
187
281
|
it 'respects original criteria with OR logic' do
|
|
188
282
|
criteria = Feed::Item.where(
|
|
189
283
|
'$or' => [{ :a_time.gt => Time.new(2015, 7, 22, 1, 2, 3) }, { :a_time.lte => Time.new(2015, 7, 22, 1, 2, 3), :a_date.gte => Date.new(2015, 7, 30) }]
|
|
190
284
|
).asc(:a_time)
|
|
191
285
|
records = []
|
|
192
286
|
cursor = nil
|
|
193
|
-
criteria.limit(2).scroll(cursor_type) do |record,
|
|
287
|
+
criteria.limit(2).scroll(cursor_type) do |record, iterator|
|
|
194
288
|
records << record
|
|
195
|
-
cursor = next_cursor
|
|
289
|
+
cursor = iterator.next_cursor
|
|
196
290
|
end
|
|
197
291
|
expect(records.size).to eq 2
|
|
198
292
|
expect(records.map(&:name)).to eq ['Feed Item 0', 'Feed Item 1']
|
|
199
293
|
records = []
|
|
200
|
-
criteria.limit(2).scroll(cursor) do |record,
|
|
294
|
+
criteria.limit(2).scroll(cursor) do |record, iterator|
|
|
201
295
|
records << record
|
|
202
|
-
cursor = next_cursor
|
|
296
|
+
cursor = iterator.next_cursor
|
|
203
297
|
end
|
|
204
298
|
expect(records.size).to eq 1
|
|
205
299
|
expect(records.map(&:name)).to eq ['Feed Item 2']
|
|
206
300
|
end
|
|
207
301
|
end
|
|
302
|
+
|
|
208
303
|
context 'with embeddable objects' do
|
|
209
304
|
before do
|
|
210
305
|
@item = Feed::Item.create! a_integer: 1, name: 'item'
|
|
211
306
|
@embedded_item = Feed::EmbeddedItem.create! name: 'embedded', item: @item
|
|
212
307
|
end
|
|
308
|
+
|
|
213
309
|
it 'respects embedded queries' do
|
|
214
310
|
records = []
|
|
215
311
|
criteria = @item.embedded_items.limit(2)
|
|
216
|
-
criteria.scroll(cursor_type) do |record,
|
|
312
|
+
criteria.scroll(cursor_type) do |record, _iterator|
|
|
217
313
|
records << record
|
|
218
314
|
end
|
|
219
315
|
expect(records.size).to eq 1
|
|
220
316
|
expect(records.map(&:name)).to eq ['embedded']
|
|
221
317
|
end
|
|
222
318
|
end
|
|
319
|
+
|
|
223
320
|
context 'with overlapping data', if: MongoDB.mmapv1? do
|
|
224
|
-
before
|
|
321
|
+
before do
|
|
225
322
|
3.times { Feed::Item.create! a_integer: 5 }
|
|
226
323
|
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
|
227
324
|
end
|
|
325
|
+
|
|
228
326
|
it 'natural order is different from order by id' do
|
|
229
327
|
# natural order isn't necessarily going to be the same as _id order
|
|
230
328
|
# if a document is updated and grows in size, it may need to be relocated and
|
|
231
329
|
# thus cause the natural order to change
|
|
232
|
-
expect(Feed::Item.order_by('$natural' => 1).to_a).
|
|
330
|
+
expect(Feed::Item.order_by('$natural' => 1).to_a).not_to eq(Feed::Item.order_by(_id: 1).to_a)
|
|
233
331
|
end
|
|
332
|
+
|
|
234
333
|
[{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
|
235
334
|
it "scrolls by #{sort_order}" do
|
|
236
335
|
records = []
|
|
237
336
|
cursor = nil
|
|
238
|
-
Feed::Item.order_by(sort_order).limit(2).scroll(cursor_type) do |record,
|
|
337
|
+
Feed::Item.order_by(sort_order).limit(2).scroll(cursor_type) do |record, iterator|
|
|
239
338
|
records << record
|
|
240
|
-
cursor = next_cursor
|
|
339
|
+
cursor = iterator.next_cursor
|
|
241
340
|
end
|
|
242
341
|
expect(records.size).to eq 2
|
|
243
|
-
Feed::Item.order_by(sort_order).scroll(cursor) do |record,
|
|
342
|
+
Feed::Item.order_by(sort_order).scroll(cursor) do |record, _iterator|
|
|
244
343
|
records << record
|
|
245
344
|
end
|
|
246
345
|
expect(records.size).to eq 3
|
|
@@ -248,10 +347,12 @@ describe Mongoid::Criteria do
|
|
|
248
347
|
end
|
|
249
348
|
end
|
|
250
349
|
end
|
|
350
|
+
|
|
251
351
|
context 'with several records having the same value' do
|
|
252
|
-
before
|
|
352
|
+
before do
|
|
253
353
|
3.times { Feed::Item.create! a_integer: 5 }
|
|
254
354
|
end
|
|
355
|
+
|
|
255
356
|
it 'returns records from the current one when Mongoid::Scroll::Cursor#include_current is true' do
|
|
256
357
|
_first_item, second_item, third_item = Feed::Item.asc(:a_integer).to_a
|
|
257
358
|
cursor = Mongoid::Scroll::Cursor.from_record(second_item, field: Feed::Item.fields['a_integer'])
|
|
@@ -260,6 +361,7 @@ describe Mongoid::Criteria do
|
|
|
260
361
|
expect(items).to eq([second_item, third_item])
|
|
261
362
|
end
|
|
262
363
|
end
|
|
364
|
+
|
|
263
365
|
context 'with DateTime with a milisecond precision' do
|
|
264
366
|
let!(:item_1) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.1, 'UTC')) }
|
|
265
367
|
let!(:item_2) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.2, 'UTC')) }
|
|
@@ -268,9 +370,9 @@ describe Mongoid::Criteria do
|
|
|
268
370
|
it 'doesn\'t lose the precision when rebuilding the cursor' do
|
|
269
371
|
records = []
|
|
270
372
|
cursor = nil
|
|
271
|
-
Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor_type) do |record,
|
|
373
|
+
Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor_type) do |record, iterator|
|
|
272
374
|
records << record
|
|
273
|
-
cursor = next_cursor
|
|
375
|
+
cursor = iterator.next_cursor
|
|
274
376
|
end
|
|
275
377
|
expect(records).to eq [item_1, item_2]
|
|
276
378
|
cursor = cursor_type.new(cursor.to_s, field: Feed::Item.fields['a_datetime'])
|
data/spec/mongoid/cursor_spec.rb
CHANGED
|
@@ -5,125 +5,158 @@ describe Mongoid::Scroll::Cursor do
|
|
|
5
5
|
subject do
|
|
6
6
|
Mongoid::Scroll::Cursor.new nil, field_name: 'a_string', field_type: String
|
|
7
7
|
end
|
|
8
|
-
|
|
9
|
-
its(:
|
|
10
|
-
its(:
|
|
8
|
+
|
|
9
|
+
its(:tiebreak_id) { is_expected.to be_nil }
|
|
10
|
+
its(:value) { is_expected.to be_nil }
|
|
11
|
+
its(:criteria) { is_expected.to eq({}) }
|
|
11
12
|
end
|
|
13
|
+
|
|
12
14
|
context 'an invalid cursor' do
|
|
13
15
|
it 'raises InvalidCursorError' do
|
|
14
16
|
expect { Mongoid::Scroll::Cursor.new 'invalid', field_name: 'a_string', field_type: String }.to raise_error Mongoid::Scroll::Errors::InvalidCursorError,
|
|
15
17
|
/The cursor supplied is invalid: invalid./
|
|
16
18
|
end
|
|
17
19
|
end
|
|
20
|
+
|
|
18
21
|
context 'an id field cursor' do
|
|
19
|
-
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
20
22
|
subject do
|
|
21
23
|
Mongoid::Scroll::Cursor.new "#{feed_item.id}:#{feed_item.id}", field_name: '_id', field_type: BSON::ObjectId, direction: 1
|
|
22
24
|
end
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
27
|
+
|
|
28
|
+
its(:value) { is_expected.to eq feed_item.id }
|
|
29
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
30
|
+
|
|
25
31
|
its(:criteria) do
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
is_expected.to eq('$or' => [
|
|
33
|
+
{ '_id' => { '$gt' => BSON::ObjectId(feed_item.id.to_s) } }
|
|
34
|
+
])
|
|
29
35
|
end
|
|
30
36
|
end
|
|
37
|
+
|
|
31
38
|
context 'a string field cursor' do
|
|
32
|
-
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
33
39
|
subject do
|
|
34
40
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String
|
|
35
41
|
end
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
|
|
43
|
+
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
44
|
+
|
|
45
|
+
its(:value) { is_expected.to eq feed_item.a_string }
|
|
46
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
47
|
+
|
|
38
48
|
its(:criteria) do
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
is_expected.to eq('$or' => [
|
|
50
|
+
{ 'a_string' => { '$gt' => feed_item.a_string } },
|
|
51
|
+
{ 'a_string' => feed_item.a_string, '_id' => { '$gt' => feed_item.id } }
|
|
52
|
+
])
|
|
43
53
|
end
|
|
44
54
|
end
|
|
55
|
+
|
|
45
56
|
context 'an integer field cursor' do
|
|
46
|
-
let(:feed_item) { Feed::Item.create!(a_integer: 10) }
|
|
47
57
|
subject do
|
|
48
58
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_integer}:#{feed_item.id}", field_name: 'a_integer', field_type: Integer
|
|
49
59
|
end
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
|
|
61
|
+
let(:feed_item) { Feed::Item.create!(a_integer: 10) }
|
|
62
|
+
|
|
63
|
+
its(:value) { is_expected.to eq feed_item.a_integer }
|
|
64
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
65
|
+
|
|
52
66
|
its(:criteria) do
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
is_expected.to eq('$or' => [
|
|
68
|
+
{ 'a_integer' => { '$gt' => feed_item.a_integer } },
|
|
69
|
+
{ 'a_integer' => feed_item.a_integer, '_id' => { '$gt' => feed_item.id } }
|
|
70
|
+
])
|
|
57
71
|
end
|
|
58
72
|
end
|
|
73
|
+
|
|
59
74
|
context 'a date/time field cursor' do
|
|
60
|
-
let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC')) }
|
|
61
75
|
subject do
|
|
62
76
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}", field_name: 'a_datetime', field_type: DateTime
|
|
63
77
|
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
|
|
79
|
+
let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC')) }
|
|
80
|
+
|
|
81
|
+
its(:value) { is_expected.to eq feed_item.a_datetime }
|
|
82
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
83
|
+
its(:to_s) { is_expected.to eq "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}" }
|
|
84
|
+
|
|
67
85
|
its(:criteria) do
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
is_expected.to eq('$or' => [
|
|
87
|
+
{ 'a_datetime' => { '$gt' => feed_item.a_datetime } },
|
|
88
|
+
{ 'a_datetime' => feed_item.a_datetime, '_id' => { '$gt' => feed_item.id } }
|
|
89
|
+
])
|
|
72
90
|
end
|
|
73
91
|
end
|
|
92
|
+
|
|
74
93
|
context 'a date field cursor' do
|
|
75
|
-
let(:feed_item) { Feed::Item.create!(a_date: Date.new(2013, 12, 21)) }
|
|
76
94
|
subject do
|
|
77
95
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}", field_name: 'a_date', field_type: Date
|
|
78
96
|
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
|
|
98
|
+
let(:feed_item) { Feed::Item.create!(a_date: Date.new(2013, 12, 21)) }
|
|
99
|
+
|
|
100
|
+
its(:value) { is_expected.to eq feed_item.a_date }
|
|
101
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
102
|
+
its(:to_s) { is_expected.to eq "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}" }
|
|
103
|
+
|
|
82
104
|
its(:criteria) do
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
is_expected.to eq('$or' => [
|
|
106
|
+
{ 'a_date' => { '$gt' => feed_item.a_date.to_datetime } },
|
|
107
|
+
{ 'a_date' => feed_item.a_date.to_datetime, '_id' => { '$gt' => feed_item.id } }
|
|
108
|
+
])
|
|
87
109
|
end
|
|
88
110
|
end
|
|
111
|
+
|
|
89
112
|
context 'a time field cursor' do
|
|
90
|
-
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
91
113
|
subject do
|
|
92
114
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field_name: 'a_time', field_type: Time
|
|
93
115
|
end
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
116
|
+
|
|
117
|
+
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
118
|
+
|
|
119
|
+
its(:value) { is_expected.to eq feed_item.a_time }
|
|
120
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
121
|
+
its(:to_s) { is_expected.to eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
|
|
122
|
+
|
|
97
123
|
its(:criteria) do
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
is_expected.to eq('$or' => [
|
|
125
|
+
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
|
126
|
+
{ 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
|
|
127
|
+
])
|
|
102
128
|
end
|
|
103
129
|
end
|
|
130
|
+
|
|
104
131
|
context 'a time field cursor with a field option' do
|
|
105
|
-
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
106
132
|
subject do
|
|
107
133
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field: Feed::Item.fields['a_time']
|
|
108
134
|
end
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
|
|
136
|
+
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
137
|
+
|
|
138
|
+
its(:value) { is_expected.to eq feed_item.a_time }
|
|
139
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
140
|
+
its(:to_s) { is_expected.to eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
|
|
141
|
+
|
|
112
142
|
its(:criteria) do
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
143
|
+
is_expected.to eq('$or' => [
|
|
144
|
+
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
|
145
|
+
{ 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
|
|
146
|
+
])
|
|
117
147
|
end
|
|
118
148
|
end
|
|
149
|
+
|
|
119
150
|
context 'an array field cursor' do
|
|
120
151
|
let(:feed_item) { Feed::Item.create!(a_array: %w[x y]) }
|
|
152
|
+
|
|
121
153
|
it 'is not supported' do
|
|
122
154
|
expect do
|
|
123
155
|
Mongoid::Scroll::Cursor.from_record feed_item, field_name: 'a_array', field_type: Array
|
|
124
156
|
end.to raise_error Mongoid::Scroll::Errors::UnsupportedFieldTypeError, /The type of the field 'a_array' is not supported: Array./
|
|
125
157
|
end
|
|
126
158
|
end
|
|
159
|
+
|
|
127
160
|
context 'an invalid field cursor' do
|
|
128
161
|
it 'raises ArgumentError' do
|
|
129
162
|
expect do
|
|
@@ -131,23 +164,38 @@ describe Mongoid::Scroll::Cursor do
|
|
|
131
164
|
end.to raise_error ArgumentError
|
|
132
165
|
end
|
|
133
166
|
end
|
|
134
|
-
|
|
167
|
+
|
|
168
|
+
context 'an invalid type cursor' do
|
|
135
169
|
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
170
|
+
|
|
171
|
+
it 'raises Mongoid::Scroll::Errors::UnsupportedTypeError' do
|
|
172
|
+
expect do
|
|
173
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String, include_current: true, type: :invalid
|
|
174
|
+
end.to raise_error Mongoid::Scroll::Errors::UnsupportedTypeError, /The type supplied in the cursor is not supported: invalid./
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
context 'a cursor with include_current set to true' do
|
|
136
179
|
subject do
|
|
137
180
|
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String, include_current: true
|
|
138
181
|
end
|
|
139
|
-
|
|
140
|
-
|
|
182
|
+
|
|
183
|
+
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
184
|
+
|
|
185
|
+
its(:value) { is_expected.to eq 'astring' }
|
|
186
|
+
its(:tiebreak_id) { is_expected.to eq feed_item.id }
|
|
187
|
+
|
|
141
188
|
its(:criteria) do
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
189
|
+
is_expected.to eq('$or' => [
|
|
190
|
+
{ 'a_string' => { '$gt' => 'astring' } },
|
|
191
|
+
{ '_id' => { '$gte' => BSON::ObjectId(feed_item.id.to_s) }, 'a_string' => 'astring' }
|
|
192
|
+
])
|
|
146
193
|
end
|
|
147
194
|
end
|
|
195
|
+
|
|
148
196
|
context 'criteria' do
|
|
149
197
|
context 'with data' do
|
|
150
|
-
before
|
|
198
|
+
before do
|
|
151
199
|
3.times do |i|
|
|
152
200
|
Feed::Item.create!(
|
|
153
201
|
name: "Feed Item #{i}",
|
|
@@ -159,6 +207,7 @@ describe Mongoid::Scroll::Cursor do
|
|
|
159
207
|
a_time: Time.new(2014, 2, 2, 1, 2, 3)
|
|
160
208
|
)
|
|
161
209
|
end
|
|
210
|
+
|
|
162
211
|
it 'merges cursor criteria when no sort field is given' do
|
|
163
212
|
criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
|
|
164
213
|
feed_item = Feed::Item.where(name: 'Feed Item 1').first
|
data/spec/mongoid/scroll_spec.rb
CHANGED