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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +36 -0
  3. data/.github/workflows/danger.yml +17 -0
  4. data/.rubocop_todo.yml +24 -19
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +6 -5
  7. data/LICENSE.md +1 -1
  8. data/README.md +59 -46
  9. data/RELEASING.md +1 -1
  10. data/UPGRADING.md +14 -0
  11. data/examples/mongoid_scroll_feed.rb +2 -8
  12. data/lib/config/locales/en.yml +8 -0
  13. data/lib/mongo/scrollable.rb +8 -3
  14. data/lib/mongoid/criteria/scrollable/cursors.rb +18 -0
  15. data/lib/mongoid/criteria/scrollable/fields.rb +21 -0
  16. data/lib/mongoid/criteria/scrollable.rb +13 -12
  17. data/lib/mongoid/scroll/base64_encoded_cursor.rb +40 -0
  18. data/lib/mongoid/scroll/base_cursor.rb +123 -0
  19. data/lib/mongoid/scroll/cursor.rb +24 -87
  20. data/lib/mongoid/scroll/errors/base.rb +1 -1
  21. data/lib/mongoid/scroll/errors/invalid_base64_cursor_error.rb +11 -0
  22. data/lib/mongoid/scroll/errors/invalid_base_cursor_error.rb +8 -0
  23. data/lib/mongoid/scroll/errors/invalid_cursor_error.rb +1 -1
  24. data/lib/mongoid/scroll/errors/mismatched_sort_fields_error.rb +15 -0
  25. data/lib/mongoid/scroll/errors.rb +3 -0
  26. data/lib/mongoid/scroll/version.rb +1 -1
  27. data/lib/mongoid-scroll.rb +4 -1
  28. data/spec/mongo/collection_view_spec.rb +122 -105
  29. data/spec/mongoid/base64_encoded_cursor_spec.rb +233 -0
  30. data/spec/mongoid/criteria_spec.rb +236 -197
  31. data/spec/mongoid/{scroll_cursor_spec.rb → cursor_spec.rb} +48 -12
  32. data/spec/support/feed/item.rb +1 -1
  33. metadata +15 -8
  34. data/.travis.yml +0 -37
  35. data/examples/moped_scroll_feed.rb +0 -45
  36. data/lib/moped/scrollable.rb +0 -38
  37. data/spec/moped/query_spec.rb +0 -126
