Skip to content

Commit 89755b1

Browse files
committed
Merge pull request #677 from estolfo/RUBY-1006-query-modifiers
RUBY-1006 Refactor View and Readable to allow for arbtirary query modifiers
2 parents db4dd59 + 2df4d9a commit 89755b1

File tree

12 files changed

+417
-88
lines changed

12 files changed

+417
-88
lines changed

lib/mongo/collection/view.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ def hash
127127
def initialize(collection, selector = {}, options = {})
128128
validate_doc!(selector)
129129
@collection = collection
130-
@selector = selector.dup
131-
@options = options.dup
130+
setup(selector, options)
132131
end
133132

134133
# Get a human-readable string representation of +View+.

lib/mongo/collection/view/immutable.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ module Immutable
2222

2323
private
2424

25-
# @api private
26-
#
27-
# @note In the including class, the method #immutable needs to be
28-
# implemented in order to define how a new class of that type needs to
29-
# be instantiated.
3025
def configure(field, value)
3126
return options[field] if value.nil?
32-
new(options.merge(field => value))
27+
new(options.merge(field => value, :modifiers => @modifiers))
28+
end
29+
30+
def configure_modifier(field, value)
31+
return @modifiers[Readable::SPECIAL_FIELDS[field]] if value.nil?
32+
new(options.merge(:modifiers => @modifiers.merge(Readable::SPECIAL_FIELDS[field] => value)))
3333
end
3434

3535
def configure_flag(flag)

lib/mongo/collection/view/map_reduce.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,16 @@ def map_reduce_spec
159159
:mapreduce => collection.name,
160160
:map => map,
161161
:reduce => reduce,
162-
:query => view.selector[:$query] || view.selector,
162+
:query => view.modifiers[:$query] || view.selector,
163163
:out => { inline: 1 }
164-
}.merge(options).merge(view.options)
164+
}.merge(options).merge(view_options)
165165
}
166166
end
167167

168+
def view_options
169+
view.sort ? view.options.merge(:sort => view.sort) : view.options
170+
end
171+
168172
def new(options)
169173
MapReduce.new(view, map, reduce, options)
170174
end

lib/mongo/collection/view/readable.rb

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,23 @@ class View
2121
# @since 2.0.0
2222
module Readable
2323

24-
# Special fields and their getters for the query selector.
24+
# Special fields and their option names for the query selector.
2525
#
2626
# @since 2.0.0
2727
SPECIAL_FIELDS = {
28-
:$query => :selector,
29-
:$readPreference => :read_pref_formatted,
30-
:$orderby => :sort,
31-
:$hint => :hint,
32-
:$comment => :comment,
33-
:$snapshot => :snapshot,
34-
:$maxScan => :max_scan,
35-
:$max => :max_value,
36-
:$min => :min_value,
37-
:$maxTimeMS => :max_time_ms,
38-
:$returnKey => :return_key,
39-
:$showDiskLoc => :show_disk_loc,
40-
:$explain => :explained?
28+
:sort => :$orderby,
29+
:hint => :$hint,
30+
:comment => :$comment,
31+
:snapshot => :$snapshot,
32+
:max_scan => :$maxScan,
33+
:max_value => :$max,
34+
:min_value => :$min,
35+
:max_time_ms => :$maxTimeMS,
36+
:return_key => :$returnKey,
37+
:show_disk_loc => :$showDiskLoc,
38+
:explain => :$explain
4139
}.freeze
4240

43-
# Get just the names of the special fields.
44-
#
45-
# @since 2.1.0
46-
SPECIAL_FIELD_OPTION_NAMES = SPECIAL_FIELDS.values.freeze
47-
4841
# Options to cursor flags mapping.
4942
#
5043
# @since 2.1.0
@@ -99,7 +92,7 @@ def allow_partial_results
9992
#
10093
# @since 2.0.0
10194
def batch_size(batch_size = nil)
102-
configure(:batch_size, batch_size)
95+
configure(__method__, batch_size)
10396
end
10497

10598
# Associate a comment with the query.
@@ -117,7 +110,7 @@ def batch_size(batch_size = nil)
117110
#
118111
# @since 2.0.0
119112
def comment(comment = nil)
120-
configure(:comment, comment)
113+
configure_modifier(__method__, comment)
121114
end
122115

123116
# Get a count of matching documents in the collection.
@@ -181,7 +174,7 @@ def distinct(field_name, options={})
181174
#
182175
# @since 2.0.0
183176
def hint(hint = nil)
184-
configure(:hint, hint)
177+
configure_modifier(__method__, hint)
185178
end
186179

