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
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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 ? [
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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,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'
|
data/lib/mongoid-scroll.rb
CHANGED
@@ -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 '
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Mongoid
|
46
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
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
|