mongoid-scroll 0.2.0 → 0.2.1
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.
- 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:
|