187180
# The max number of docs to return from the query.
@@ -195,7 +188,7 @@ def hint(hint = nil)
195188
#
196189
# @since 2.0.0
197190
def limit(limit = nil)
198-
configure(:limit, limit)
191+
configure(__method__, limit)
199192
end
200193

201194
# Execute a map/reduce operation on the collection view.
@@ -225,7 +218,7 @@ def map_reduce(map, reduce, options = {})
225218
#
226219
# @since 2.0.0
227220
def max_scan(value = nil)
228-
configure(:max_scan, value)
221+
configure_modifier(__method__, value)
229222
end
230223

231224
# Set the maximum value to search.
@@ -239,7 +232,7 @@ def max_scan(value = nil)
239232
#
240233
# @since 2.1.0
241234
def max_value(value = nil)
242-
configure(:max_value, value)
235+
configure_modifier(__method__, value)
243236
end
244237

245238
# Set the minimum value to search.
@@ -253,7 +246,7 @@ def max_value(value = nil)
253246
#
254247
# @since 2.1.0
255248
def min_value(value = nil)
256-
configure(:min_value, value)
249+
configure_modifier(__method__, value)
257250
end
258251

259252
# The server normally times out idle cursors after an inactivity period
@@ -266,7 +259,7 @@ def min_value(value = nil)
266259
#
267260
# @since 2.0.0
268261
def no_cursor_timeout
269-
configure_flag(:no_cursor_timeout)
262+
configure_flag(__method__)
270263
end
271264

272265
# The fields to include or exclude from each doc in the result set.
@@ -285,7 +278,7 @@ def no_cursor_timeout
285278
# @since 2.0.0
286279
def projection(document = nil)
287280
validate_doc!(document) if document
288-
configure(:projection, document)
281+
configure(__method__, document)
289282
end
290283

291284
# The read preference to use for the query.
@@ -301,7 +294,7 @@ def projection(document = nil)
301294
# @since 2.0.0
302295
def read(value = nil)
303296
return default_read if value.nil?
304-
configure(:read, value.is_a?(Hash) ? ServerSelector.get(value) : value)
297+
configure(__method__, value.is_a?(Hash) ? ServerSelector.get(value) : value)
305298
end
306299

307300
# Set whether to return only the indexed field or fields.
@@ -315,7 +308,7 @@ def read(value = nil)
315308
#
316309
# @since 2.1.0
317310
def return_key(value = nil)
318-
configure(:return_key, value)
311+
configure_modifier(__method__, value)
319312
end
320313

321314
# Set whether the disk location should be shown for each document.
@@ -330,7 +323,7 @@ def return_key(value = nil)
330323
#
331324
# @since 2.0.0
332325
def show_disk_loc(value = nil)
333-
configure(:show_disk_loc, value)
326+
configure_modifier(__method__, value)
334327
end
335328

336329
# The number of docs to skip before returning results.
@@ -345,7 +338,7 @@ def show_disk_loc(value = nil)
345338
#
346339
# @since 2.0.0
347340
def skip(number = nil)
348-
configure(:skip, number)
341+
configure(__method__, number)
349342
end
350343

351344
# Set the snapshot value for the view.
@@ -360,7 +353,7 @@ def skip(number = nil)
360353
#
361354
# @since 2.0.0
362355
def snapshot(value = nil)
363-
configure(:snapshot, value)
356+
configure_modifier(__method__, value)
364357
end
365358

366359
# The key and direction pairs by which the result set will be sorted.
@@ -375,7 +368,7 @@ def snapshot(value = nil)
375368
#
376369
# @since 2.0.0
377370
def sort(spec = nil)
378-
configure(:sort, spec)
371+
configure_modifier(__method__, spec)
379372
end
380373

381374
# “meta” operators that let you modify the output or behavior of a query.
@@ -389,7 +382,8 @@ def sort(spec = nil)
389382
#
390383
# @since 2.1.0
391384
def modifiers(doc = nil)
392-
configure(:modifiers, doc)
385+
return @modifiers if doc.nil?
386+
new(options.merge(__method__ => doc))
393387
end
394388

395389
# A cumulative time limit in milliseconds for processing operations on a cursor.
@@ -403,7 +397,7 @@ def modifiers(doc = nil)
403397
#
404398
# @since 2.1.0
405399
def max_time_ms(max = nil)
406-
configure(:max_time_ms, max)
400+
configure_modifier(__method__, max)
407401
end
408402

409403
private
@@ -421,10 +415,6 @@ def flags
421415
end
422416
end
423417

