mongoid-scroll 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/README.md +4 -4
- data/lib/mongoid/criterion/scrollable.rb +2 -2
- data/lib/mongoid/scroll/cursor.rb +4 -3
- data/lib/mongoid/scroll/version.rb +1 -1
- data/lib/moped/scrollable.rb +13 -9
- data/spec/mongoid/criteria_spec.rb +21 -12
- data/spec/moped/query_spec.rb +22 -13
- metadata +3 -3
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
0.2.1 (3/21/2013)
|
2
|
+
=================
|
3
|
+
|
4
|
+
* 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
|
+
|
1
6
|
0.2.0 (3/14/2013)
|
2
7
|
=================
|
3
8
|
|
data/README.md
CHANGED
@@ -101,8 +101,8 @@ Indexes and Performance
|
|
101
101
|
A query without a cursor is identical to a query without a scroll.
|
102
102
|
|
103
103
|
``` ruby
|
104
|
-
# db.feed_items.find().sort({ position: 1 }).limit(
|
105
|
-
Feed::Item.desc(:position).limit(
|
104
|
+
# db.feed_items.find().sort({ position: 1 }).limit(7)
|
105
|
+
Feed::Item.desc(:position).limit(7).scroll
|
106
106
|
```
|
107
107
|
|
108
108
|
Subsequent queries use an `$or` to avoid skipping items with the same value as the one at the current cursor position.
|
@@ -111,8 +111,8 @@ Subsequent queries use an `$or` to avoid skipping items with the same value as t
|
|
111
111
|
# db.feed_items.find({ "$or" : [
|
112
112
|
# { "position" : { "$gt" : 13 }},
|
113
113
|
# { "position" : 13, "_id": { "$gt" : ObjectId("511d7c7c3b5552c92400000e") }}
|
114
|
-
# ]}).sort({ position: 1 })
|
115
|
-
Feed:Item.desc(:position).limit(
|
114
|
+
# ]}).sort({ position: 1 }).limit(7)
|
115
|
+
Feed:Item.desc(:position).limit(7).scroll(cursor)
|
116
116
|
```
|
117
117
|
|
118
118
|
This means you need to hit an index on `position` and `_id`.
|
@@ -13,14 +13,14 @@ module Mongoid
|
|
13
13
|
end
|
14
14
|
# scroll field and direction
|
15
15
|
scroll_field = criteria.options[:sort].keys.first
|
16
|
-
scroll_direction = criteria.options[:sort].values.first.to_i
|
16
|
+
scroll_direction = criteria.options[:sort].values.first.to_i
|
17
17
|
# scroll cursor from the parameter, with value and tiebreak_id
|
18
18
|
field = criteria.klass.fields[scroll_field.to_s]
|
19
19
|
cursor_options = { field_type: field.type, field_name: scroll_field, direction: scroll_direction }
|
20
20
|
cursor = cursor.is_a?(Mongoid::Scroll::Cursor) ? cursor : Mongoid::Scroll::Cursor.new(cursor, cursor_options)
|
21
21
|
# scroll
|
22
22
|
if block_given?
|
23
|
-
criteria.where(cursor.criteria).each do |record|
|
23
|
+
criteria.where(cursor.criteria).order_by(_id: scroll_direction).each do |record|
|
24
24
|
yield record, Mongoid::Scroll::Cursor.from_record(record, cursor_options)
|
25
25
|
end
|
26
26
|
else
|
@@ -6,14 +6,15 @@ module Mongoid
|
|
6
6
|
|
7
7
|
def initialize(value = nil, options = {})
|
8
8
|
@field_type, @field_name = Mongoid::Scroll::Cursor.extract_field_options(options)
|
9
|
-
@direction = options[:direction] ||
|
9
|
+
@direction = options[:direction] || 1
|
10
10
|
parse(value)
|
11
11
|
end
|
12
12
|
|
13
13
|
def criteria
|
14
14
|
mongo_value = value.class.mongoize(value) if value
|
15
|
-
|
16
|
-
|
15
|
+
compare_direction = direction == 1 ? "$gt" : "$lt"
|
16
|
+
cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
|
17
|
+
tiebreak_criteria = { field_name => mongo_value, :_id => { compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
|
17
18
|
(cursor_criteria || tiebreak_criteria) ? { '$or' => [ cursor_criteria, tiebreak_criteria].compact } : {}
|
18
19
|
end
|
19
20
|
|
data/lib/moped/scrollable.rb
CHANGED
@@ -2,27 +2,31 @@ module Moped
|
|
2
2
|
module Scrollable
|
3
3
|
|
4
4
|
def scroll(cursor = nil, options = { field_type: Moped::BSON::ObjectId }, &block)
|
5
|
+
query = Query.new(collection, operation.selector.dup)
|
6
|
+
query.operation.skip = operation.skip
|
7
|
+
query.operation.limit = operation.limit
|
5
8
|
# we don't support scrolling over a criteria with multiple fields
|
6
|
-
if operation.selector["$orderby"] && operation.selector["$orderby"].keys.size != 1
|
7
|
-
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: operation.selector["$orderby"])
|
8
|
-
elsif ! operation.selector.has_key?("$orderby") || operation.selector["$orderby"].empty?
|
9
|
+
if query.operation.selector["$orderby"] && query.operation.selector["$orderby"].keys.size != 1
|
10
|
+
raise Mongoid::Scroll::Errors::MultipleSortFieldsError.new(sort: query.operation.selector["$orderby"])
|
11
|
+
elsif ! query.operation.selector.has_key?("$orderby") || query.operation.selector["$orderby"].empty?
|
9
12
|
# introduce a default sort order if there's none
|
10
|
-
sort(
|
13
|
+
query.sort(_id: 1)
|
11
14
|
end
|
12
15
|
# scroll field and direction
|
13
|
-
scroll_field = operation.selector["$orderby"].keys.first
|
14
|
-
scroll_direction = operation.selector["$orderby"].values.first.to_i
|
16
|
+
scroll_field = query.operation.selector["$orderby"].keys.first
|
17
|
+
scroll_direction = query.operation.selector["$orderby"].values.first.to_i
|
15
18
|
# scroll cursor from the parameter, with value and tiebreak_id
|
16
19
|
cursor_options = { field_name: scroll_field, field_type: options[:field_type], direction: scroll_direction }
|
17
20
|
cursor = cursor.is_a?(Mongoid::Scroll::Cursor) ? cursor : Mongoid::Scroll::Cursor.new(cursor, cursor_options)
|
18
|
-
operation.selector["$query"].merge
|
21
|
+
query.operation.selector["$query"] = query.operation.selector["$query"].merge(cursor.criteria)
|
22
|
+
query.operation.selector["$orderby"] = query.operation.selector["$orderby"].merge(_id: scroll_direction)
|
19
23
|
# scroll
|
20
24
|
if block_given?
|
21
|
-
each do |record|
|
25
|
+
query.each do |record|
|
22
26
|
yield record, Mongoid::Scroll::Cursor.from_record(record, cursor_options)
|
23
27
|
end
|
24
28
|
else
|
25
|
-
|
29
|
+
query
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
@@ -96,20 +96,29 @@ describe Mongoid::Criteria do
|
|
96
96
|
context "with overlapping data" do
|
97
97
|
before :each do
|
98
98
|
3.times { Feed::Item.create! a_integer: 5 }
|
99
|
+
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
99
100
|
end
|
100
|
-
it "
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
101
|
+
it "natural order is different from order by id" do
|
102
|
+
# natural order isn't necessarily going to be the same as _id order
|
103
|
+
# if a document is updated and grows in size, it may need to be relocated and
|
104
|
+
# thus cause the natural order to change
|
105
|
+
Feed::Item.order_by("$natural" => 1).to_a.should_not eq Feed::Item.order_by(_id: 1).to_a
|
106
|
+
end
|
107
|
+
[ { a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
108
|
+
it "scrolls by #{sort_order}" do
|
109
|
+
records = []
|
110
|
+
cursor = nil
|
111
|
+
Feed::Item.order_by(sort_order).limit(2).scroll do |record, next_cursor|
|
112
|
+
records << record
|
113
|
+
cursor = next_cursor
|
114
|
+
end
|
115
|
+
records.size.should == 2
|
116
|
+
Feed::Item.order_by(sort_order).scroll(cursor) do |record, next_cursor|
|
117
|
+
records << record
|
118
|
+
end
|
119
|
+
records.size.should == 3
|
120
|
+
records.should eq Feed::Item.all.sort(_id: sort_order[:a_integer]).to_a
|
110
121
|
end
|
111
|
-
records.size.should == 3
|
112
|
-
records.should eq Feed::Item.all.to_a
|
113
122
|
end
|
114
123
|
end
|
115
124
|
end
|
data/spec/moped/query_spec.rb
CHANGED
@@ -23,7 +23,7 @@ describe Moped::Query do
|
|
23
23
|
Mongoid.default_session['feed_items'].find
|
24
24
|
end
|
25
25
|
it "adds a default sort by _id" do
|
26
|
-
subject.scroll.operation.selector["$orderby"].should == {
|
26
|
+
subject.scroll.operation.selector["$orderby"].should == { _id: 1 }
|
27
27
|
end
|
28
28
|
end
|
29
29
|
context "with data" do
|
@@ -98,20 +98,29 @@ describe Moped::Query do
|
|
98
98
|
context "with overlapping data" do
|
99
99
|
before :each do
|
100
100
|
3.times { Feed::Item.create! a_integer: 5 }
|
101
|
+
Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
|
101
102
|
end
|
102
|
-
it "
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
103
|
+
it "natural order is different from order by id" do
|
104
|
+
# natural order isn't necessarily going to be the same as _id order
|
105
|
+
# if a document is updated and grows in size, it may need to be relocated and
|
106
|
+
# thus cause the natural order to change
|
107
|
+
Feed::Item.order_by("$natural" => 1).to_a.should_not eq Feed::Item.order_by(_id: 1).to_a
|
108
|
+
end
|
109
|
+
[ { a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
|
110
|
+
it "scrolls by #{sort_order}" do
|
111
|
+
records = []
|
112
|
+
cursor = nil
|
113
|
+
Mongoid.default_session['feed_items'].find.sort(sort_order).limit(2).scroll do |record, next_cursor|
|
114
|
+
records << record
|
115
|
+
cursor = next_cursor
|
116
|
+
end
|
117
|
+
records.size.should == 2
|
118
|
+
Mongoid.default_session['feed_items'].find.sort(sort_order).scroll(cursor) do |record, next_cursor|
|
119
|
+
records << record
|
120
|
+
end
|
121
|
+
records.size.should == 3
|
122
|
+
records.should eq Mongoid.default_session['feed_items'].find.sort(_id: sort_order[:a_integer]).to_a
|
112
123
|
end
|
113
|
-
records.size.should == 3
|
114
|
-
records.should eq Mongoid.default_session['feed_items'].find.to_a
|
115
124
|
end
|
116
125
|
end
|
117
126
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-scroll
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: mongoid
|
@@ -95,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
95
|
version: '0'
|
96
96
|
segments:
|
97
97
|
- 0
|
98
|
-
hash:
|
98
|
+
hash: -2452181324971075888
|
99
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
100
|
none: false
|
101
101
|
requirements:
|