mongoid-scroll 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|