424-
def has_special_fields?
425-
contains_modifiers? || explained? || cluster.sharded?
426-
end
427-
428418
def parallel_scan(cursor_count)
429419
server = read.select_server(cluster)
430420
Operation::ParallelScan.new(
@@ -441,6 +431,27 @@ def parallel_scan(cursor_count)
441431
end
442432
end
443433

434+
def setup(sel, opts)
435+
setup_options(opts)
436+
setup_selector(sel)
437+
end
438+
439+
def setup_options(opts)
440+
@options = opts ? opts.dup : {}
441+
@modifiers = @options[:modifiers] ? @options.delete(:modifiers).dup : BSON::Document.new
442+
@options.keys.each { |k| @modifiers.merge!(SPECIAL_FIELDS[k] => @options.delete(k)) if SPECIAL_FIELDS[k] }
443+
@options.freeze
444+
end
445+
446+
def setup_selector(sel)
447+
@selector = sel ? sel.dup : {}
448+
if @selector[:$query] || @selector['$query']
449+
@selector.keys.each { |k| @modifiers.merge!(k => @selector.delete(k)) if k[0] == '$' }
450+
end
451+
@modifiers.freeze
452+
@selector.freeze
453+
end
454+
444455
def query_options
445456
{
446457
:project => projection,
@@ -451,8 +462,12 @@ def query_options
451462
}
452463
end
453464

465+
def requires_special_selector?
466+
!modifiers.empty? || cluster.sharded?
467+
end
468+
454469
def query_spec
455-
sel = has_special_fields? ? special_selector : selector
470+
sel = requires_special_selector? ? special_selector : selector
456471
{ :selector => sel,
457472
:read => read,
458473
:options => query_options,
@@ -461,26 +476,18 @@ def query_spec
461476
end
462477

463478
def read_pref_formatted
464-
read.to_mongos
479+
@read_formatted ||= read.to_mongos
465480
end
466481

467482
def special_selector
468-
SPECIAL_FIELDS.reduce({}) do |hash, (key, method)|
469-
value = send(method) || (options[:modifiers] && options[:modifiers][key])
470-
hash[key] = value unless value.nil?
471-
hash
472-
end
483+
sel = BSON::Document.new(:$query => selector).merge!(modifiers)
484+
sel[:$readPreference] = read_pref_formatted unless read_pref_formatted.nil?
485+
sel
473486
end
474487

475488
def validate_doc!(doc)
476489
raise Error::InvalidDocument.new unless doc.respond_to?(:keys)
477490
end
478-
479-
def contains_modifiers?
480-
modifiers || options.keys.any? do |key|
481-
SPECIAL_FIELD_OPTION_NAMES.include?(key)
482-
end
483-
end
484491
end
485492
end
486493
end

lib/mongo/grid/fs_bucket.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,19 @@ def download_to_stream(id, io)
261261
#
262262
# @since 2.1.0
263263
def open_download_stream_by_name(filename, opts = {}, &block)
264-
view = files_collection.find(:filename => filename).projection(_id: 1)
265264
revision = opts.fetch(:revision, -1)
266265
if revision < 0
267-
file_doc = view.sort('uploadDate' => Mongo::Index::DESCENDING).skip(revision.abs - 1).limit(-1).first
266+
skip = revision.abs - 1
267+
sort = { 'uploadDate' => Mongo::Index::DESCENDING }
268268
else
269-
file_doc = view.sort('uploadDate' => Mongo::Index::ASCENDING).skip(revision).limit(-1).first
269+
skip = revision
270+
sort = { 'uploadDate' => Mongo::Index::ASCENDING }
270271
end
272+
file_doc = files_collection.find({ filename: filename} ,
273+
projection: { _id: 1 },
274+
sort: sort,
275+
skip: skip,
276+
limit: -1).first
271277
unless file_doc
272278
raise Error::FileNotFound.new(filename, :filename) unless opts[:revision]
273279
raise Error::InvalidFileRevision.new(filename, opts[:revision])

lib/mongo/grid/stream/read.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class Read
4949
# @since 2.1.0
5050
def initialize(fs, options)
5151
@fs = fs
52-
@options = options
53-
@file_id = options[:file_id]
52+
@options = options.dup
53+
@file_id = @options.delete(:file_id)
5454
@open = true
5555
end
5656

lib/mongo/protocol/query.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ def op_command
258258
def find_command
259259
document = BSON::Document.new
260260
document[:find] = collection
261-
document[:filter] = filter.key?(:$query) ? filter[:$query] : filter
261+
document[:filter] = filter[:$query] ? filter[:$query] : filter
262262
OPTION_MAPPINGS.each do |legacy, option|
263263
document[option] = options[legacy] unless options[legacy].nil?
264264
end

0 commit comments

Comments
 (0)