mongoid-scroll 0.3.7 → 1.0.0
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 +10 -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 +236 -197
- data/spec/mongoid/{scroll_cursor_spec.rb → cursor_spec.rb} +48 -12
- 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,45 +0,0 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
Bundler.setup(:default, :development)
|
3
|
-
|
4
|
-
require 'mongoid-scroll'
|
5
|
-
require 'faker'
|
6
|
-
|
7
|
-
Mongoid.connect_to 'mongoid_scroll_demo'
|
8
|
-
Mongoid.purge!
|
9
|
-
|
10
|
-
raise 'No Moped' unless Object.const_defined?(:Moped)
|
11
|
-
|
12
|
-
# total items to insert
|
13
|
-
total_items = 20
|
14
|
-
# a MongoDB query will be executed every scroll_by items
|
15
|
-
scroll_by = 7
|
16
|
-
|
17
|
-
# insert items with a position out-of-order
|
18
|
-
rands = (0..total_items).to_a.sort { rand }[0..total_items]
|
19
|
-
total_items.times do
|
20
|
-
Mongoid.default_session['feed_items'].insert(title: Faker::Lorem.sentence, position: rands.pop)
|
21
|
-
end
|
22
|
-
|
23
|
-
Mongoid.default_session['feed_items'].indexes.create(position: 1, _id: 1)
|
24
|
-
|
25
|
-
Moped.logger = Logger.new($stdout)
|
26
|
-
Moped.logger.level = Logger::DEBUG
|
27
|
-
|
28
|
-
total_shown = 0
|
29
|
-
next_cursor = nil
|
30
|
-
loop do
|
31
|
-
current_cursor = next_cursor
|
32
|
-
next_cursor = nil
|
33
|
-
Mongoid.default_session['feed_items'].find.limit(scroll_by).sort(position: 1).scroll(current_cursor, field_type: Integer, field_name: 'position') do |item, cursor|
|
34
|
-
puts "#{item['position']}: #{item['title']}"
|
35
|
-
next_cursor = cursor
|
36
|
-
total_shown += 1
|
37
|
-
end
|
38
|
-
break unless next_cursor
|
39
|
-
# destroy an item just for the heck of it, scroll is not affected
|
40
|
-
item = Mongoid.default_session['feed_items'].find.sort(position: 1).first
|
41
|
-
Mongoid.default_session['feed_items'].find(_id: item['_id']).remove
|
42
|
-
end
|
43
|
-
|
44
|
-
# this will be 20
|
45
|
-
puts "Shown #{total_shown} items."
|
data/lib/moped/scrollable.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
module Moped
|
2
|
-
module Scrollable
|
3
|
-
def scroll(cursor = nil, options = nil, &_block)
|
4
|
-
unless options
|
5
|
-
bson_type = Mongoid::Compatibility::Version.mongoid3? ? Moped::BSON::ObjectId : BSON::ObjectId
|
6
|
-
options = { field_type: bson_type }
|
7
|
-
end
|
8
|
-
query = Query.new(collection, operation.selector.dup)
|
9
|
-
query.operation.skip = operation.skip
|
10
|
-
query.operation.limit = operation.limit
|
11
|
-
# we don't support scrolling over a criteria with multiple fields
|
12
|
-
if query.operation.selector['$orderby'] && query.operation.selector['$orderby'].keys.size != 1
|
13
|
-
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: query.operation.selector['$orderby'])
|
14
|
-
elsif !query.operation.selector.key?('$orderby') || query.operation.selector['$orderby'].empty?
|
15
|
-
# introduce a default sort order if there's none
|
16
|
-
query.sort(_id: 1)
|
17
|
-
end
|
18
|
-
# scroll field and direction
|
19
|
-
scroll_field = query.operation.selector['$orderby'].keys.first
|
20
|
-
scroll_direction = query.operation.selector['$orderby'].values.first.to_i
|
21
|
-
# scroll cursor from the parameter, with value and tiebreak_id
|
22
|
-
cursor_options = { field_name: scroll_field, field_type: options[:field_type], direction: scroll_direction }
|
23
|
-
cursor = cursor.is_a?(Mongoid::Scroll::Cursor) ? cursor : Mongoid::Scroll::Cursor.new(cursor, cursor_options)
|
24
|
-
query.operation.selector['$query'] = query.operation.selector['$query'].merge(cursor.criteria)
|
25
|
-
query.operation.selector['$orderby'] = query.operation.selector['$orderby'].merge(_id: scroll_direction)
|
26
|
-
# scroll
|
27
|
-
if block_given?
|
28
|
-
query.each do |record|
|
29
|
-
yield record, Mongoid::Scroll::Cursor.from_record(record, cursor_options)
|
30
|
-
end
|
31
|
-
else
|
32
|
-
query
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
Moped::Query.send(:include, Moped::Scrollable)
|
data/spec/moped/query_spec.rb
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
if Object.const_defined?(:Moped)
|
4
|
-
describe Moped::Query do
|
5
|
-
context 'scrollable' do
|
6
|
-
subject do
|
7
|
-
Mongoid.default_session['feed_items'].find
|
8
|
-
end
|
9
|
-
it ':scroll' do
|
10
|
-
expect(subject).to respond_to(:scroll)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
context 'with multiple sort fields' do
|
14
|
-
subject do
|
15
|
-
Mongoid.default_session['feed_items'].find.sort(name: 1, value: -1)
|
16
|
-
end
|
17
|
-
it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
|
18
|
-
expect { subject.scroll }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
|
19
|
-
/You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
|
20
|
-
end
|
21
|
-
end
|
22
|
-
context 'with no sort' do
|
23
|
-
subject do
|
24
|
-
Mongoid.default_session['feed_items'].find
|
25
|
-
end
|
26
|
-
it 'adds a default sort by _id' do
|
27
|
-
expect(subject.scroll.operation.selector['$orderby']).to eq(_id: 1)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
context 'with data' do
|
31
|
-
before :each do
|
32
|
-
10.times do |i|
|
33
|
-
Mongoid.default_session['feed_items'].insert(
|
34
|
-
a_string: i.to_s,
|
35
|
-
a_integer: i,
|
36
|
-
a_datetime: DateTime.mongoize(DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC')),
|
37
|
-
a_date: Date.mongoize(Date.new(2013, i + 1, 21)),
|
38
|
-
a_time: Time.mongoize(Time.at(Time.now.to_i + i))
|
39
|
-
)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
context 'default' do
|
43
|
-
it 'scrolls all' do
|
44
|
-
records = []
|
45
|
-
Mongoid.default_session['feed_items'].find.scroll do |record, _next_cursor|
|
46
|
-
records << record
|
47
|
-
end
|
48
|
-
expect(records.size).to eq 10
|
49
|
-
expect(records).to eq Mongoid.default_session['feed_items'].find.to_a
|
50
|
-
end
|
51
|
-
end
|
52
|
-
{ a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
|
53
|
-
context field_type do
|
54
|
-
it 'scrolls all with a block' do
|
55
|
-
records = []
|
56
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(nil, field_type: field_type) do |record, _next_cursor|
|
57
|
-
records << record
|
58
|
-
end
|
59
|
-
expect(records.size).to eq 10
|
60
|
-
expect(records).to eq Mongoid.default_session['feed_items'].find.to_a
|
61
|
-
end
|
62
|
-
it 'scrolls all with a break' do
|
63
|
-
records = []
|
64
|
-
cursor = nil
|
65
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).limit(5).scroll(nil, field_type: field_type) do |record, next_cursor|
|
66
|
-
records << record
|
67
|
-
cursor = next_cursor
|
68
|
-
end
|
69
|
-
expect(records.size).to eq 5
|
70
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
|
71
|
-
records << record
|
72
|
-
cursor = next_cursor
|
73
|
-
end
|
74
|
-
expect(records.size).to eq 10
|
75
|
-
expect(records).to eq Mongoid.default_session['feed_items'].find.to_a
|
76
|
-
end
|
77
|
-
it 'scrolls in descending order' do
|
78
|
-
records = []
|
79
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => -1).limit(3).scroll(nil, field_type: field_type, field_name: field_name) do |record, _next_cursor|
|
80
|
-
records << record
|
81
|
-
end
|
82
|
-
expect(records.size).to eq 3
|
83
|
-
expect(records).to eq Mongoid.default_session['feed_items'].find.sort(field_name => -1).limit(3).to_a
|
84
|
-
end
|
85
|
-
it 'map' do
|
86
|
-
record = Mongoid.default_session['feed_items'].find.limit(3).scroll(nil, field_type: field_type, field_name: field_name).map { |r| r }.last
|
87
|
-
cursor = Mongoid::Scroll::Cursor.from_record(record, field_type: field_type, field_name: field_name)
|
88
|
-
expect(cursor).to_not be nil
|
89
|
-
expect(cursor.to_s.split(':')).to eq [
|
90
|
-
Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, record[field_name.to_s]).to_s,
|
91
|
-
record['_id'].to_s
|
92
|
-
]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
context 'with overlapping data', if: MongoDB.mmapv1? do
|
98
|
-
before :each do
|
99
|
-
3.times { Feed::Item.create! a_integer: 5 }
|
100
|
-
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
101
|
-
end
|
102
|
-
it 'natural order is different from order by id' do
|
103
|
-
# natural order isn't necessarily going to be the same as _id order
|
104
|
-
# if a document is updated and grows in size, it may need to be relocated and
|
105
|
-
# thus cause the natural order to change
|
106
|
-
expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq Feed::Item.order_by(_id: 1).to_a
|
107
|
-
end
|
108
|
-
[{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
109
|
-
it "scrolls by #{sort_order}" do
|
110
|
-
records = []
|
111
|
-
cursor = nil
|
112
|
-
Mongoid.default_session['feed_items'].find.sort(sort_order).limit(2).scroll do |record, next_cursor|
|
113
|
-
records << record
|
114
|
-
cursor = next_cursor
|
115
|
-
end
|
116
|
-
expect(records.size).to eq 2
|
117
|
-
Mongoid.default_session['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
|
118
|
-
records << record
|
119
|
-
end
|
120
|
-
expect(records.size).to eq 3
|
121
|
-
expect(records).to eq Mongoid.default_session['feed_items'].find.sort(sort_order.merge(_id: sort_order[:a_integer])).to_a
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|