mongoid-scroll 0.3.7 → 1.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 +36 -0
- data/.github/workflows/danger.yml +17 -0
- data/.rubocop_todo.yml +24 -19
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -5
- data/LICENSE.md +1 -1
- data/README.md +59 -46
- data/RELEASING.md +1 -1
- data/UPGRADING.md +14 -0
- data/examples/mongoid_scroll_feed.rb +2 -8
- data/lib/config/locales/en.yml +8 -0
- data/lib/mongo/scrollable.rb +8 -3
- data/lib/mongoid/criteria/scrollable/cursors.rb +18 -0
- data/lib/mongoid/criteria/scrollable/fields.rb +21 -0
- data/lib/mongoid/criteria/scrollable.rb +13 -12
- data/lib/mongoid/scroll/base64_encoded_cursor.rb +40 -0
- data/lib/mongoid/scroll/base_cursor.rb +123 -0
- data/lib/mongoid/scroll/cursor.rb +24 -87
- data/lib/mongoid/scroll/errors/base.rb +1 -1
- data/lib/mongoid/scroll/errors/invalid_base64_cursor_error.rb +11 -0
- data/lib/mongoid/scroll/errors/invalid_base_cursor_error.rb +8 -0
- data/lib/mongoid/scroll/errors/invalid_cursor_error.rb +1 -1
- data/lib/mongoid/scroll/errors/mismatched_sort_fields_error.rb +15 -0
- data/lib/mongoid/scroll/errors.rb +3 -0
- data/lib/mongoid/scroll/version.rb +1 -1
- data/lib/mongoid-scroll.rb +4 -1
- data/spec/mongo/collection_view_spec.rb +122 -105
- data/spec/mongoid/base64_encoded_cursor_spec.rb +233 -0
- data/spec/mongoid/criteria_spec.rb +252 -196
- data/spec/mongoid/{scroll_cursor_spec.rb → cursor_spec.rb} +54 -18
- data/spec/support/feed/item.rb +1 -1
- metadata +15 -8
- data/.travis.yml +0 -37
- data/examples/moped_scroll_feed.rb +0 -45
- data/lib/moped/scrollable.rb +0 -38
- data/spec/moped/query_spec.rb +0 -126
|
@@ -1,225 +1,281 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Mongoid::Criteria do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
subject do
|
|
19
|
-
Feed::Item.all
|
|
20
|
-
end
|
|
21
|
-
it 'adds a default sort by _id' do
|
|
22
|
-
expect(subject.scroll.options[:sort]).to eq('_id' => 1)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
context 'with data' do
|
|
26
|
-
before :each do
|
|
27
|
-
10.times do |i|
|
|
28
|
-
Feed::Item.create!(
|
|
29
|
-
a_string: i.to_s,
|
|
30
|
-
a_integer: i,
|
|
31
|
-
a_datetime: DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC'),
|
|
32
|
-
a_date: Date.new(2013, i + 1, 21),
|
|
33
|
-
a_time: Time.at(Time.now.to_i + i)
|
|
34
|
-
)
|
|
4
|
+
[Mongoid::Scroll::Cursor, Mongoid::Scroll::Base64EncodedCursor].each do |cursor_type|
|
|
5
|
+
context cursor_type do
|
|
6
|
+
context 'with multiple sort fields' do
|
|
7
|
+
subject do
|
|
8
|
+
Feed::Item.desc(:name).asc(:value)
|
|
9
|
+
end
|
|
10
|
+
it ':scroll' do
|
|
11
|
+
expect(subject).to respond_to(:scroll)
|
|
12
|
+
end
|
|
13
|
+
it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
|
|
14
|
+
expect do
|
|
15
|
+
subject.scroll(cursor_type)
|
|
16
|
+
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
|
+
end
|
|
35
18
|
end
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
19
|
+
context 'with different sort fields between the cursor and the criteria' do
|
|
20
|
+
subject do
|
|
21
|
+
Feed::Item.desc(:name)
|
|
22
|
+
end
|
|
23
|
+
it 'raises Mongoid::Scroll::Errors::MismatchedSortFieldsError' do
|
|
24
|
+
record = Feed::Item.create!
|
|
25
|
+
cursor = cursor_type.from_record(record, field: record.fields['a_string'])
|
|
26
|
+
error_string = /You're attempting to scroll over data with a sort order that differs between the cursor and the original criteria: field_name, direction./
|
|
27
|
+
expect { subject.scroll(cursor) }.to raise_error Mongoid::Scroll::Errors::MismatchedSortFieldsError, error_string
|
|
28
|
+
end
|
|
45
29
|
end
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
cursor = next_cursor
|
|
54
|
-
end
|
|
55
|
-
criteria.scroll(cursor) do |_record, next_cursor|
|
|
56
|
-
cursor = next_cursor
|
|
57
|
-
end
|
|
58
|
-
expect(criteria).to eq original_criteria
|
|
30
|
+
context 'with no sort' do
|
|
31
|
+
subject do
|
|
32
|
+
Feed::Item.all
|
|
33
|
+
end
|
|
34
|
+
it 'adds a default sort by _id' do
|
|
35
|
+
expect(subject.scroll(cursor_type).options[:sort]).to eq('_id' => 1)
|
|
36
|
+
end
|
|
59
37
|
end
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
38
|
+
context 'with data' do
|
|
39
|
+
before :each do
|
|
40
|
+
10.times do |i|
|
|
41
|
+
Feed::Item.create!(
|
|
42
|
+
name: i.to_s,
|
|
43
|
+
a_string: i.to_s,
|
|
44
|
+
a_integer: i,
|
|
45
|
+
a_datetime: DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC'),
|
|
46
|
+
a_date: Date.new(2013, i + 1, 21),
|
|
47
|
+
a_time: Time.at(Time.now.to_i + i)
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
context 'default' do
|
|
52
|
+
it 'scrolls all' do
|
|
53
|
+
records = []
|
|
54
|
+
Feed::Item.all.scroll(cursor_type) do |record, _next_cursor|
|
|
55
|
+
records << record
|
|
56
|
+
end
|
|
57
|
+
expect(records.size).to eq 10
|
|
58
|
+
expect(records).to eq Feed::Item.all.to_a
|
|
59
|
+
end
|
|
60
|
+
it 'does not change original criteria' do
|
|
61
|
+
criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
|
|
62
|
+
original_criteria = criteria.dup
|
|
63
|
+
criteria.limit(2).scroll(cursor_type)
|
|
64
|
+
expect(criteria).to eq original_criteria
|
|
65
|
+
cursor = nil
|
|
66
|
+
criteria.limit(2).scroll(cursor) do |_record, next_cursor|
|
|
67
|
+
cursor = next_cursor
|
|
68
|
+
end
|
|
69
|
+
criteria.scroll(cursor) do |_record, next_cursor|
|
|
70
|
+
cursor = next_cursor
|
|
71
|
+
end
|
|
72
|
+
expect(criteria).to eq original_criteria
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
context 'with a foreign key' do
|
|
76
|
+
it 'sorts by object id' do
|
|
77
|
+
records = []
|
|
78
|
+
Feed::Item.asc('publisher_id').scroll(cursor_type) { |r, _| records << r }
|
|
79
|
+
expect(records).not_to be_empty
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
{ a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
|
|
83
|
+
context field_type do
|
|
84
|
+
it 'scrolls all with a block' do
|
|
85
|
+
records = []
|
|
86
|
+
Feed::Item.asc(field_name).scroll(cursor_type) do |record, _next_cursor|
|
|
87
|
+
records << record
|
|
88
|
+
end
|
|
89
|
+
expect(records.size).to eq 10
|
|
90
|
+
expect(records).to eq Feed::Item.all.to_a
|
|
91
|
+
end
|
|
92
|
+
it 'scrolls all with a break' do
|
|
93
|
+
records = []
|
|
94
|
+
cursor = nil
|
|
95
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
|
|
96
|
+
records << record
|
|
97
|
+
cursor = next_cursor
|
|
98
|
+
end
|
|
99
|
+
expect(records.size).to eq 5
|
|
100
|
+
Feed::Item.asc(field_name).scroll(cursor) do |record, next_cursor|
|
|
101
|
+
records << record
|
|
102
|
+
cursor = next_cursor
|
|
103
|
+
end
|
|
104
|
+
expect(records.size).to eq 10
|
|
105
|
+
expect(records).to eq Feed::Item.all.to_a
|
|
106
|
+
end
|
|
107
|
+
it 'scrolls from a cursor' do
|
|
108
|
+
last_record = nil
|
|
109
|
+
cursor = nil
|
|
110
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
|
|
111
|
+
last_record = record
|
|
112
|
+
cursor = next_cursor
|
|
113
|
+
end
|
|
114
|
+
sixth_item = Feed::Item.asc(field_name).to_a[5]
|
|
115
|
+
from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
|
|
116
|
+
expect(from_item).to eq sixth_item
|
|
117
|
+
end
|
|
118
|
+
it 'includes the current record when Mongoid::Scroll::Cursor#include_current is true' do
|
|
119
|
+
last_record = nil
|
|
120
|
+
cursor = nil
|
|
121
|
+
Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
|
|
122
|
+
last_record = record
|
|
123
|
+
cursor = next_cursor
|
|
124
|
+
end
|
|
125
|
+
fifth_item = last_record
|
|
126
|
+
cursor.include_current = true
|
|
127
|
+
from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
|
|
128
|
+
expect(from_item).to eq fifth_item
|
|
129
|
+
end
|
|
130
|
+
it 'scrolls in descending order' do
|
|
131
|
+
records = []
|
|
132
|
+
Feed::Item.desc(field_name).limit(3).scroll(cursor_type) do |record, _next_cursor|
|
|
133
|
+
records << record
|
|
134
|
+
end
|
|
135
|
+
expect(records.size).to eq 3
|
|
136
|
+
expect(records).to eq Feed::Item.desc(field_name).limit(3).to_a
|
|
137
|
+
end
|
|
138
|
+
it 'map' do
|
|
139
|
+
record = Feed::Item.desc(field_name).limit(3).scroll(cursor_type).map { |r| r }.last
|
|
140
|
+
expect(record).to_not be nil
|
|
141
|
+
cursor = cursor_type.from_record(record, field_type: field_type, field_name: field_name)
|
|
142
|
+
expect(cursor).to_not be nil
|
|
143
|
+
expect(cursor.tiebreak_id).to eq record.id
|
|
144
|
+
expect(cursor.value).to eq record.send(field_name)
|
|
145
|
+
end
|
|
146
|
+
it 'can be reused' do
|
|
147
|
+
ids = Feed::Item.asc(field_name).limit(2).map(&:id)
|
|
148
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, cursor|
|
|
149
|
+
cursor.include_current = true
|
|
150
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(cursor).pluck(:id)).to eq ids
|
|
151
|
+
break
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
it 'can be re-created and reused' do
|
|
155
|
+
ids = Feed::Item.asc(field_name).limit(2).map(&:id)
|
|
156
|
+
Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, cursor|
|
|
157
|
+
new_cursor = cursor_type.new(cursor.to_s, field_type: field_type, field_name: field_name)
|
|
158
|
+
new_cursor.include_current = true
|
|
159
|
+
expect(Feed::Item.asc(field_name).limit(2).scroll(new_cursor).pluck(:id)).to eq ids
|
|
160
|
+
break
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
67
165
|
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
166
|
+
context 'with logic in initial criteria' do
|
|
167
|
+
before :each do
|
|
168
|
+
3.times do |i|
|
|
169
|
+
Feed::Item.create!(
|
|
170
|
+
name: "Feed Item #{i}",
|
|
171
|
+
a_string: i.to_s,
|
|
172
|
+
a_integer: i,
|
|
173
|
+
a_datetime: DateTime.new(2015, i + 1, 21, 1, 42, 3, 'UTC'),
|
|
174
|
+
a_date: Date.new(2016, i + 1, 21),
|
|
175
|
+
a_time: Time.new(2015, i + 1, 22, 1, 2, 3)
|
|
176
|
+
)
|
|
76
177
|
end
|
|
77
|
-
|
|
78
|
-
|
|
178
|
+
Feed::Item.create!(
|
|
179
|
+
name: 'Feed Item 3',
|
|
180
|
+
a_string: '3',
|
|
181
|
+
a_integer: 3,
|
|
182
|
+
a_datetime: DateTime.new(2015, 3, 2, 1, 2, 3),
|
|
183
|
+
a_date: Date.new(2012, 2, 3),
|
|
184
|
+
a_time: Time.new(2014, 2, 2, 1, 2, 3)
|
|
185
|
+
)
|
|
79
186
|
end
|
|
80
|
-
it '
|
|
187
|
+
it 'respects original criteria with OR logic' do
|
|
188
|
+
criteria = Feed::Item.where(
|
|
189
|
+
'$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
|
+
).asc(:a_time)
|
|
81
191
|
records = []
|
|
82
192
|
cursor = nil
|
|
83
|
-
|
|
193
|
+
criteria.limit(2).scroll(cursor_type) do |record, next_cursor|
|
|
84
194
|
records << record
|
|
85
195
|
cursor = next_cursor
|
|
86
196
|
end
|
|
87
|
-
expect(records.size).to eq
|
|
88
|
-
|
|
197
|
+
expect(records.size).to eq 2
|
|
198
|
+
expect(records.map(&:name)).to eq ['Feed Item 0', 'Feed Item 1']
|
|
199
|
+
records = []
|
|
200
|
+
criteria.limit(2).scroll(cursor) do |record, next_cursor|
|
|
89
201
|
records << record
|
|
90
202
|
cursor = next_cursor
|
|
91
203
|
end
|
|
92
|
-
expect(records.size).to eq
|
|
93
|
-
expect(records).to eq Feed
|
|
204
|
+
expect(records.size).to eq 1
|
|
205
|
+
expect(records.map(&:name)).to eq ['Feed Item 2']
|
|
94
206
|
end
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Feed::Item.
|
|
99
|
-
|
|
100
|
-
cursor = next_cursor
|
|
101
|
-
end
|
|
102
|
-
sixth_item = Feed::Item.asc(field_name).to_a[5]
|
|
103
|
-
from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
|
|
104
|
-
expect(from_item).to eq sixth_item
|
|
207
|
+
end
|
|
208
|
+
context 'with embeddable objects' do
|
|
209
|
+
before do
|
|
210
|
+
@item = Feed::Item.create! a_integer: 1, name: 'item'
|
|
211
|
+
@embedded_item = Feed::EmbeddedItem.create! name: 'embedded', item: @item
|
|
105
212
|
end
|
|
106
|
-
it '
|
|
213
|
+
it 'respects embedded queries' do
|
|
107
214
|
records = []
|
|
108
|
-
|
|
215
|
+
criteria = @item.embedded_items.limit(2)
|
|
216
|
+
criteria.scroll(cursor_type) do |record, _next_cursor|
|
|
109
217
|
records << record
|
|
110
218
|
end
|
|
111
|
-
expect(records.size).to eq
|
|
112
|
-
expect(records).to eq
|
|
113
|
-
end
|
|
114
|
-
it 'map' do
|
|
115
|
-
record = Feed::Item.desc(field_name).limit(3).scroll.map { |r| r }.last
|
|
116
|
-
expect(record).to_not be nil
|
|
117
|
-
cursor = Mongoid::Scroll::Cursor.from_record(record, field_type: field_type, field_name: field_name)
|
|
118
|
-
expect(cursor).to_not be nil
|
|
119
|
-
expect(cursor.to_s.split(':')).to eq [
|
|
120
|
-
Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, record.send(field_name)).to_s,
|
|
121
|
-
record.id.to_s
|
|
122
|
-
]
|
|
219
|
+
expect(records.size).to eq 1
|
|
220
|
+
expect(records.map(&:name)).to eq ['embedded']
|
|
123
221
|
end
|
|
124
222
|
end
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
records = []
|
|
153
|
-
cursor = nil
|
|
154
|
-
criteria.limit(2).scroll do |record, next_cursor|
|
|
155
|
-
records << record
|
|
156
|
-
cursor = next_cursor
|
|
157
|
-
end
|
|
158
|
-
expect(records.size).to eq 2
|
|
159
|
-
expect(records.map(&:name)).to eq ['Feed Item 0', 'Feed Item 1']
|
|
160
|
-
records = []
|
|
161
|
-
criteria.limit(2).scroll(cursor) do |record, next_cursor|
|
|
162
|
-
records << record
|
|
163
|
-
cursor = next_cursor
|
|
164
|
-
end
|
|
165
|
-
expect(records.size).to eq 1
|
|
166
|
-
expect(records.map(&:name)).to eq ['Feed Item 2']
|
|
167
|
-
end
|
|
168
|
-
it 'merges cursor criteria when no sort field is given' do
|
|
169
|
-
criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
|
|
170
|
-
feed_item = Feed::Item.where(name: 'Feed Item 1').first
|
|
171
|
-
cursor_input = "#{feed_item.id}:#{feed_item.id}"
|
|
172
|
-
field_type = Mongoid::Compatibility::Version.mongoid3? ? Moped::BSON::ObjectId : BSON::ObjectId
|
|
173
|
-
cursor_options = { field_type: field_type, field_name: '_id', direction: 1 }
|
|
174
|
-
cursor = Mongoid::Scroll::Cursor.new(cursor_input, cursor_options)
|
|
175
|
-
records = []
|
|
176
|
-
criteria.limit(2).scroll(cursor) do |record, next_cursor|
|
|
177
|
-
records << record
|
|
178
|
-
cursor = next_cursor
|
|
223
|
+
context 'with overlapping data', if: MongoDB.mmapv1? do
|
|
224
|
+
before :each do
|
|
225
|
+
3.times { Feed::Item.create! a_integer: 5 }
|
|
226
|
+
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
|
227
|
+
end
|
|
228
|
+
it 'natural order is different from order by id' do
|
|
229
|
+
# natural order isn't necessarily going to be the same as _id order
|
|
230
|
+
# if a document is updated and grows in size, it may need to be relocated and
|
|
231
|
+
# thus cause the natural order to change
|
|
232
|
+
expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq(Feed::Item.order_by(_id: 1).to_a)
|
|
233
|
+
end
|
|
234
|
+
[{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
|
235
|
+
it "scrolls by #{sort_order}" do
|
|
236
|
+
records = []
|
|
237
|
+
cursor = nil
|
|
238
|
+
Feed::Item.order_by(sort_order).limit(2).scroll(cursor_type) do |record, next_cursor|
|
|
239
|
+
records << record
|
|
240
|
+
cursor = next_cursor
|
|
241
|
+
end
|
|
242
|
+
expect(records.size).to eq 2
|
|
243
|
+
Feed::Item.order_by(sort_order).scroll(cursor) do |record, _next_cursor|
|
|
244
|
+
records << record
|
|
245
|
+
end
|
|
246
|
+
expect(records.size).to eq 3
|
|
247
|
+
expect(records).to eq Feed::Item.all.sort(_id: sort_order[:a_integer]).to_a
|
|
248
|
+
end
|
|
249
|
+
end
|
|
179
250
|
end
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
criteria.scroll do |record, _next_cursor|
|
|
192
|
-
records << record
|
|
251
|
+
context 'with several records having the same value' do
|
|
252
|
+
before :each do
|
|
253
|
+
3.times { Feed::Item.create! a_integer: 5 }
|
|
254
|
+
end
|
|
255
|
+
it 'returns records from the current one when Mongoid::Scroll::Cursor#include_current is true' do
|
|
256
|
+
_first_item, second_item, third_item = Feed::Item.asc(:a_integer).to_a
|
|
257
|
+
cursor = Mongoid::Scroll::Cursor.from_record(second_item, field: Feed::Item.fields['a_integer'])
|
|
258
|
+
cursor.include_current = true
|
|
259
|
+
items = Feed::Item.asc(:a_integer).limit(2).scroll(cursor).to_a
|
|
260
|
+
expect(items).to eq([second_item, third_item])
|
|
261
|
+
end
|
|
193
262
|
end
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
it "scrolls by #{sort_order}" do
|
|
211
|
-
records = []
|
|
212
|
-
cursor = nil
|
|
213
|
-
Feed::Item.order_by(sort_order).limit(2).scroll do |record, next_cursor|
|
|
214
|
-
records << record
|
|
215
|
-
cursor = next_cursor
|
|
216
|
-
end
|
|
217
|
-
expect(records.size).to eq 2
|
|
218
|
-
Feed::Item.order_by(sort_order).scroll(cursor) do |record, _next_cursor|
|
|
219
|
-
records << record
|
|
220
|
-
end
|
|
221
|
-
expect(records.size).to eq 3
|
|
222
|
-
expect(records).to eq Feed::Item.all.sort(_id: sort_order[:a_integer]).to_a
|
|
263
|
+
context 'with DateTime with a milisecond precision' do
|
|
264
|
+
let!(:item_1) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.1, 'UTC')) }
|
|
265
|
+
let!(:item_2) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.2, 'UTC')) }
|
|
266
|
+
let!(:item_3) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.3, 'UTC')) }
|
|
267
|
+
|
|
268
|
+
it 'doesn\'t lose the precision when rebuilding the cursor' do
|
|
269
|
+
records = []
|
|
270
|
+
cursor = nil
|
|
271
|
+
Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor_type) do |record, next_cursor|
|
|
272
|
+
records << record
|
|
273
|
+
cursor = next_cursor
|
|
274
|
+
end
|
|
275
|
+
expect(records).to eq [item_1, item_2]
|
|
276
|
+
cursor = cursor_type.new(cursor.to_s, field: Feed::Item.fields['a_datetime'])
|
|
277
|
+
expect(Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor).to_a).to eq([item_3])
|
|
278
|
+
end
|
|
223
279
|
end
|
|
224
280
|
end
|
|
225
281
|
end
|
|
@@ -17,22 +17,15 @@ describe Mongoid::Scroll::Cursor do
|
|
|
17
17
|
end
|
|
18
18
|
context 'an id field cursor' do
|
|
19
19
|
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
20
|
-
field_type = Mongoid::Compatibility::Version.mongoid3? ? Moped::BSON::ObjectId : BSON::ObjectId
|
|
21
20
|
subject do
|
|
22
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.id}:#{feed_item.id}", field_name: '_id', field_type:
|
|
21
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.id}:#{feed_item.id}", field_name: '_id', field_type: BSON::ObjectId, direction: 1
|
|
23
22
|
end
|
|
24
|
-
its(:value) { should eq feed_item.id
|
|
23
|
+
its(:value) { should eq feed_item.id }
|
|
25
24
|
its(:tiebreak_id) { should eq feed_item.id }
|
|
26
25
|
its(:criteria) do
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
])
|
|
31
|
-
else
|
|
32
|
-
should eq('$or' => [
|
|
33
|
-
{ '_id' => { '$gt' => BSON::ObjectId(feed_item.id.to_s) } }
|
|
34
|
-
])
|
|
35
|
-
end
|
|
26
|
+
should eq('$or' => [
|
|
27
|
+
{ '_id' => { '$gt' => BSON::ObjectId(feed_item.id.to_s) } }
|
|
28
|
+
])
|
|
36
29
|
end
|
|
37
30
|
end
|
|
38
31
|
context 'a string field cursor' do
|
|
@@ -66,11 +59,11 @@ describe Mongoid::Scroll::Cursor do
|
|
|
66
59
|
context 'a date/time field cursor' do
|
|
67
60
|
let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC')) }
|
|
68
61
|
subject do
|
|
69
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.utc.
|
|
62
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}", field_name: 'a_datetime', field_type: DateTime
|
|
70
63
|
end
|
|
71
64
|
its(:value) { should eq feed_item.a_datetime }
|
|
72
65
|
its(:tiebreak_id) { should eq feed_item.id }
|
|
73
|
-
its(:to_s) { should eq "#{feed_item.a_datetime.utc.
|
|
66
|
+
its(:to_s) { should eq "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}" }
|
|
74
67
|
its(:criteria) do
|
|
75
68
|
should eq('$or' => [
|
|
76
69
|
{ 'a_datetime' => { '$gt' => feed_item.a_datetime } },
|
|
@@ -96,11 +89,11 @@ describe Mongoid::Scroll::Cursor do
|
|
|
96
89
|
context 'a time field cursor' do
|
|
97
90
|
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
98
91
|
subject do
|
|
99
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.
|
|
92
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field_name: 'a_time', field_type: Time
|
|
100
93
|
end
|
|
101
94
|
its(:value) { should eq feed_item.a_time }
|
|
102
95
|
its(:tiebreak_id) { should eq feed_item.id }
|
|
103
|
-
its(:to_s) { should eq "#{feed_item.a_time.
|
|
96
|
+
its(:to_s) { should eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
|
|
104
97
|
its(:criteria) do
|
|
105
98
|
should eq('$or' => [
|
|
106
99
|
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
|
@@ -111,11 +104,11 @@ describe Mongoid::Scroll::Cursor do
|
|
|
111
104
|
context 'a time field cursor with a field option' do
|
|
112
105
|
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
|
113
106
|
subject do
|
|
114
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.
|
|
107
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field: Feed::Item.fields['a_time']
|
|
115
108
|
end
|
|
116
109
|
its(:value) { should eq feed_item.a_time }
|
|
117
110
|
its(:tiebreak_id) { should eq feed_item.id }
|
|
118
|
-
its(:to_s) { should eq "#{feed_item.a_time.
|
|
111
|
+
its(:to_s) { should eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
|
|
119
112
|
its(:criteria) do
|
|
120
113
|
should eq('$or' => [
|
|
121
114
|
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
|
@@ -138,4 +131,47 @@ describe Mongoid::Scroll::Cursor do
|
|
|
138
131
|
end.to raise_error ArgumentError
|
|
139
132
|
end
|
|
140
133
|
end
|
|
134
|
+
context 'a cursor with include_current set to true' do
|
|
135
|
+
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
|
136
|
+
subject do
|
|
137
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String, include_current: true
|
|
138
|
+
end
|
|
139
|
+
its(:value) { should eq 'astring' }
|
|
140
|
+
its(:tiebreak_id) { should eq feed_item.id }
|
|
141
|
+
its(:criteria) do
|
|
142
|
+
should eq('$or' => [
|
|
143
|
+
{ 'a_string' => { '$gt' => 'astring' } },
|
|
144
|
+
{ '_id' => { '$gte' => BSON::ObjectId(feed_item.id.to_s) }, 'a_string' => 'astring' }
|
|
145
|
+
])
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
context 'criteria' do
|
|
149
|
+
context 'with data' do
|
|
150
|
+
before :each do
|
|
151
|
+
3.times do |i|
|
|
152
|
+
Feed::Item.create!(
|
|
153
|
+
name: "Feed Item #{i}",
|
|
154
|
+
a_time: Time.new(2015, i + 1, 22, 1, 2, 3)
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
Feed::Item.create!(
|
|
158
|
+
name: 'Feed Item 3',
|
|
159
|
+
a_time: Time.new(2014, 2, 2, 1, 2, 3)
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
it 'merges cursor criteria when no sort field is given' do
|
|
163
|
+
criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
|
|
164
|
+
feed_item = Feed::Item.where(name: 'Feed Item 1').first
|
|
165
|
+
cursor_input = "#{feed_item.id}:#{feed_item.id}"
|
|
166
|
+
cursor_options = { field_type: BSON::ObjectId, field_name: '_id', direction: 1 }
|
|
167
|
+
cursor = Mongoid::Scroll::Cursor.new(cursor_input, cursor_options)
|
|
168
|
+
records = []
|
|
169
|
+
criteria.limit(2).scroll(cursor) do |record, next_cursor|
|
|
170
|
+
records << record
|
|
171
|
+
cursor = next_cursor
|
|
172
|
+
end
|
|
173
|
+
expect(records.size).to eq 2
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
141
177
|
end
|
data/spec/support/feed/item.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Feed
|
|
|
13
13
|
embeds_many :embedded_items, class_name: 'Feed::EmbeddedItem'
|
|
14
14
|
|
|
15
15
|
publisher_options = { class_name: 'Feed::Publisher' }
|
|
16
|
-
publisher_options[:optional] = true if Mongoid::Compatibility::Version.
|
|
16
|
+
publisher_options[:optional] = true if Mongoid::Compatibility::Version.mongoid6_or_newer?
|
|
17
17
|
belongs_to :publisher, publisher_options
|
|
18
18
|
end
|
|
19
19
|
end
|