mongoid-scroll 0.2.1 → 0.3.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 +7 -0
- data/.rubocop.yml +64 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +9 -3
- data/Gemfile +11 -1
- data/README.md +1 -1
- data/Rakefile +5 -2
- data/examples/mongoid_scroll_feed.rb +4 -4
- data/examples/moped_scroll_feed.rb +5 -5
- data/lib/mongoid/criterion/scrollable.rb +2 -4
- data/lib/mongoid/scroll/cursor.rb +49 -49
- data/lib/mongoid/scroll/errors/base.rb +44 -46
- data/lib/mongoid/scroll/errors/invalid_cursor_error.rb +1 -3
- data/lib/mongoid/scroll/errors/multiple_sort_fields_error.rb +2 -4
- data/lib/mongoid/scroll/errors/no_such_field_error.rb +1 -3
- data/lib/mongoid/scroll/errors/unsupported_field_type_error.rb +1 -3
- data/lib/mongoid/scroll/mongoid.rb +7 -0
- data/lib/mongoid/scroll/version.rb +1 -1
- data/lib/mongoid-scroll.rb +2 -1
- data/lib/mongoid_scroll.rb +0 -1
- data/lib/moped/scrollable.rb +13 -10
- data/mongoid-scroll.gemspec +11 -13
- data/spec/mongoid/criteria_spec.rb +26 -26
- data/spec/mongoid/scroll_cursor_spec.rb +50 -50
- data/spec/mongoid/scroll_spec.rb +0 -1
- data/spec/moped/query_spec.rb +30 -30
- data/spec/spec_helper.rb +0 -1
- data/spec/support/feed/item.rb +1 -0
- metadata +13 -20
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 614ee5395d9475c74547769cefbfd7c531b09d45
|
4
|
+
data.tar.gz: 9a61c61ce360c352a4b69fd770eca27f1ee9a6c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05e6f26d88ad8515bb3d0871bed4ea5386d3fb801300394ffedbdb4529833b2fb19ed73da362267e35cba02fba6b11c74e4d8ca4bfecb93cb389bc2408967c84
|
7
|
+
data.tar.gz: 3511a0273d3c198987a5812cfb137083216373e6602d03cbfe828ad0fa8c65d4fc577c20598bb1a8239c480d4f891d2179fa89e382b942347e01a063c4bd18a1
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- vendor/**/*
|
4
|
+
- gemfiles/vendor/**/*
|
5
|
+
|
6
|
+
LineLength:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
MethodLength:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
ClassLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Documentation:
|
16
|
+
# don't require classes to be documented
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
CollectionMethods:
|
20
|
+
# don't prefer map to collect, recuce to inject
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Encoding:
|
24
|
+
# no need to always specify encoding
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Void:
|
28
|
+
# == operator used in void context in specs
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
SignalException:
|
32
|
+
# prefer raise to fail
|
33
|
+
EnforcedStyle: only_raise
|
34
|
+
|
35
|
+
RaiseArgs:
|
36
|
+
# don't care for what kind of raise
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
PerlBackrefs:
|
40
|
+
# TODO: regular expression matching with $1, $2, etc.
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
BlockNesting:
|
44
|
+
# TODO: fix too much nesting
|
45
|
+
Max: 4
|
46
|
+
|
47
|
+
Lambda:
|
48
|
+
# TODO: replace all lambda with -> or Proc
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
Blocks:
|
52
|
+
# allow multi-line blocks like expect { }
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
WordArray:
|
56
|
+
# %w vs. [ '', ... ]
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
CyclomaticComplexity:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
FileName:
|
63
|
+
Enabled: false
|
64
|
+
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,16 +1,22 @@
|
|
1
|
+
0.3.0 (1/7/2014)
|
2
|
+
----------------
|
3
|
+
|
4
|
+
* Compatibility with Mongoid 4.x - [@dblock](https://github.com/dblock).
|
5
|
+
* Implemeneted Rubocop, Ruby linter - [@dblock](https://github.com/dblock).
|
6
|
+
|
1
7
|
0.2.1 (3/21/2013)
|
2
|
-
|
8
|
+
-----------------
|
3
9
|
|
4
10
|
* Fix: scroll over a collection that has duplicate values while data is being modified in a way that causes a change in the natural sort order - [@dblock](https://github.com/dblock).
|
5
11
|
|
6
12
|
0.2.0 (3/14/2013)
|
7
|
-
|
13
|
+
-----------------
|
8
14
|
|
9
15
|
* Extended `Moped::Query` with `scroll` - [@dblock](https://github.com/dblock).
|
10
16
|
* `Mongoid::Scroll::Cursor.from_record` can now be called with either a Mongoid field or `field_type` and `field_name` in the `options` hash - [@dblock](https://github.com/dblock).
|
11
17
|
|
12
18
|
0.1.0 (2/14/2013)
|
13
|
-
|
19
|
+
-----------------
|
14
20
|
|
15
21
|
* Initial public release, extends `Mongoid::Criteria` with `scroll` - [@dblock](https://github.com/dblock).
|
16
22
|
|
data/Gemfile
CHANGED
@@ -2,9 +2,19 @@ source "http://rubygems.org"
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
+
case version = ENV['MONGOID_VERSION'] || '~> 4.0'
|
6
|
+
when /4/
|
7
|
+
gem 'mongoid', '~> 4.0'
|
8
|
+
when /3/
|
9
|
+
gem 'mongoid', '~> 3.1'
|
10
|
+
else
|
11
|
+
gem 'mongoid', version
|
12
|
+
end
|
13
|
+
|
5
14
|
group :development, :test do
|
6
15
|
gem "rake"
|
7
16
|
gem "bundler"
|
8
|
-
gem "rspec"
|
17
|
+
gem "rspec", "~> 2.9"
|
9
18
|
gem "faker"
|
19
|
+
gem "rubocop", "0.24.0"
|
10
20
|
end
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Mongoid extension that enables infinite scrolling for `Mongoid::Criteria` and `M
|
|
6
6
|
Demo
|
7
7
|
----
|
8
8
|
|
9
|
-
Check out [artsy.net](http://artsy.net)
|
9
|
+
Check out [shows on artsy.net](http://artsy.net/shows). Keep scrolling down.
|
10
10
|
|
11
11
|
There're also two code samples for Mongoid and Moped in [examples](examples). Run `bundle exec ruby examples/mongoid_scroll_feed.rb`.
|
12
12
|
|
data/Rakefile
CHANGED
@@ -7,7 +7,7 @@ begin
|
|
7
7
|
Bundler.setup(:default, :development)
|
8
8
|
rescue Bundler::BundlerError => e
|
9
9
|
$stderr.puts e.message
|
10
|
-
$stderr.puts
|
10
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
11
11
|
exit e.status_code
|
12
12
|
end
|
13
13
|
|
@@ -20,5 +20,8 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
20
20
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
21
21
|
end
|
22
22
|
|
23
|
-
task :
|
23
|
+
task :spec
|
24
|
+
require 'rubocop/rake_task'
|
25
|
+
RuboCop::RakeTask.new(:rubocop)
|
24
26
|
|
27
|
+
task default: [:rubocop, :spec]
|
@@ -4,7 +4,7 @@ Bundler.setup(:default, :development)
|
|
4
4
|
require 'mongoid-scroll'
|
5
5
|
require 'faker'
|
6
6
|
|
7
|
-
Mongoid.connect_to
|
7
|
+
Mongoid.connect_to 'mongoid_scroll_demo'
|
8
8
|
Mongoid.purge!
|
9
9
|
|
10
10
|
module Feed
|
@@ -12,7 +12,7 @@ module Feed
|
|
12
12
|
include Mongoid::Document
|
13
13
|
field :title, type: String
|
14
14
|
field :position, type: Integer
|
15
|
-
index(
|
15
|
+
index(position: 1, _id: 1)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -23,7 +23,7 @@ scroll_by = 7
|
|
23
23
|
|
24
24
|
# insert items with a position out-of-order
|
25
25
|
rands = (0..total_items).to_a.sort { rand }[0..total_items]
|
26
|
-
total_items.times do |
|
26
|
+
total_items.times do |_i|
|
27
27
|
Feed::Item.create! title: Faker::Lorem.sentence, position: rands.pop
|
28
28
|
end
|
29
29
|
|
@@ -34,7 +34,7 @@ Feed::Item.create_indexes
|
|
34
34
|
|
35
35
|
total_shown = 0
|
36
36
|
next_cursor = nil
|
37
|
-
|
37
|
+
loop do
|
38
38
|
current_cursor = next_cursor
|
39
39
|
next_cursor = nil
|
40
40
|
Feed::Item.asc(:position).limit(scroll_by).scroll(current_cursor) do |item, cursor|
|
@@ -4,7 +4,7 @@ Bundler.setup(:default, :development)
|
|
4
4
|
require 'mongoid-scroll'
|
5
5
|
require 'faker'
|
6
6
|
|
7
|
-
Mongoid.connect_to
|
7
|
+
Mongoid.connect_to 'mongoid_scroll_demo'
|
8
8
|
Mongoid.purge!
|
9
9
|
|
10
10
|
# total items to insert
|
@@ -14,7 +14,7 @@ scroll_by = 7
|
|
14
14
|
|
15
15
|
# insert items with a position out-of-order
|
16
16
|
rands = (0..total_items).to_a.sort { rand }[0..total_items]
|
17
|
-
total_items.times do
|
17
|
+
total_items.times do
|
18
18
|
Mongoid.default_session['feed_items'].insert(title: Faker::Lorem.sentence, position: rands.pop)
|
19
19
|
end
|
20
20
|
|
@@ -25,10 +25,10 @@ Moped.logger.level = Logger::DEBUG
|
|
25
25
|
|
26
26
|
total_shown = 0
|
27
27
|
next_cursor = nil
|
28
|
-
|
28
|
+
loop do
|
29
29
|
current_cursor = next_cursor
|
30
30
|
next_cursor = nil
|
31
|
-
Mongoid.default_session['feed_items'].find.limit(scroll_by).sort(position: 1).scroll(current_cursor,
|
31
|
+
Mongoid.default_session['feed_items'].find.limit(scroll_by).sort(position: 1).scroll(current_cursor, field_type: Integer, field_name: 'position') do |item, cursor|
|
32
32
|
puts "#{item['position']}: #{item['title']}"
|
33
33
|
next_cursor = cursor
|
34
34
|
total_shown += 1
|
@@ -36,7 +36,7 @@ while true
|
|
36
36
|
break unless next_cursor
|
37
37
|
# destroy an item just for the heck of it, scroll is not affected
|
38
38
|
item = Mongoid.default_session['feed_items'].find.sort(position: 1).first
|
39
|
-
Mongoid.default_session['feed_items'].find(_id: item[
|
39
|
+
Mongoid.default_session['feed_items'].find(_id: item['_id']).remove
|
40
40
|
end
|
41
41
|
|
42
42
|
# this will be 20
|
@@ -1,13 +1,12 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Criterion
|
3
3
|
module Scrollable
|
4
|
-
|
5
|
-
def scroll(cursor = nil, &block)
|
4
|
+
def scroll(cursor = nil, &_block)
|
6
5
|
criteria = self
|
7
6
|
# we don't support scrolling over a criteria with multiple fields
|
8
7
|
if criteria.options[:sort] && criteria.options[:sort].keys.size != 1
|
9
8
|
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: criteria.options[:sort])
|
10
|
-
elsif !
|
9
|
+
elsif !criteria.options.key?(:sort) || criteria.options[:sort].empty?
|
11
10
|
# introduce a default sort order if there's none
|
12
11
|
criteria = criteria.asc(:_id)
|
13
12
|
end
|
@@ -27,7 +26,6 @@ module Mongoid
|
|
27
26
|
criteria
|
28
27
|
end
|
29
28
|
end
|
30
|
-
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Scroll
|
3
3
|
class Cursor
|
4
|
-
|
5
4
|
attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction
|
6
5
|
|
7
6
|
def initialize(value = nil, options = {})
|
@@ -12,10 +11,10 @@ module Mongoid
|
|
12
11
|
|
13
12
|
def criteria
|
14
13
|
mongo_value = value.class.mongoize(value) if value
|
15
|
-
compare_direction = direction == 1 ?
|
14
|
+
compare_direction = direction == 1 ? '$gt' : '$lt'
|
16
15
|
cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
|
17
16
|
tiebreak_criteria = { field_name => mongo_value, :_id => { compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
|
18
|
-
|
17
|
+
cursor_criteria || tiebreak_criteria ? { '$or' => [cursor_criteria, tiebreak_criteria].compact } : {}
|
19
18
|
end
|
20
19
|
|
21
20
|
class << self
|
@@ -23,70 +22,71 @@ module Mongoid
|
|
23
22
|
cursor = Mongoid::Scroll::Cursor.new(nil, options)
|
24
23
|
value = record.respond_to?(cursor.field_name) ? record.send(cursor.field_name) : record[cursor.field_name]
|
25
24
|
cursor.value = Mongoid::Scroll::Cursor.parse_field_value(cursor.field_type, cursor.field_name, value)
|
26
|
-
cursor.tiebreak_id = record[
|
25
|
+
cursor.tiebreak_id = record['_id']
|
27
26
|
cursor
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
30
|
def to_s
|
32
|
-
tiebreak_id ? [
|
31
|
+
tiebreak_id ? [Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, value), tiebreak_id].join(':') : nil
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
def parse(value)
|
37
|
+
return unless value
|
38
|
+
parts = value.split(':')
|
39
|
+
unless parts.length >= 2
|
40
|
+
raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
|
41
|
+
end
|
42
|
+
id = parts[-1]
|
43
|
+
value = parts[0...-1].join(':')
|
44
|
+
@value = Mongoid::Scroll::Cursor.parse_field_value(field_type, field_name, value)
|
45
|
+
if Mongoid::Scroll.mongoid3?
|
46
46
|
@tiebreak_id = Moped::BSON::ObjectId(id)
|
47
|
+
else
|
48
|
+
@tiebreak_id = BSON::ObjectId.from_string(id)
|
47
49
|
end
|
50
|
+
end
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
raise ArgumentError.new "Missing options[:field_name] and/or options[:field_type]."
|
58
|
-
end
|
52
|
+
class << self
|
53
|
+
def extract_field_options(options)
|
54
|
+
if options && (field_name = options[:field_name]) && (field_type = options[:field_type])
|
55
|
+
[field_type.to_s, field_name.to_s]
|
56
|
+
elsif options && (field = options[:field])
|
57
|
+
[field.type.to_s, field.name.to_s]
|
58
|
+
else
|
59
|
+
raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].'
|
59
60
|
end
|
61
|
+
end
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
63
|
+
def parse_field_value(field_type, field_name, value)
|
64
|
+
case field_type.to_s
|
65
|
+
when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
|
66
|
+
when 'String' then value.to_s
|
67
|
+
when 'DateTime' then value.is_a?(DateTime) ? value : Time.at(value.to_i).to_datetime
|
68
|
+
when 'Time' then value.is_a?(Time) ? value : Time.at(value.to_i)
|
69
|
+
when 'Date' then value.is_a?(Date) ? value : Time.at(value.to_i).utc.to_date
|
70
|
+
when 'Float' then value.to_f
|
71
|
+
when 'Integer' then value.to_i
|
72
|
+
else
|
73
|
+
raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
|
73
74
|
end
|
75
|
+
end
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
77
|
+
def transform_field_value(field_type, field_name, value)
|
78
|
+
case field_type.to_s
|
79
|
+
when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
|
80
|
+
when 'String' then value.to_s
|
81
|
+
when 'Date' then Time.utc(value.year, value.month, value.day).to_i
|
82
|
+
when 'DateTime', 'Time' then value.to_i
|
83
|
+
when 'Float' then value.to_f
|
84
|
+
when 'Integer' then value.to_i
|
85
|
+
else
|
86
|
+
raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
|
86
87
|
end
|
87
|
-
|
88
88
|
end
|
89
|
-
|
89
|
+
end
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
@@ -2,7 +2,6 @@ module Mongoid
|
|
2
2
|
module Scroll
|
3
3
|
module Errors
|
4
4
|
class Base < StandardError
|
5
|
-
|
6
5
|
# Problem occurred.
|
7
6
|
attr_reader :problem
|
8
7
|
|
@@ -21,60 +20,59 @@ module Mongoid
|
|
21
20
|
@summary = create_summary(key, attributes)
|
22
21
|
@resolution = create_resolution(key, attributes)
|
23
22
|
|
24
|
-
"\nProblem:\n #{@problem}"
|
25
|
-
"\nSummary:\n #{@summary}"
|
23
|
+
"\nProblem:\n #{@problem}" \
|
24
|
+
"\nSummary:\n #{@summary}" \
|
26
25
|
"\nResolution:\n #{@resolution}"
|
27
26
|
end
|
28
27
|
|
29
28
|
private
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
# Given the key of the specific error and the options hash, translate the
|
34
|
-
# message.
|
35
|
-
#
|
36
|
-
# === Parameters
|
37
|
-
# [key] The key of the error in the locales.
|
38
|
-
# [options] The objects to pass to create the message.
|
39
|
-
#
|
40
|
-
# Returns a localized error message string.
|
41
|
-
def translate(key, options)
|
42
|
-
::I18n.translate("#{BASE_KEY}.#{key}", { :locale => :en }.merge(options)).strip
|
43
|
-
end
|
30
|
+
BASE_KEY = 'mongoid.scroll.errors.messages' #:nodoc:
|
44
31
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
32
|
+
# Given the key of the specific error and the options hash, translate the
|
33
|
+
# message.
|
34
|
+
#
|
35
|
+
# === Parameters
|
36
|
+
# [key] The key of the error in the locales.
|
37
|
+
# [options] The objects to pass to create the message.
|
38
|
+
#
|
39
|
+
# Returns a localized error message string.
|
40
|
+
def translate(key, options)
|
41
|
+
::I18n.translate("#{BASE_KEY}.#{key}", { locale: :en }.merge(options)).strip
|
42
|
+
end
|
55
43
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
44
|
+
# Create the problem.
|
45
|
+
#
|
46
|
+
# === Parameters
|
47
|
+
# [key] The error key.
|
48
|
+
# [attributes] The attributes to interpolate.
|
49
|
+
#
|
50
|
+
# Returns the problem.
|
51
|
+
def create_problem(key, attributes)
|
52
|
+
translate("#{key}.message", attributes)
|
53
|
+
end
|
66
54
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
55
|
+
# Create the summary.
|
56
|
+
#
|
57
|
+
# === Parameters
|
58
|
+
# [key] The error key.
|
59
|
+
# [attributes] The attributes to interpolate.
|
60
|
+
#
|
61
|
+
# Returns the summary.
|
62
|
+
def create_summary(key, attributes)
|
63
|
+
translate("#{key}.summary", attributes)
|
64
|
+
end
|
77
65
|
|
66
|
+
# Create the resolution.
|
67
|
+
#
|
68
|
+
# === Parameters
|
69
|
+
# [key] The error key.
|
70
|
+
# [attributes] The attributes to interpolate.
|
71
|
+
#
|
72
|
+
# Returns the resolution.
|
73
|
+
def create_resolution(key, attributes)
|
74
|
+
translate("#{key}.resolution", attributes)
|
75
|
+
end
|
78
76
|
end
|
79
77
|
end
|
80
78
|
end
|
@@ -2,11 +2,9 @@ module Mongoid
|
|
2
2
|
module Scroll
|
3
3
|
module Errors
|
4
4
|
class InvalidCursorError < Mongoid::Scroll::Errors::Base
|
5
|
-
|
6
5
|
def initialize(opts = {})
|
7
|
-
super(compose_message(
|
6
|
+
super(compose_message('invalid_cursor', opts))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
@@ -2,14 +2,12 @@ module Mongoid
|
|
2
2
|
module Scroll
|
3
3
|
module Errors
|
4
4
|
class MultipleSortFieldsError < Mongoid::Scroll::Errors::Base
|
5
|
-
|
6
5
|
def initialize(opts = {})
|
7
6
|
if opts[:sort] && opts[:sort].is_a?(Hash)
|
8
|
-
opts = opts.merge(sort: opts[:sort].keys.join(
|
7
|
+
opts = opts.merge(sort: opts[:sort].keys.join(', '))
|
9
8
|
end
|
10
|
-
super(compose_message(
|
9
|
+
super(compose_message('multiple_sort_fields', opts))
|
11
10
|
end
|
12
|
-
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
@@ -2,11 +2,9 @@ module Mongoid
|
|
2
2
|
module Scroll
|
3
3
|
module Errors
|
4
4
|
class NoSuchFieldError < Mongoid::Scroll::Errors::Base
|
5
|
-
|
6
5
|
def initialize(opts = {})
|
7
|
-
super(compose_message(
|
6
|
+
super(compose_message('no_such_field', opts))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
@@ -2,11 +2,9 @@ module Mongoid
|
|
2
2
|
module Scroll
|
3
3
|
module Errors
|
4
4
|
class UnsupportedFieldTypeError < Mongoid::Scroll::Errors::Base
|
5
|
-
|
6
5
|
def initialize(opts = {})
|
7
|
-
super(compose_message(
|
6
|
+
super(compose_message('unsupported_field_type', opts))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
data/lib/mongoid-scroll.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'i18n'
|
2
2
|
|
3
|
-
I18n.load_path << File.join(File.dirname(__FILE__),
|
3
|
+
I18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
|
4
4
|
|
5
5
|
require 'mongoid'
|
6
6
|
require 'mongoid/scroll/version'
|
7
|
+
require 'mongoid/scroll/mongoid'
|
7
8
|
require 'mongoid/scroll/errors'
|
8
9
|
require 'mongoid/scroll/cursor'
|
9
10
|
|
data/lib/mongoid_scroll.rb
CHANGED
data/lib/moped/scrollable.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
module Moped
|
2
2
|
module Scrollable
|
3
|
-
|
4
|
-
|
3
|
+
def scroll(cursor = nil, options = nil, &_block)
|
4
|
+
options = if Mongoid::Scroll.mongoid3?
|
5
|
+
{ field_type: Moped::BSON::ObjectId }
|
6
|
+
else
|
7
|
+
{ field_type: BSON::ObjectId }
|
8
|
+
end unless options
|
5
9
|
query = Query.new(collection, operation.selector.dup)
|
6
10
|
query.operation.skip = operation.skip
|
7
11
|
query.operation.limit = operation.limit
|
8
12
|
# we don't support scrolling over a criteria with multiple fields
|
9
|
-
if query.operation.selector[
|
10
|
-
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: query.operation.selector[
|
11
|
-
elsif !
|
13
|
+
if query.operation.selector['$orderby'] && query.operation.selector['$orderby'].keys.size != 1
|
14
|
+
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: query.operation.selector['$orderby'])
|
15
|
+
elsif !query.operation.selector.key?('$orderby') || query.operation.selector['$orderby'].empty?
|
12
16
|
# introduce a default sort order if there's none
|
13
17
|
query.sort(_id: 1)
|
14
18
|
end
|
15
19
|
# scroll field and direction
|
16
|
-
scroll_field = query.operation.selector[
|
17
|
-
scroll_direction = query.operation.selector[
|
20
|
+
scroll_field = query.operation.selector['$orderby'].keys.first
|
21
|
+
scroll_direction = query.operation.selector['$orderby'].values.first.to_i
|
18
22
|
# scroll cursor from the parameter, with value and tiebreak_id
|
19
23
|
cursor_options = { field_name: scroll_field, field_type: options[:field_type], direction: scroll_direction }
|
20
24
|
cursor = cursor.is_a?(Mongoid::Scroll::Cursor) ? cursor : Mongoid::Scroll::Cursor.new(cursor, cursor_options)
|
21
|
-
query.operation.selector[
|
22
|
-
query.operation.selector[
|
25
|
+
query.operation.selector['$query'] = query.operation.selector['$query'].merge(cursor.criteria)
|
26
|
+
query.operation.selector['$orderby'] = query.operation.selector['$orderby'].merge(_id: scroll_direction)
|
23
27
|
# scroll
|
24
28
|
if block_given?
|
25
29
|
query.each do |record|
|
@@ -29,6 +33,5 @@ module Moped
|
|
29
33
|
query
|
30
34
|
end
|
31
35
|
end
|
32
|
-
|
33
36
|
end
|
34
37
|
end
|
data/mongoid-scroll.gemspec
CHANGED
@@ -1,20 +1,18 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'mongoid/scroll/version'
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
5
|
+
s.name = 'mongoid-scroll'
|
6
6
|
s.version = Mongoid::Scroll::VERSION
|
7
|
-
s.authors = [
|
8
|
-
s.email =
|
7
|
+
s.authors = ['Daniel Doubrovkine', 'Frank Macreery']
|
8
|
+
s.email = 'dblock@dblock.org'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.required_rubygems_version = '>= 1.3.6'
|
11
11
|
s.files = `git ls-files`.split("\n")
|
12
|
-
s.require_paths = [
|
13
|
-
s.homepage =
|
14
|
-
s.licenses = [
|
15
|
-
s.summary =
|
16
|
-
s.add_dependency
|
17
|
-
s.add_dependency
|
12
|
+
s.require_paths = ['lib']
|
13
|
+
s.homepage = 'http://github.com/dblock/mongoid-scroll'
|
14
|
+
s.licenses = ['MIT']
|
15
|
+
s.summary = 'Mongoid extensions to enable infinite scroll.'
|
16
|
+
s.add_dependency 'mongoid', '>= 3.0'
|
17
|
+
s.add_dependency 'i18n'
|
18
18
|
end
|
19
|
-
|
20
|
-
|
@@ -1,32 +1,32 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mongoid::Criteria do
|
4
|
-
context
|
4
|
+
context 'scrollable' do
|
5
5
|
subject do
|
6
6
|
Feed::Item
|
7
7
|
end
|
8
|
-
it
|
8
|
+
it ':scroll' do
|
9
9
|
subject.should.respond_to? :scroll
|
10
10
|
end
|
11
11
|
end
|
12
|
-
context
|
12
|
+
context 'with multiple sort fields' do
|
13
13
|
subject do
|
14
14
|
Feed::Item.desc(:name).asc(:value)
|
15
15
|
end
|
16
|
-
it
|
16
|
+
it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
|
17
17
|
expect { subject.scroll }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
|
18
|
-
|
18
|
+
/You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
|
19
19
|
end
|
20
20
|
end
|
21
|
-
context
|
21
|
+
context 'with no sort' do
|
22
22
|
subject do
|
23
23
|
Feed::Item.all
|
24
24
|
end
|
25
|
-
it
|
26
|
-
subject.scroll.options[:sort].should == {
|
25
|
+
it 'adds a default sort by _id' do
|
26
|
+
subject.scroll.options[:sort].should == { '_id' => 1 }
|
27
27
|
end
|
28
28
|
end
|
29
|
-
context
|
29
|
+
context 'with data' do
|
30
30
|
before :each do
|
31
31
|
10.times do |i|
|
32
32
|
Feed::Item.create!(
|
@@ -38,10 +38,10 @@ describe Mongoid::Criteria do
|
|
38
38
|
)
|
39
39
|
end
|
40
40
|
end
|
41
|
-
context
|
42
|
-
it
|
41
|
+
context 'default' do
|
42
|
+
it 'scrolls all' do
|
43
43
|
records = []
|
44
|
-
Feed::Item.all.scroll do |record,
|
44
|
+
Feed::Item.all.scroll do |record, _next_cursor|
|
45
45
|
records << record
|
46
46
|
end
|
47
47
|
records.size.should == 10
|
@@ -50,15 +50,15 @@ describe Mongoid::Criteria do
|
|
50
50
|
end
|
51
51
|
{ a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
|
52
52
|
context field_type do
|
53
|
-
it
|
53
|
+
it 'scrolls all with a block' do
|
54
54
|
records = []
|
55
|
-
Feed::Item.asc(field_name).scroll do |record,
|
55
|
+
Feed::Item.asc(field_name).scroll do |record, _next_cursor|
|
56
56
|
records << record
|
57
57
|
end
|
58
58
|
records.size.should == 10
|
59
59
|
records.should eq Feed::Item.all.to_a
|
60
60
|
end
|
61
|
-
it
|
61
|
+
it 'scrolls all with a break' do
|
62
62
|
records = []
|
63
63
|
cursor = nil
|
64
64
|
Feed::Item.asc(field_name).limit(5).scroll do |record, next_cursor|
|
@@ -73,19 +73,19 @@ describe Mongoid::Criteria do
|
|
73
73
|
records.size.should == 10
|
74
74
|
records.should eq Feed::Item.all.to_a
|
75
75
|
end
|
76
|
-
it
|
76
|
+
it 'scrolls in descending order' do
|
77
77
|
records = []
|
78
|
-
Feed::Item.desc(field_name).limit(3).scroll do |record,
|
78
|
+
Feed::Item.desc(field_name).limit(3).scroll do |record, _next_cursor|
|
79
79
|
records << record
|
80
80
|
end
|
81
81
|
records.size.should == 3
|
82
82
|
records.should eq Feed::Item.desc(field_name).limit(3).to_a
|
83
83
|
end
|
84
|
-
it
|
85
|
-
record = Feed::Item.desc(field_name).limit(3).scroll.map { |
|
86
|
-
cursor = Mongoid::Scroll::Cursor.from_record(record,
|
84
|
+
it 'map' do
|
85
|
+
record = Feed::Item.desc(field_name).limit(3).scroll.map { |r, _| r }.last
|
86
|
+
cursor = Mongoid::Scroll::Cursor.from_record(record, field_type: field_type, field_name: field_name)
|
87
87
|
cursor.should_not be_nil
|
88
|
-
cursor.to_s.split(
|
88
|
+
cursor.to_s.split(':').should == [
|
89
89
|
Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, record.send(field_name)).to_s,
|
90
90
|
record.id.to_s
|
91
91
|
]
|
@@ -93,18 +93,18 @@ describe Mongoid::Criteria do
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
96
|
-
context
|
96
|
+
context 'with overlapping data' do
|
97
97
|
before :each do
|
98
98
|
3.times { Feed::Item.create! a_integer: 5 }
|
99
99
|
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
100
100
|
end
|
101
|
-
it
|
101
|
+
it 'natural order is different from order by id' do
|
102
102
|
# natural order isn't necessarily going to be the same as _id order
|
103
103
|
# if a document is updated and grows in size, it may need to be relocated and
|
104
104
|
# thus cause the natural order to change
|
105
|
-
Feed::Item.order_by(
|
105
|
+
Feed::Item.order_by('$natural' => 1).to_a.should_not eq Feed::Item.order_by(_id: 1).to_a
|
106
106
|
end
|
107
|
-
[
|
107
|
+
[{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
108
108
|
it "scrolls by #{sort_order}" do
|
109
109
|
records = []
|
110
110
|
cursor = nil
|
@@ -113,7 +113,7 @@ describe Mongoid::Criteria do
|
|
113
113
|
cursor = next_cursor
|
114
114
|
end
|
115
115
|
records.size.should == 2
|
116
|
-
Feed::Item.order_by(sort_order).scroll(cursor) do |record,
|
116
|
+
Feed::Item.order_by(sort_order).scroll(cursor) do |record, _next_cursor|
|
117
117
|
records << record
|
118
118
|
end
|
119
119
|
records.size.should == 3
|
@@ -1,120 +1,120 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mongoid::Scroll::Cursor do
|
4
|
-
context
|
4
|
+
context 'an empty cursor' do
|
5
5
|
subject do
|
6
|
-
Mongoid::Scroll::Cursor.new nil, field_name:
|
6
|
+
Mongoid::Scroll::Cursor.new nil, field_name: 'a_string', field_type: String
|
7
7
|
end
|
8
8
|
its(:tiebreak_id) { should be_nil }
|
9
9
|
its(:value) { should be_nil }
|
10
10
|
its(:criteria) { should eq({}) }
|
11
11
|
end
|
12
|
-
context
|
13
|
-
it
|
14
|
-
expect { Mongoid::Scroll::Cursor.new
|
15
|
-
|
12
|
+
context 'an invalid cursor' do
|
13
|
+
it 'raises InvalidCursorError' do
|
14
|
+
expect { Mongoid::Scroll::Cursor.new 'invalid', field_name: 'a_string', field_type: String }.to raise_error Mongoid::Scroll::Errors::InvalidCursorError,
|
15
|
+
/The cursor supplied is invalid: invalid./
|
16
16
|
end
|
17
17
|
end
|
18
|
-
context
|
19
|
-
let(:feed_item) { Feed::Item.create!(a_string:
|
18
|
+
context 'a string field cursor' do
|
19
|
+
let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
|
20
20
|
subject do
|
21
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name:
|
21
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String
|
22
22
|
end
|
23
23
|
its(:value) { should eq feed_item.a_string }
|
24
24
|
its(:tiebreak_id) { should eq feed_item.id }
|
25
25
|
its(:criteria) {
|
26
|
-
should eq(
|
27
|
-
{
|
28
|
-
{
|
29
|
-
]
|
26
|
+
should eq('$or' => [
|
27
|
+
{ 'a_string' => { '$gt' => feed_item.a_string } },
|
28
|
+
{ 'a_string' => feed_item.a_string, :_id => { '$gt' => feed_item.id } }
|
29
|
+
])
|
30
30
|
}
|
31
31
|
end
|
32
|
-
context
|
32
|
+
context 'an integer field cursor' do
|
33
33
|
let(:feed_item) { Feed::Item.create!(a_integer: 10) }
|
34
34
|
subject do
|
35
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_integer}:#{feed_item.id}", field_name:
|
35
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_integer}:#{feed_item.id}", field_name: 'a_integer', field_type: Integer
|
36
36
|
end
|
37
37
|
its(:value) { should eq feed_item.a_integer }
|
38
38
|
its(:tiebreak_id) { should eq feed_item.id }
|
39
39
|
its(:criteria) {
|
40
|
-
should eq(
|
41
|
-
{
|
42
|
-
{
|
43
|
-
]
|
40
|
+
should eq('$or' => [
|
41
|
+
{ 'a_integer' => { '$gt' => feed_item.a_integer } },
|
42
|
+
{ 'a_integer' => feed_item.a_integer, :_id => { '$gt' => feed_item.id } }
|
43
|
+
])
|
44
44
|
}
|
45
45
|
end
|
46
|
-
context
|
46
|
+
context 'a date/time field cursor' do
|
47
47
|
let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3)) }
|
48
48
|
subject do
|
49
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.to_i}:#{feed_item.id}", field_name:
|
49
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.to_i}:#{feed_item.id}", field_name: 'a_datetime', field_type: DateTime
|
50
50
|
end
|
51
51
|
its(:value) { should eq feed_item.a_datetime }
|
52
52
|
its(:tiebreak_id) { should eq feed_item.id }
|
53
53
|
its(:to_s) { should eq "#{feed_item.a_datetime.to_i}:#{feed_item.id}" }
|
54
54
|
its(:criteria) {
|
55
|
-
should eq(
|
56
|
-
{
|
57
|
-
{
|
58
|
-
]
|
55
|
+
should eq('$or' => [
|
56
|
+
{ 'a_datetime' => { '$gt' => feed_item.a_datetime } },
|
57
|
+
{ 'a_datetime' => feed_item.a_datetime, :_id => { '$gt' => feed_item.id } }
|
58
|
+
])
|
59
59
|
}
|
60
60
|
end
|
61
|
-
context
|
61
|
+
context 'a date field cursor' do
|
62
62
|
let(:feed_item) { Feed::Item.create!(a_date: Date.new(2013, 12, 21)) }
|
63
63
|
subject do
|
64
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}", field_name:
|
64
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}", field_name: 'a_date', field_type: Date
|
65
65
|
end
|
66
66
|
its(:value) { should eq feed_item.a_date }
|
67
67
|
its(:tiebreak_id) { should eq feed_item.id }
|
68
68
|
its(:to_s) { should eq "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}" }
|
69
69
|
its(:criteria) {
|
70
|
-
should eq(
|
71
|
-
{
|
72
|
-
{
|
73
|
-
]
|
70
|
+
should eq('$or' => [
|
71
|
+
{ 'a_date' => { '$gt' => feed_item.a_date.to_datetime } },
|
72
|
+
{ 'a_date' => feed_item.a_date.to_datetime, :_id => { '$gt' => feed_item.id } }
|
73
|
+
])
|
74
74
|
}
|
75
75
|
end
|
76
|
-
context
|
76
|
+
context 'a time field cursor' do
|
77
77
|
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
78
78
|
subject do
|
79
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_i}:#{feed_item.id}", field_name:
|
79
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_i}:#{feed_item.id}", field_name: 'a_time', field_type: Time
|
80
80
|
end
|
81
81
|
its(:value) { should eq feed_item.a_time }
|
82
82
|
its(:tiebreak_id) { should eq feed_item.id }
|
83
83
|
its(:to_s) { should eq "#{feed_item.a_time.to_i}:#{feed_item.id}" }
|
84
84
|
its(:criteria) {
|
85
|
-
should eq(
|
86
|
-
{
|
87
|
-
{
|
88
|
-
]
|
85
|
+
should eq('$or' => [
|
86
|
+
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
87
|
+
{ 'a_time' => feed_item.a_time, :_id => { '$gt' => feed_item.id } }
|
88
|
+
])
|
89
89
|
}
|
90
90
|
end
|
91
|
-
context
|
91
|
+
context 'a time field cursor with a field option' do
|
92
92
|
let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
|
93
93
|
subject do
|
94
|
-
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_i}:#{feed_item.id}", field: Feed::Item.fields[
|
94
|
+
Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_i}:#{feed_item.id}", field: Feed::Item.fields['a_time']
|
95
95
|
end
|
96
96
|
its(:value) { should eq feed_item.a_time }
|
97
97
|
its(:tiebreak_id) { should eq feed_item.id }
|
98
98
|
its(:to_s) { should eq "#{feed_item.a_time.to_i}:#{feed_item.id}" }
|
99
99
|
its(:criteria) {
|
100
|
-
should eq(
|
101
|
-
{
|
102
|
-
{
|
103
|
-
]
|
100
|
+
should eq('$or' => [
|
101
|
+
{ 'a_time' => { '$gt' => feed_item.a_time } },
|
102
|
+
{ 'a_time' => feed_item.a_time, :_id => { '$gt' => feed_item.id } }
|
103
|
+
])
|
104
104
|
}
|
105
105
|
end
|
106
|
-
context
|
107
|
-
let(:feed_item) { Feed::Item.create!(a_array: [
|
108
|
-
it
|
106
|
+
context 'an array field cursor' do
|
107
|
+
let(:feed_item) { Feed::Item.create!(a_array: ['x', 'y']) }
|
108
|
+
it 'is not supported' do
|
109
109
|
expect {
|
110
|
-
Mongoid::Scroll::Cursor.from_record feed_item, field_name:
|
110
|
+
Mongoid::Scroll::Cursor.from_record feed_item, field_name: 'a_array', field_type: Array
|
111
111
|
}.to raise_error Mongoid::Scroll::Errors::UnsupportedFieldTypeError, /The type of the field 'a_array' is not supported: Array./
|
112
112
|
end
|
113
113
|
end
|
114
|
-
context
|
115
|
-
it
|
114
|
+
context 'an invalid field cursor' do
|
115
|
+
it 'raises ArgumentError' do
|
116
116
|
expect {
|
117
|
-
Mongoid::Scroll::Cursor.new
|
117
|
+
Mongoid::Scroll::Cursor.new 'invalid:whatever', {}
|
118
118
|
}.to raise_error ArgumentError
|
119
119
|
end
|
120
120
|
end
|
data/spec/mongoid/scroll_spec.rb
CHANGED
data/spec/moped/query_spec.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Moped::Query do
|
4
|
-
context
|
4
|
+
context 'scrollable' do
|
5
5
|
subject do
|
6
6
|
Mongoid.default_session['feed_items'].find
|
7
7
|
end
|
8
|
-
it
|
8
|
+
it ':scroll' do
|
9
9
|
subject.should.respond_to? :scroll
|
10
10
|
end
|
11
11
|
end
|
12
|
-
context
|
12
|
+
context 'with multiple sort fields' do
|
13
13
|
subject do
|
14
14
|
Mongoid.default_session['feed_items'].find.sort(name: 1, value: -1)
|
15
15
|
end
|
16
|
-
it
|
16
|
+
it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
|
17
17
|
expect { subject.scroll }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
|
18
|
-
|
18
|
+
/You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
|
19
19
|
end
|
20
20
|
end
|
21
|
-
context
|
21
|
+
context 'with no sort' do
|
22
22
|
subject do
|
23
23
|
Mongoid.default_session['feed_items'].find
|
24
24
|
end
|
25
|
-
it
|
26
|
-
subject.scroll.operation.selector[
|
25
|
+
it 'adds a default sort by _id' do
|
26
|
+
subject.scroll.operation.selector['$orderby'].should == { _id: 1 }
|
27
27
|
end
|
28
28
|
end
|
29
|
-
context
|
29
|
+
context 'with data' do
|
30
30
|
before :each do
|
31
31
|
10.times do |i|
|
32
32
|
Mongoid.default_session['feed_items'].insert(
|
@@ -38,10 +38,10 @@ describe Moped::Query do
|
|
38
38
|
)
|
39
39
|
end
|
40
40
|
end
|
41
|
-
context
|
42
|
-
it
|
41
|
+
context 'default' do
|
42
|
+
it 'scrolls all' do
|
43
43
|
records = []
|
44
|
-
Mongoid.default_session['feed_items'].find.scroll do |record,
|
44
|
+
Mongoid.default_session['feed_items'].find.scroll do |record, _next_cursor|
|
45
45
|
records << record
|
46
46
|
end
|
47
47
|
records.size.should == 10
|
@@ -50,63 +50,63 @@ describe Moped::Query do
|
|
50
50
|
end
|
51
51
|
{ a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
|
52
52
|
context field_type do
|
53
|
-
it
|
53
|
+
it 'scrolls all with a block' do
|
54
54
|
records = []
|
55
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(nil,
|
55
|
+
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(nil, field_type: field_type) do |record, _next_cursor|
|
56
56
|
records << record
|
57
57
|
end
|
58
58
|
records.size.should == 10
|
59
59
|
records.should eq Mongoid.default_session['feed_items'].find.to_a
|
60
60
|
end
|
61
|
-
it
|
61
|
+
it 'scrolls all with a break' do
|
62
62
|
records = []
|
63
63
|
cursor = nil
|
64
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).limit(5).scroll(nil,
|
64
|
+
Mongoid.default_session['feed_items'].find.sort(field_name => 1).limit(5).scroll(nil, field_type: field_type) do |record, next_cursor|
|
65
65
|
records << record
|
66
66
|
cursor = next_cursor
|
67
67
|
end
|
68
68
|
records.size.should == 5
|
69
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(cursor,
|
69
|
+
Mongoid.default_session['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
|
70
70
|
records << record
|
71
71
|
cursor = next_cursor
|
72
72
|
end
|
73
73
|
records.size.should == 10
|
74
74
|
records.should eq Mongoid.default_session['feed_items'].find.to_a
|
75
75
|
end
|
76
|
-
it
|
76
|
+
it 'scrolls in descending order' do
|
77
77
|
records = []
|
78
|
-
Mongoid.default_session['feed_items'].find.sort(field_name => -1).limit(3).scroll(nil,
|
78
|
+
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|
|
79
79
|
records << record
|
80
80
|
end
|
81
81
|
records.size.should == 3
|
82
82
|
records.should eq Mongoid.default_session['feed_items'].find.sort(field_name => -1).limit(3).to_a
|
83
83
|
end
|
84
|
-
it
|
85
|
-
record = Mongoid.default_session['feed_items'].find.limit(3).scroll(nil,
|
86
|
-
|
|
84
|
+
it 'map' do
|
85
|
+
record = Mongoid.default_session['feed_items'].find.limit(3).scroll(nil, field_type: field_type, field_name: field_name).map {
|
86
|
+
|r, _| r
|
87
87
|
}.last
|
88
|
-
cursor = Mongoid::Scroll::Cursor.from_record(record,
|
88
|
+
cursor = Mongoid::Scroll::Cursor.from_record(record, field_type: field_type, field_name: field_name)
|
89
89
|
cursor.should_not be_nil
|
90
|
-
cursor.to_s.split(
|
90
|
+
cursor.to_s.split(':').should == [
|
91
91
|
Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, record[field_name.to_s]).to_s,
|
92
|
-
record[
|
92
|
+
record['_id'].to_s
|
93
93
|
]
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
|
-
context
|
98
|
+
context 'with overlapping data' do
|
99
99
|
before :each do
|
100
100
|
3.times { Feed::Item.create! a_integer: 5 }
|
101
101
|
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
102
102
|
end
|
103
|
-
it
|
103
|
+
it 'natural order is different from order by id' do
|
104
104
|
# natural order isn't necessarily going to be the same as _id order
|
105
105
|
# if a document is updated and grows in size, it may need to be relocated and
|
106
106
|
# thus cause the natural order to change
|
107
|
-
Feed::Item.order_by(
|
107
|
+
Feed::Item.order_by('$natural' => 1).to_a.should_not eq Feed::Item.order_by(_id: 1).to_a
|
108
108
|
end
|
109
|
-
[
|
109
|
+
[{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
110
110
|
it "scrolls by #{sort_order}" do
|
111
111
|
records = []
|
112
112
|
cursor = nil
|
@@ -115,7 +115,7 @@ describe Moped::Query do
|
|
115
115
|
cursor = next_cursor
|
116
116
|
end
|
117
117
|
records.size.should == 2
|
118
|
-
Mongoid.default_session['feed_items'].find.sort(sort_order).scroll(cursor) do |record,
|
118
|
+
Mongoid.default_session['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
|
119
119
|
records << record
|
120
120
|
end
|
121
121
|
records.size.should == 3
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/feed/item.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-scroll
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Daniel Doubrovkine
|
@@ -10,38 +9,34 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2014-07-02 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: mongoid
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- -
|
18
|
+
- - '>='
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '3.0'
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- -
|
25
|
+
- - '>='
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '3.0'
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: i18n
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - '>='
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :runtime
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - '>='
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
description:
|
@@ -52,6 +47,7 @@ extra_rdoc_files: []
|
|
52
47
|
files:
|
53
48
|
- .gitignore
|
54
49
|
- .rspec
|
50
|
+
- .rubocop.yml
|
55
51
|
- .travis.yml
|
56
52
|
- CHANGELOG.md
|
57
53
|
- Gemfile
|
@@ -70,6 +66,7 @@ files:
|
|
70
66
|
- lib/mongoid/scroll/errors/multiple_sort_fields_error.rb
|
71
67
|
- lib/mongoid/scroll/errors/no_such_field_error.rb
|
72
68
|
- lib/mongoid/scroll/errors/unsupported_field_type_error.rb
|
69
|
+
- lib/mongoid/scroll/mongoid.rb
|
73
70
|
- lib/mongoid/scroll/version.rb
|
74
71
|
- lib/mongoid_scroll.rb
|
75
72
|
- lib/moped/scrollable.rb
|
@@ -83,29 +80,25 @@ files:
|
|
83
80
|
homepage: http://github.com/dblock/mongoid-scroll
|
84
81
|
licenses:
|
85
82
|
- MIT
|
83
|
+
metadata: {}
|
86
84
|
post_install_message:
|
87
85
|
rdoc_options: []
|
88
86
|
require_paths:
|
89
87
|
- lib
|
90
88
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
-
none: false
|
92
89
|
requirements:
|
93
|
-
- -
|
90
|
+
- - '>='
|
94
91
|
- !ruby/object:Gem::Version
|
95
92
|
version: '0'
|
96
|
-
segments:
|
97
|
-
- 0
|
98
|
-
hash: -2452181324971075888
|
99
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
-
none: false
|
101
94
|
requirements:
|
102
|
-
- -
|
95
|
+
- - '>='
|
103
96
|
- !ruby/object:Gem::Version
|
104
97
|
version: 1.3.6
|
105
98
|
requirements: []
|
106
99
|
rubyforge_project:
|
107
|
-
rubygems_version: 1.
|
100
|
+
rubygems_version: 2.1.11
|
108
101
|
signing_key:
|
109
|
-
specification_version:
|
102
|
+
specification_version: 4
|
110
103
|
summary: Mongoid extensions to enable infinite scroll.
|
111
104
|
test_files: []
|