@@ -0,0 +1,123 @@
1
+ module Mongoid
2
+ module Scroll
3
+ class BaseCursor
4
+ attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction, :include_current
5
+
6
+ def initialize(value, options = {})
7
+ @value = value
8
+ @tiebreak_id = options[:tiebreak_id]
9
+ @field_type = options[:field_type]
10
+ @field_name = options[:field_name]
11
+ @direction = options[:direction] || 1
12
+ @include_current = options[:include_current] || false
13
+ end
14
+
15
+ def criteria
16
+ mongo_value = value.class.mongoize(value) if value
17
+ cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
18
+ tiebreak_criteria = { field_name => mongo_value, :_id => { tiebreak_compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
19
+ cursor_selector = if Mongoid::Compatibility::Version.mongoid6_or_newer?
20
+ Mongoid::Criteria::Queryable::Selector.new
21
+ else
22
+ Origin::Selector.new
23
+ end
24
+ cursor_selector['$or'] = [cursor_criteria, tiebreak_criteria].compact if cursor_criteria || tiebreak_criteria
25
+ cursor_selector.__evolve_object_id__
26
+ end
27
+
28
+ def sort_options
29
+ {
30
+ field_type: field_type,
31
+ field_name: field_name,
32
+ direction: direction
33
+ }
34
+ end
35
+
36
+ def to_s
37
+ raise NotImplementedError.new(:to_s)
38
+ end
39
+
40
+ class << self
41
+ def from_record(record, options)
42
+ cursor = new(nil, options)
43
+ record_value = record.respond_to?(cursor.field_name) ? record.send(cursor.field_name) : record[cursor.field_name]
44
+ cursor.value = cursor.send(:parse_field_value, cursor.field_type, cursor.field_name, record_value)
45
+ cursor.tiebreak_id = record['_id']
46
+ cursor
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def parse_field_value(field_type, field_name, value)
53
+ return nil unless value
54
+
55
+ case field_type.to_s
56
+ when 'BSON::ObjectId' then BSON::ObjectId.from_string(value)
57
+ when 'String' then value.to_s == '' ? nil : value.to_s
58
+ when 'DateTime' then value.is_a?(DateTime) ? value : Time.at(value.to_i).to_datetime
59
+ when 'Time' then value.is_a?(Time) ? value : Time.at(value.to_i)
60
+ when 'Date' then value.is_a?(Date) ? value : Time.at(value.to_i).utc.to_date
61
+ when 'Float' then value.to_f
62
+ when 'Integer' then value.to_i
63
+ else
64
+ raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
65
+ end
66
+ end
67
+
68
+ def transform_field_value(field_type, field_name, value)
69
+ return nil unless value
70
+
71
+ case field_type.to_s
72
+ when 'BSON::ObjectId' then value.to_s
73
+ when 'String' then value.to_s
74
+ when 'Date' then Time.utc(value.year, value.month, value.day).to_i
75
+ when 'DateTime', 'Time' then value.utc.to_i
76
+ when 'Float' then value.to_f
77
+ when 'Integer' then value.to_i
78
+ else
79
+ raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
80
+ end
81
+ end
82
+
83
+ def extract_field_options(options)
84
+ if options && (field_name = options[:field_name]) && (field_type = options[:field_type])
85
+ {
86
+ field_type: field_type.to_s,
87
+ field_name: field_name.to_s,
88
+ direction: options[:direction] || 1,
89
+ include_current: options[:include_current] || false
90
+ }
91
+ elsif options && (field = options[:field])
92
+ {
93
+ field_type: field.type.to_s,
94
+ field_name: field.name.to_s,
95
+ direction: options[:direction] || 1,
96
+ include_current: options[:include_current] || false
97
+ }
98
+ end
99
+ end
100
+
101
+ def compare_direction
102
+ direction == 1 ? '$gt' : '$lt'
103
+ end
104
+
105
+ def tiebreak_compare_direction
106
+ if include_current
107
+ case compare_direction
108
+ when '$gt'
109
+ '$gte'
110
+ when '$lt'
111
+ '$lte'
112
+ end
113
+ else
114
+ compare_direction
115
+ end
116
+ end
117
+
118
+ def parse(_value)
119
+ raise NotImplementedError.new(:parse)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,97 +1,34 @@
1
1
  module Mongoid
2
2
  module Scroll
3
- class Cursor
4
- attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction
5
-
3
+ class Cursor < BaseCursor
6
4
  def initialize(value = nil, options = {})
7
- @field_type, @field_name = Mongoid::Scroll::Cursor.extract_field_options(options)
8
- @direction = options[:direction] || 1
9
- parse(value)
10
- end
11
-
12
- def criteria
13
- mongo_value = value.class.mongoize(value) if value
14
- compare_direction = direction == 1 ? '$gt' : '$lt'
15
- cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
16
- tiebreak_criteria = { field_name => mongo_value, :_id => { compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
17
- cursor_selector = if Mongoid::Compatibility::Version.mongoid6? || Mongoid::Compatibility::Version.mongoid7?
18
- Mongoid::Criteria::Queryable::Selector.new
19
- else
20
- Origin::Selector.new
21
- end
22
- cursor_selector['$or'] = [cursor_criteria, tiebreak_criteria].compact if cursor_criteria || tiebreak_criteria
23
- cursor_selector.__evolve_object_id__
24
- end
25
-
26
- class << self
27
- def from_record(record, options)
28
- cursor = Mongoid::Scroll::Cursor.new(nil, options)
29
- value = record.respond_to?(cursor.field_name) ? record.send(cursor.field_name) : record[cursor.field_name]
30
- cursor.value = Mongoid::Scroll::Cursor.parse_field_value(cursor.field_type, cursor.field_name, value)
31
- cursor.tiebreak_id = record['_id']
32
- cursor
5
+ options = extract_field_options(options)
6
+ raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].' unless options
7
+ if value
8
+ parts = value.split(':') if value
9
+ unless parts && parts.length >= 2
10
+ raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
11
+ end
12
+ value = parse_field_value(
13
+ options[:field_type],
14
+ options[:field_name],
15
+ parts[0...-1].join(':')
16
+ )
17
+ options[:tiebreak_id] = BSON::ObjectId.from_string(parts[-1])
18
+ super value, options
19
+ else
20
+ super nil, options
33
21
  end
34
22
  end
35
23
 
36
24
  def to_s
37
- tiebreak_id ? [Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, value), tiebreak_id].join(':') : nil
38
- end
39
-
40
- private
41
-
42
- def parse(value)
43
- return unless value
44
- parts = value.split(':')
45
- unless parts.length >= 2
46
- raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
47
- end
48
- id = parts[-1]
49
- value = parts[0...-1].join(':')
50
- @value = Mongoid::Scroll::Cursor.parse_field_value(field_type, field_name, value)
51
- @tiebreak_id = if Mongoid::Compatibility::Version.mongoid3?
52
- Moped::BSON::ObjectId(id)
53
- else
54
- BSON::ObjectId.from_string(id)
55
- end
56
- end
57
-
58
- class << self
59
- def extract_field_options(options)
60
- if options && (field_name = options[:field_name]) && (field_type = options[:field_type])
61
- [field_type.to_s, field_name.to_s]
62
- elsif options && (field = options[:field])
63
- [field.type.to_s, field.name.to_s]
64
- else
65
- raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].'
66
- end
67
- end
68
-
69
- def parse_field_value(field_type, field_name, value)
70
- case field_type.to_s
71
- when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
72
- when 'String' then value.to_s
73
- when 'DateTime' then value.is_a?(DateTime) ? value : Time.at(value.to_i).to_datetime
74
- when 'Time' then value.is_a?(Time) ? value : Time.at(value.to_i)
75
- when 'Date' then value.is_a?(Date) ? value : Time.at(value.to_i).utc.to_date
76
- when 'Float' then value.to_f
77
- when 'Integer' then value.to_i
78
- else
79
- raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
80
- end
81
- end
82
-
83
- def transform_field_value(field_type, field_name, value)
84
- case field_type.to_s
85
- when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
86
- when 'String' then value.to_s
87
- when 'Date' then Time.utc(value.year, value.month, value.day).to_i
88
- when 'DateTime', 'Time' then value.utc.to_i
89
- when 'Float' then value.to_f
90
- when 'Integer' then value.to_i
91
- else
92
- raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
93
- end
94
- end
25
+ tiebreak_id ? [
26
+ transform_field_value(
27
+ field_type,
28
+ field_name,
29
+ value
30
+ ), tiebreak_id
31
+ ].join(':') : nil
95
32
  end
96
33
  end
97
34
  end
@@ -38,7 +38,7 @@ module Mongoid
38
38
  #
39
39
  # Returns a localized error message string.
40
40
  def translate(key, options)
41
- ::I18n.translate("#{BASE_KEY}.#{key}", { locale: :en }.merge(options)).strip
41
+ ::I18n.translate("#{BASE_KEY}.#{key}", **{ locale: :en }.merge(options)).strip
42
42
  end
43
43
 
44
44
  # Create the problem.
@@ -0,0 +1,11 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ class InvalidBase64CursorError < InvalidBaseCursorError
5
+ def initialize(opts = {})
6
+ super(compose_message('invalid_base64_cursor', opts))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ class InvalidBaseCursorError < Mongoid::Scroll::Errors::Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module Scroll
3
3
  module Errors
4
- class InvalidCursorError < Mongoid::Scroll::Errors::Base
4
+ class InvalidCursorError < InvalidBaseCursorError
5
5
  def initialize(opts = {})
6
6
  super(compose_message('invalid_cursor', opts))
7
7
  end
@@ -0,0 +1,15 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ # Raised when the original sort params and the cursor sort params are different
5
+ class MismatchedSortFieldsError < Mongoid::Scroll::Errors::Base
6
+ def initialize(opts = {})
7
+ if opts[:diff] && opts[:diff].is_a?(Hash)
8
+ opts = opts.merge(diff: opts[:diff].keys.join(', '))
9
+ end
10
+ super(compose_message('mismatched_sort_fields', opts))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,8 @@
1
1
  require 'mongoid/scroll/errors/base'
2
2
  require 'mongoid/scroll/errors/multiple_sort_fields_error'
3
+ require 'mongoid/scroll/errors/mismatched_sort_fields_error'
4
+ require 'mongoid/scroll/errors/invalid_base_cursor_error'
3
5
  require 'mongoid/scroll/errors/invalid_cursor_error'
6
+ require 'mongoid/scroll/errors/invalid_base64_cursor_error'
4
7
  require 'mongoid/scroll/errors/no_such_field_error'
5
8
  require 'mongoid/scroll/errors/unsupported_field_type_error'
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Scroll
3
- VERSION = '0.3.7'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
5
5
  end
@@ -6,7 +6,10 @@ require 'mongoid'
6
6
  require 'mongoid-compatibility'
7
7
  require 'mongoid/scroll/version'
8
8
  require 'mongoid/scroll/errors'
9
+ require 'mongoid/scroll/base_cursor'
9
10
  require 'mongoid/scroll/cursor'
10
- require 'moped/scrollable' if Object.const_defined?(:Moped)
11
+ require 'mongoid/scroll/base64_encoded_cursor'
12
+ require 'mongoid/criteria/scrollable/fields'
13
+ require 'mongoid/criteria/scrollable/cursors'
11
14
  require 'mongo/scrollable' if Object.const_defined?(:Mongo)
12
15
  require 'mongoid/criteria/scrollable'
@@ -2,123 +2,140 @@ require 'spec_helper'
2
2
 
3
3
  if Object.const_defined?(:Mongo)
4
4
  describe Mongo::Collection::View do
5
- context 'scrollable' do
6
- subject do
7
- Mongoid.default_client['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_client['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_client['feed_items'].find
25
- end
26
- it 'adds a default sort by _id' do
27
- expect(subject.scroll.sort).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_client['feed_items'].insert_one(
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
- )
5
+ [Mongoid::Scroll::Cursor, Mongoid::Scroll::Base64EncodedCursor].each do |cursor_type|
6
+ context cursor_type do
7
+ context 'scrollable' do
8
+ subject do
9
+ Mongoid.default_client['feed_items'].find
10
+ end
11
+ it ':scroll' do
12
+ expect(subject).to respond_to(:scroll)
13
+ end
40
14
  end
41
- end
42
- context 'default' do
43
- it 'scrolls all' do
44
- records = []
45
- Mongoid.default_client['feed_items'].find.scroll do |record, _next_cursor|
46
- records << record
15
+ context 'with multiple sort fields' do
16
+ subject do
17
+ Mongoid.default_client['feed_items'].find.sort(name: 1, value: -1)
18
+ end
19
+ it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
20
+ expect { subject.scroll(cursor_type) }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
21
+ /You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
47
22
  end
48
- expect(records.size).to eq 10
49
- expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
50
23
  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_client['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_client['feed_items'].find.to_a
24
+ context 'with different sort fields between the cursor and the criteria' do
25
+ subject do
26
+ Mongoid.default_client['feed_items'].find.sort(name: -1)
61
27
  end
62
- it 'scrolls all with a break' do
63
- records = []
64
- cursor = nil
65
- Mongoid.default_client['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_client['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
71
- records << record
72
- cursor = next_cursor
28
+
29
+ it 'raises Mongoid::Scroll::Errors::MismatchedSortFieldsError' do
30
+ record = Feed::Item.create!
31
+ cursor = cursor_type.from_record(record, field: record.fields['a_string'])
32
+ expect(cursor).to be_a cursor_type
33
+ 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./
34
+ expect { subject.scroll(cursor, field_type: String) }.to raise_error Mongoid::Scroll::Errors::MismatchedSortFieldsError, error_string
35
+ end
36
+ end
37
+ context 'with no sort' do
38
+ subject do
39
+ Mongoid.default_client['feed_items'].find
40
+ end
41
+ it 'adds a default sort by _id' do
42
+ expect(subject.scroll(cursor_type).sort).to eq('_id' => 1)
43
+ end
44
+ end
45
+ context 'with data' do
46
+ before :each do
47
+ 10.times do |i|
48
+ Mongoid.default_client['feed_items'].insert_one(
49
+ a_string: i.to_s,
50
+ a_integer: i,
51
+ a_datetime: DateTime.mongoize(DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC')),
52
+ a_date: Date.mongoize(Date.new(2013, i + 1, 21)),
53
+ a_time: Time.mongoize(Time.at(Time.now.to_i + i))
54
+ )
73
55
  end
74
- expect(records.size).to eq 10
75
- expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
76
56
  end
77
- it 'scrolls in descending order' do
78
- records = []
79
- Mongoid.default_client['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
57
+ context 'default' do
58
+ it 'scrolls all' do
59
+ records = []
60
+ Mongoid.default_client['feed_items'].find.scroll(cursor_type) do |record, _next_cursor|
61
+ records << record
62
+ end
63
+ expect(records.size).to eq 10
64
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
81
65
  end
82
- expect(records.size).to eq 3
83
- expect(records).to eq Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).to_a
84
66
  end
85
- it 'map' do
86
- record = Mongoid.default_client['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
- ]
67
+ { a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
68
+ context field_type do
69
+ it 'scrolls all with a block' do
70
+ records = []
71
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(cursor_type, field_type: field_type) do |record, _next_cursor|
72
+ records << record
73
+ end
74
+ expect(records.size).to eq 10
75
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
76
+ end
77
+ it 'scrolls all with a break' do
78
+ records = []
79
+ cursor = nil
80
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).limit(5).scroll(cursor_type, field_type: field_type) do |record, next_cursor|
81
+ records << record
82
+ cursor = next_cursor
83
+ expect(cursor).to be_a cursor_type
84
+ end
85
+ expect(records.size).to eq 5
86
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
87
+ records << record
88
+ cursor = next_cursor
89
+ expect(cursor).to be_a cursor_type
90
+ end
91
+ expect(records.size).to eq 10
92
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
93
+ end
94
+ it 'scrolls in descending order' do
95
+ records = []
96
+ Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).scroll(cursor_type, field_type: field_type, field_name: field_name) do |record, _next_cursor|
97
+ records << record
98
+ end
99
+ expect(records.size).to eq 3
100
+ expect(records).to eq Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).to_a
101
+ end
102
+ it 'map' do
103
+ record = Mongoid.default_client['feed_items'].find.limit(3).scroll(cursor_type, field_type: field_type, field_name: field_name).map { |r| r }.last
104
+ cursor = cursor_type.from_record(record, field_type: field_type, field_name: field_name)
105
+ expect(cursor).to_not be nil
106
+ expect(cursor.value).to eq record[field_name.to_s]
107
+ expect(cursor.tiebreak_id).to eq record['_id']
108
+ end
109
+ end
93
110
  end
94
111
  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_client['feed_items'].find.sort(sort_order).limit(2).scroll do |record, next_cursor|
113
- records << record
114
- cursor = next_cursor
112
+ context 'with overlapping data', if: MongoDB.mmapv1? do
113
+ before :each do
114
+ 3.times { Feed::Item.create! a_integer: 5 }
115
+ Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
116
+ end
117
+ it 'natural order is different from order by id' do
118
+ # natural order isn't necessarily going to be the same as _id order
119
+ # if a document is updated and grows in size, it may need to be relocated and
120
+ # thus cause the natural order to change
121
+ expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq Feed::Item.order_by(_id: 1).to_a
115
122
  end
116
- expect(records.size).to eq 2
117
- Mongoid.default_client['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
118
- records << record
123
+ [{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
124
+ it "scrolls by #{sort_order}" do
125
+ records = []
126
+ cursor = nil
127
+ Mongoid.default_client['feed_items'].find.sort(sort_order).limit(2).scroll(cursor_type) do |record, next_cursor|
128
+ records << record
129
+ cursor = next_cursor
130
+ end
131
+ expect(records.size).to eq 2
132
+ Mongoid.default_client['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
133
+ records << record
134
+ end
135
+ expect(records.size).to eq 3
136
+ expect(records).to eq Mongoid.default_client['feed_items'].find.sort(sort_order.merge(_id: sort_order[:a_integer])).to_a
137
+ end
119
138
  end
120
- expect(records.size).to eq 3
121
- expect(records).to eq Mongoid.default_client['feed_items'].find.sort(sort_order.merge(_id: sort_order[:a_integer])).to_a
122
139
  end
123
140
  end
124
141
  end