mongo 1.8.0 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/README.md +14 -29
  2. data/VERSION +1 -1
  3. data/lib/mongo.rb +3 -0
  4. data/lib/mongo/collection.rb +99 -49
  5. data/lib/mongo/cursor.rb +17 -17
  6. data/lib/mongo/db.rb +30 -14
  7. data/lib/mongo/gridfs/grid.rb +5 -3
  8. data/lib/mongo/gridfs/grid_file_system.rb +5 -3
  9. data/lib/mongo/gridfs/grid_io.rb +5 -3
  10. data/lib/mongo/legacy.rb +9 -2
  11. data/lib/mongo/mongo_client.rb +100 -72
  12. data/lib/mongo/mongo_replica_set_client.rb +46 -60
  13. data/lib/mongo/mongo_sharded_client.rb +5 -66
  14. data/lib/mongo/networking.rb +2 -1
  15. data/lib/mongo/util/node.rb +41 -42
  16. data/lib/mongo/util/pool.rb +15 -43
  17. data/lib/mongo/util/pool_manager.rb +16 -65
  18. data/lib/mongo/util/read_preference.rb +82 -0
  19. data/lib/mongo/util/sharding_pool_manager.rb +0 -86
  20. data/lib/mongo/util/ssl_socket.rb +2 -1
  21. data/lib/mongo/util/support.rb +8 -18
  22. data/lib/mongo/util/tcp_socket.rb +5 -4
  23. data/lib/mongo/util/thread_local_variable_manager.rb +29 -0
  24. data/lib/mongo/util/unix_socket.rb +23 -0
  25. data/lib/mongo/util/uri_parser.rb +31 -18
  26. data/lib/mongo/util/write_concern.rb +7 -2
  27. data/mongo.gemspec +1 -1
  28. data/test/auxillary/repl_set_auth_test.rb +2 -2
  29. data/test/bson/bson_test.rb +1 -1
  30. data/test/bson/byte_buffer_test.rb +24 -26
  31. data/test/bson/hash_with_indifferent_access_test.rb +11 -1
  32. data/test/functional/collection_test.rb +16 -16
  33. data/test/functional/connection_test.rb +1 -4
  34. data/test/functional/db_api_test.rb +14 -10
  35. data/test/functional/pool_test.rb +23 -31
  36. data/test/functional/timeout_test.rb +3 -5
  37. data/test/functional/uri_test.rb +10 -5
  38. data/test/replica_set/basic_test.rb +3 -8
  39. data/test/replica_set/client_test.rb +47 -31
  40. data/test/replica_set/complex_connect_test.rb +12 -10
  41. data/test/replica_set/connection_test.rb +8 -151
  42. data/test/replica_set/count_test.rb +9 -5
  43. data/test/replica_set/cursor_test.rb +17 -27
  44. data/test/replica_set/insert_test.rb +5 -10
  45. data/test/replica_set/query_test.rb +4 -9
  46. data/test/replica_set/read_preference_test.rb +200 -0
  47. data/test/replica_set/refresh_test.rb +54 -65
  48. data/test/replica_set/replication_ack_test.rb +16 -14
  49. data/test/sharded_cluster/basic_test.rb +30 -0
  50. data/test/test_helper.rb +33 -15
  51. data/test/threading/basic_test.rb +79 -0
  52. data/test/tools/mongo_config.rb +62 -22
  53. data/test/unit/client_test.rb +36 -14
  54. data/test/unit/collection_test.rb +23 -0
  55. data/test/unit/connection_test.rb +30 -14
  56. data/test/unit/cursor_test.rb +137 -7
  57. data/test/unit/db_test.rb +17 -4
  58. data/test/unit/grid_test.rb +2 -2
  59. data/test/unit/node_test.rb +2 -1
  60. data/test/unit/pool_manager_test.rb +29 -1
  61. data/test/unit/read_test.rb +15 -15
  62. data/test/unit/safe_test.rb +4 -4
  63. data/test/unit/write_concern_test.rb +4 -4
  64. metadata +134 -143
  65. data/examples/admin.rb +0 -43
  66. data/examples/capped.rb +0 -22
  67. data/examples/cursor.rb +0 -48
  68. data/examples/gridfs.rb +0 -44
  69. data/examples/index_test.rb +0 -126
  70. data/examples/info.rb +0 -31
  71. data/examples/queries.rb +0 -74
  72. data/examples/replica_set.rb +0 -26
  73. data/examples/simple.rb +0 -25
  74. data/examples/strict.rb +0 -35
  75. data/examples/types.rb +0 -36
  76. data/examples/web/thin/load.rb +0 -23
  77. data/examples/web/unicorn/load.rb +0 -25
  78. data/test/support/hash_with_indifferent_access.rb +0 -186
  79. data/test/support/keys.rb +0 -45
  80. data/test/threading/threading_with_large_pool_test.rb +0 -90
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- [![Build Status][travis-img]][travis-url] [![Jenkins Status][jenkins-img]][jenkins-url] [![Code Climate][codeclimate-img]][codeclimate-url]
1
+ [![Build Status][travis-img]][travis-url]
2
+ [![Jenkins Status][jenkins-img]][jenkins-url]
3
+ [![Code Climate][codeclimate-img]][codeclimate-url]
4
+ [![Latest Version][version-img]][version-url]
2
5
 
3
6
  [travis-img]: https://secure.travis-ci.org/mongodb/mongo-ruby-driver.png
4
7
  [travis-url]: http://travis-ci.org/mongodb/mongo-ruby-driver
@@ -6,6 +9,8 @@
6
9
  [codeclimate-url]: https://codeclimate.com/github/mongodb/mongo-ruby-driver
7
10
  [jenkins-img]: https://jenkins.10gen.com/job/mongo-ruby-driver/badge/icon
8
11
  [jenkins-url]: https://jenkins.10gen.com/job/mongo-ruby-driver/
12
+ [version-img]: https://badge.fury.io/rb/mongo.png
13
+ [version-url]: http://badge.fury.io/rb/mongo
9
14
  [api-url]: http://api.mongodb.org/ruby/current
10
15
 
11
16
  # Documentation
@@ -97,30 +102,14 @@ you can install it as a gem from the source by typing:
97
102
 
98
103
  For extensive examples, see the [MongoDB Ruby Tutorial](https://github.com/mongodb/mongo-ruby-driver/wiki/Tutorial).
99
104
 
100
- Bundled with the driver are many examples, located in the "docs/examples" subdirectory. Samples include using
101
- the driver and using the GridFS class GridStore. MongoDB must be running for
102
- these examples to work, of course.
103
-
104
- Here's how to start MongoDB and run the "simple.rb" example:
105
-
106
- $ cd path/to/mongo
107
- $ ./mongod run
108
- ... then in another window ...
109
- $ cd path/to/mongo-ruby-driver
110
- $ ruby docs/examples/simple.rb
111
-
112
- See also the test code, especially test/test_db_api.rb.
113
-
114
105
  # GridFS
115
106
 
116
107
  The Ruby driver include two abstractions for storing large files: Grid and GridFileSystem.
108
+
117
109
  The Grid class is a Ruby implementation of MongoDB's GridFS file storage
118
- specification. GridFileSystem is essentially the same, but provides a more filesystem-like API
119
- and assumes that filenames are unique.
110
+ specification. GridFileSystem is essentially the same, but provides a more filesystem-like API and assumes that filenames are unique.
120
111
 
121
- An instance of both classes represents an individual file store. See the API reference
122
- for details, and see examples/gridfs.rb for code that uses many of the Grid
123
- features (metadata, content type, seek, tell, etc).
112
+ An instance of both classes represents an individual file store. See the API reference for details.
124
113
 
125
114
  Examples:
126
115
 
@@ -302,26 +291,22 @@ Before running the tests, make sure you install all test dependencies by running
302
291
 
303
292
  $ gem install bundler; bundle install
304
293
 
305
- To run all default test suites, just type:
294
+ To run all default test suites (without the BSON extensions) just type:
306
295
 
307
296
  $ rake test
308
297
 
309
- If you have the source code, you can run the tests. Skip this test with the C extension if you're running JRuby.
310
-
311
- $ rake test:c
312
-
313
- If you want to test the basic Ruby encoder, without the C extension, or if you're running JRuby:
298
+ If you want to run the default test suite using the BSON extensions:
314
299
 
315
- $ rake test:ruby
300
+ $ rake test:ext
316
301
 
317
302
  These will run both unit and functional tests. To run these tests alone:
318
303
 
319
304
  $ rake test:unit
320
305
  $ rake test:functional
321
306
 
322
- To run any individual rake tasks with the C extension enabled, just pass C_EXT=true to the task (don't do this with JRuby):
307
+ To run any individual rake tasks with the BSON extension disabled, just pass BSON_EXT_DISABLED=true to the task:
323
308
 
324
- $ rake test:unit C_EXT=true
309
+ $ rake test:unit BSON_EXT_DISABLED=true
325
310
 
326
311
  If you want to test replica set, you can run the following task:
327
312
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.0
1
+ 1.8.2
@@ -50,8 +50,10 @@ end
50
50
 
51
51
  require 'bson'
52
52
 
53
+ require 'mongo/util/thread_local_variable_manager'
53
54
  require 'mongo/util/conversions'
54
55
  require 'mongo/util/support'
56
+ require 'mongo/util/read_preference'
55
57
  require 'mongo/util/write_concern'
56
58
  require 'mongo/util/core_ext'
57
59
  require 'mongo/util/logging'
@@ -62,6 +64,7 @@ require 'mongo/util/sharding_pool_manager'
62
64
  require 'mongo/util/server_version'
63
65
  require 'mongo/util/ssl_socket'
64
66
  require 'mongo/util/tcp_socket'
67
+ require 'mongo/util/unix_socket'
65
68
  require 'mongo/util/uri_parser'
66
69
 
67
70
 
@@ -29,7 +29,7 @@ module Mongo
29
29
  :write_concern
30
30
 
31
31
  # Read Preference
32
- attr_accessor :read_preference,
32
+ attr_accessor :read,
33
33
  :tag_sets,
34
34
  :acceptable_latency
35
35
 
@@ -37,15 +37,21 @@ module Mongo
37
37
  #
38
38
  # @param [String, Symbol] name the name of the collection.
39
39
  # @param [DB] db a MongoDB database instance.
40
+ #
41
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
42
+ # should be acknowledged
43
+ # @option opts [Boolean] :j (false) Set journal acknowledgement
44
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout
45
+ # @option opts [Boolean] :fsync (false) Set fsync acknowledgement.
46
+ #
47
+ # Notes about write concern:
48
+ # These write concern options will be used for insert, update, and remove methods called on this
49
+ # Collection instance. If no value is provided, the default values set on this instance's DB will be used.
50
+ # These option values can be overridden for any invocation of insert, update, or remove.
40
51
  #
41
52
  # @option opts [:create_pk] :pk (BSON::ObjectId) A primary key factory to use
42
- # other than the default BSON::ObjectId.
43
- #
44
- # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the default write concern
45
- # for +insert+, +update+, and +remove+ method called on this Collection instance. If no
46
- # value is provided, the default values set on this instance's DB will be used. These option
47
- # values can be overridden for any invocation of +insert+, +update+, or +remove+.
48
- # @option options [:primary, :secondary] :read The default read preference for queries
53
+ # other than the default BSON::ObjectId.
54
+ # @option opts [:primary, :secondary] :read The default read preference for queries
49
55
  # initiates from this connection object. If +:secondary+ is chosen, reads will be sent
50
56
  # to one of the closest available secondary nodes. If a secondary node cannot be located, the
51
57
  # read will be sent to the primary. If this option is left unspecified, the value of the read
@@ -101,12 +107,8 @@ module Mongo
101
107
  @cache = Hash.new(0)
102
108
  unless pk_factory
103
109
  @write_concern = get_write_concern(opts, db)
104
- if value = opts[:read]
105
- Mongo::Support.validate_read_preference(value)
106
- else
107
- value = @db.read_preference
108
- end
109
- @read_preference = value.is_a?(Hash) ? value.dup : value
110
+ @read = opts[:read] || @db.read
111
+ Mongo::ReadPreference::validate(@read)
110
112
  @tag_sets = opts.fetch(:tag_sets, @db.tag_sets)
111
113
  @acceptable_latency = opts.fetch(:acceptable_latency, @db.acceptable_latency)
112
114
  end
@@ -233,7 +235,7 @@ module Mongo
233
235
  transformer = opts.delete(:transformer)
234
236
  show_disk_loc = opts.delete(:show_disk_loc)
235
237
  comment = opts.delete(:comment)
236
- read = opts.delete(:read) || @read_preference
238
+ read = opts.delete(:read) || @read
237
239
  tag_sets = opts.delete(:tag_sets) || @tag_sets
238
240
  acceptable_latency = opts.delete(:acceptable_latency) || @acceptable_latency
239
241
 
@@ -326,7 +328,7 @@ module Mongo
326
328
  # :fsync will confirm that a write has been fsynced.
327
329
  # Options provided here will override any write concern options set on this collection,
328
330
  # its database object, or the current connection. See the options
329
- # for +DB#get_last_error+.
331
+ # for DB#get_last_error.
330
332
  #
331
333
  # @raise [Mongo::OperationFailure] will be raised iff :w > 0 and the operation fails.
332
334
  def save(doc, opts={})
@@ -351,14 +353,15 @@ module Mongo
351
353
  # 2nd, a list of invalid documents.
352
354
  # Return this result format only when :collect_on_error is true.
353
355
  #
354
- # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
355
- # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
356
- # :j will confirm a write has been committed to the journal,
357
- # :wtimeout specifies how long to wait for write confirmation,
358
- # :fsync will confirm that a write has been fsynced.
359
- # Options provided here will override any write concern options set on this collection,
360
- # its database object, or the current connection. See the options
361
- # for +DB#get_last_error+.
356
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
357
+ # should be acknowledged
358
+ # @option opts [Boolean] :j (false) Set journal acknowledgement
359
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout
360
+ # @option opts [Boolean] :fsync (false) Set fsync acknowledgement.
361
+ #
362
+ # Notes on write concern:
363
+ # Options provided here will override any write concern options set on this collection,
364
+ # its database object, or the current connection. See the options for +DB#get_last_error+.
362
365
  #
363
366
  # @option opts [Boolean] :continue_on_error (+false+) If true, then
364
367
  # continue a bulk insert even if one of the documents inserted
@@ -388,14 +391,15 @@ module Mongo
388
391
  # @param [Hash] selector
389
392
  # If specified, only matching documents will be removed.
390
393
  #
391
- # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
392
- # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
393
- # :j will confirm a write has been committed to the journal,
394
- # :wtimeout specifies how long to wait for write confirmation,
395
- # :fsync will confirm that a write has been fsynced.
396
- # Options provided here will override any write concern options set on this collection,
397
- # its database object, or the current connection. See the options
398
- # for +DB#get_last_error+.
394
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
395
+ # should be acknowledged
396
+ # @option opts [Boolean] :j (false) Set journal acknowledgement
397
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout
398
+ # @option opts [Boolean] :fsync (false) Set fsync acknowledgement.
399
+ #
400
+ # Notes on write concern:
401
+ # Options provided here will override any write concern options set on this collection,
402
+ # its database object, or the current connection. See the options for +DB#get_last_error+.
399
403
  #
400
404
  # @example remove all documents from the 'users' collection:
401
405
  # users.remove
@@ -441,14 +445,15 @@ module Mongo
441
445
  # @option opts [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
442
446
  # @option opts [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
443
447
  # just the first matching document. Note: only works in MongoDB 1.1.3 or later.
444
- # @option opts [Hash] :w, :j, :wtimeout, :fsync Set the write concern for this operation.
445
- # :w > 0 will run a +getlasterror+ command on the database to report any assertion.
446
- # :j will confirm a write has been committed to the journal,
447
- # :wtimeout specifies how long to wait for write confirmation,
448
- # :fsync will confirm that a write has been fsynced.
449
- # Options provided here will override any write concern options set on this collection,
450
- # its database object, or the current connection. See the options
451
- # for +DB#get_last_error+.
448
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
449
+ # should be acknowledged
450
+ # @option opts [Boolean] :j (false) Set journal acknowledgement
451
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout
452
+ # @option opts [Boolean] :fsync (false) Set fsync acknowledgement.
453
+ #
454
+ # Notes on write concern:
455
+ # Options provided here will override any write concern options set on this collection,
456
+ # its database object, or the current connection. See the options for DB#get_last_error.
452
457
  #
453
458
  # @return [Hash, true] Returns a Hash containing the last error object if acknowledging writes.
454
459
  # Otherwise, returns true.
@@ -506,6 +511,9 @@ module Mongo
506
511
  # @option opts [Integer] :min (nil) specify the minimum longitude and latitude for a geo index.
507
512
  # @option opts [Integer] :max (nil) specify the maximum longitude and latitude for a geo index.
508
513
  #
514
+ # @example Creating a compound index using a hash: (Ruby 1.9 and above)
515
+ # @posts.create_index({'subject' => Mongo::ASCENDING, 'created_at' => Mongo::DESCENDING})
516
+ #
509
517
  # @example Creating a compound index:
510
518
  # @posts.create_index([['subject', Mongo::ASCENDING], ['created_at', Mongo::DESCENDING]])
511
519
  #
@@ -640,12 +648,16 @@ module Mongo
640
648
  #
641
649
  # '$sort' Sorts all input documents and returns them to the pipeline in sorted order.
642
650
  #
651
+ # @option opts [:primary, :secondary] :read Read preference indicating which server to perform this query
652
+ # on. See Collection#find for more details.
653
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
654
+ #
643
655
  # @return [Array] An Array with the aggregate command's results.
644
656
  #
645
657
  # @raise MongoArgumentError if operators either aren't supplied or aren't in the correct format.
646
658
  # @raise MongoOperationFailure if the aggregate command fails.
647
659
  #
648
- def aggregate(pipeline=nil)
660
+ def aggregate(pipeline=nil, opts={})
649
661
  raise MongoArgumentError, "pipeline must be an array of operators" unless pipeline.class == Array
650
662
  raise MongoArgumentError, "pipeline operators must be hashes" unless pipeline.all? { |op| op.class == Hash }
651
663
 
@@ -653,7 +665,7 @@ module Mongo
653
665
  hash['aggregate'] = self.name
654
666
  hash['pipeline'] = pipeline
655
667
 
656
- result = @db.command(hash)
668
+ result = @db.command(hash, command_options(opts))
657
669
  unless Mongo::Support.ok?(result)
658
670
  raise Mongo::OperationFailure, "aggregate failed: #{result['errmsg']}"
659
671
  end
@@ -683,6 +695,9 @@ module Mongo
683
695
  # the instantiated collection that's returned by default. Note if a collection name isn't returned in the
684
696
  # map-reduce output (as, for example, when using :out => { :inline => 1 }), then you must specify this option
685
697
  # or an ArgumentError will be raised.
698
+ # @option opts [:primary, :secondary] :read Read preference indicating which server to run this map-reduce
699
+ # on. See Collection#find for more details.
700
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
686
701
  #
687
702
  # @return [Collection, Hash] a Mongo::Collection object or a Hash with the map-reduce command's results.
688
703
  #
@@ -705,7 +720,7 @@ module Mongo
705
720
  hash[:sort] = Mongo::Support.format_order_clause(hash[:sort])
706
721
  end
707
722
 
708
- result = @db.command(hash)
723
+ result = @db.command(hash, command_options(opts))
709
724
  unless Mongo::Support.ok?(result)
710
725
  raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
711
726
  end
@@ -740,6 +755,9 @@ module Mongo
740
755
  # @option opts [String, BSON::Code] :finalize (nil) a JavaScript function that receives and modifies
741
756
  # each of the resultant grouped objects. Available only when group is run with command
742
757
  # set to true.
758
+ # @option opts [:primary, :secondary] :read Read preference indicating which server to perform this group
759
+ # on. See Collection#find for more details.
760
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
743
761
  #
744
762
  # @return [Array] the command response consisting of grouped items.
745
763
  def group(opts, condition={}, initial={}, reduce=nil, finalize=nil)
@@ -829,7 +847,7 @@ module Mongo
829
847
  cmd["group"]["$keyf"] = keyf.to_bson_code
830
848
  end
831
849
 
832
- result = @db.command(cmd)
850
+ result = @db.command(cmd, command_options(opts))
833
851
  result["retval"]
834
852
  end
835
853
 
@@ -841,6 +859,11 @@ module Mongo
841
859
  #
842
860
  # @param [String, Symbol, OrderedHash] key or hash to group by.
843
861
  # @param [Hash] query a selector for limiting the result set over which to group.
862
+ # @param [Hash] opts the options for this distinct operation.
863
+ #
864
+ # @option opts [:primary, :secondary] :read Read preference indicating which server to perform this query
865
+ # on. See Collection#find for more details.
866
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
844
867
  #
845
868
  # @example Saving zip codes and ages and returning distinct results.
846
869
  # @collection.save({:zip => 10010, :name => {:age => 27}})
@@ -860,13 +883,14 @@ module Mongo
860
883
  # [27]
861
884
  #
862
885
  # @return [Array] an array of distinct values.
863
- def distinct(key, query=nil)
886
+ def distinct(key, query=nil, opts={})
864
887
  raise MongoArgumentError unless [String, Symbol].include?(key.class)
865
888
  command = BSON::OrderedHash.new
866
889
  command[:distinct] = @name
867
890
  command[:key] = key.to_s
868
891
  command[:query] = query
869
- @db.command(command)["values"]
892
+
893
+ @db.command(command, command_options(opts))["values"]
870
894
  end
871
895
 
872
896
  # Rename this collection.
@@ -931,18 +955,38 @@ module Mongo
931
955
  # @option opts [Hash] :query ({}) A query selector for filtering the documents counted.
932
956
  # @option opts [Integer] :skip (nil) The number of documents to skip.
933
957
  # @option opts [Integer] :limit (nil) The number of documents to limit.
958
+ # @option opts [:primary, :secondary] :read Read preference for this command. See Collection#find for
959
+ # more details.
960
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
934
961
  #
935
962
  # @return [Integer]
936
963
  def count(opts={})
937
964
  find(opts[:query],
938
- :skip => opts[:skip],
939
- :limit => opts[:limit]).count(true)
965
+ :skip => opts[:skip],
966
+ :limit => opts[:limit],
967
+ :read => opts[:read],
968
+ :comment => opts[:comment]).count(true)
940
969
  end
941
970
 
942
971
  alias :size :count
943
972
 
944
973
  protected
945
974
 
975
+ # Parse common options for read-only commands from an input @opts
976
+ # hash and return a hash suitable for passing to DB#command.
977
+ def command_options(opts)
978
+ out = {}
979
+
980
+ if read = opts[:read]
981
+ Mongo::ReadPreference::validate(read)
982
+ else
983
+ read = @read
984
+ end
985
+ out[:read] = read
986
+ out[:comment] = opts[:comment] if opts[:comment]
987
+ out
988
+ end
989
+
946
990
  def normalize_hint_fields(hint)
947
991
  case hint
948
992
  when String
@@ -972,6 +1016,12 @@ module Mongo
972
1016
  field_spec = BSON::OrderedHash.new
973
1017
  if spec.is_a?(String) || spec.is_a?(Symbol)
974
1018
  field_spec[spec.to_s] = 1
1019
+ elsif spec.is_a?(Hash)
1020
+ if RUBY_VERSION < '1.9' && !spec.is_a?(BSON::OrderedHash)
1021
+ raise MongoArgumentError, "Must use OrderedHash in Ruby < 1.9.0"
1022
+ else
1023
+ field_spec = spec.is_a?(BSON::OrderedHash) ? spec : BSON::OrderedHash.try_convert(spec)
1024
+ end
975
1025
  elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
976
1026
  spec.each do |f|
977
1027
  if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
@@ -983,7 +1033,7 @@ module Mongo
983
1033
  end
984
1034
  else
985
1035
  raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
986
- "should be either a string, symbol, or an array of arrays."
1036
+ "should be either a hash (OrderedHash), string, symbol, or an array of arrays."
987
1037
  end
988
1038
  field_spec
989
1039
  end
@@ -27,7 +27,7 @@ module Mongo
27
27
  :order, :hint, :snapshot, :timeout,
28
28
  :full_collection_name, :transformer,
29
29
  :options, :cursor_id, :show_disk_loc,
30
- :comment
30
+ :comment, :read, :tag_sets
31
31
 
32
32
  # Create a new cursor.
33
33
  #
@@ -72,14 +72,10 @@ module Mongo
72
72
  @query_run = false
73
73
 
74
74
  @transformer = opts[:transformer]
75
- if value = opts[:read]
76
- Mongo::Support.validate_read_preference(value)
77
- else
78
- value = collection.read_preference
79
- end
80
- @read_preference = value.is_a?(Hash) ? value.dup : value
81
- @tag_sets = opts.fetch(:tag_sets, @collection.tag_sets)
82
- @acceptable_latency = opts.fetch(:acceptable_latency, @collection.acceptable_latency)
75
+ @read = opts[:read] || @collection.read
76
+ Mongo::ReadPreference::validate(@read)
77
+ @tag_sets = opts[:tag_sets] || @collection.tag_sets
78
+ @acceptable_latency = opts[:acceptable_latency] || @collection.acceptable_latency
83
79
 
84
80
  batch_size(opts[:batch_size] || 0)
85
81
 
@@ -90,7 +86,7 @@ module Mongo
90
86
  if(!@timeout)
91
87
  add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
92
88
  end
93
- if(@read_preference != :primary)
89
+ if(@read != :primary)
94
90
  add_option(OP_QUERY_SLAVE_OK)
95
91
  end
96
92
  if(@tailable)
@@ -188,7 +184,7 @@ module Mongo
188
184
 
189
185
  command.merge!(BSON::OrderedHash["fields", @fields])
190
186
 
191
- response = @db.command(command)
187
+ response = @db.command(command, :read => @read, :comment => @comment)
192
188
  return response['n'].to_i if Mongo::Support.ok?(response)
193
189
  return 0 if response['errmsg'] == "ns missing"
194
190
  raise OperationFailure.new("Count failed: #{response['errmsg']}", response['code'], response)
@@ -406,7 +402,8 @@ module Mongo
406
402
  #
407
403
  # @return [Hash]
408
404
  def query_options_hash
409
- { :selector => @selector,
405
+ BSON::OrderedHash[
406
+ :selector => @selector,
410
407
  :fields => @fields,
411
408
  :skip => @skip,
412
409
  :limit => @limit,
@@ -417,7 +414,7 @@ module Mongo
417
414
  :max_scan => @max_scan,
418
415
  :return_key => @return_key,
419
416
  :show_disk_loc => @show_disk_loc,
420
- :comment => @comment }
417
+ :comment => @comment ]
421
418
  end
422
419
 
423
420
  # Clean output for inspect.
@@ -485,7 +482,7 @@ module Mongo
485
482
  Mongo::Constants::OP_QUERY, message, nil, sock, @command,
486
483
  nil, @options & OP_QUERY_EXHAUST != 0)
487
484
  rescue ConnectionFailure => ex
488
- if tries < 3 && !@socket && (!@command || Mongo::Support.secondary_ok?(@selector))
485
+ if tries < 3 && !@socket && (!@command || Mongo::Support::secondary_ok?(@selector))
489
486
  @connection.unpin_pool(sock.pool) if sock
490
487
  @connection.refresh
491
488
  retry
@@ -545,7 +542,7 @@ module Mongo
545
542
  if @command && !Mongo::Support::secondary_ok?(@selector)
546
543
  @connection.checkout_reader(:primary)
547
544
  else
548
- @connection.checkout_reader(@read_preference, @tag_sets, @acceptable_latency)
545
+ @connection.checkout_reader(@read, @tag_sets, @acceptable_latency)
549
546
  end
550
547
  rescue SystemStackError, NoMemoryError, SystemCallError => ex
551
548
  @connection.close
@@ -590,13 +587,16 @@ module Mongo
590
587
  spec['$returnKey'] = true if @return_key
591
588
  spec['$showDiskLoc'] = true if @show_disk_loc
592
589
  spec['$comment'] = @comment if @comment
590
+ if @connection.mongos? && @read != :primary
591
+ read_pref = Mongo::ReadPreference::mongos(@read, @tag_sets)
592
+ spec['$readPreference'] = read_pref if read_pref
593
+ end
593
594
  spec
594
595
  end
595
596
 
596
- # Returns true if the query contains order, explain, hint, or snapshot.
597
597
  def query_contains_special_fields?
598
598
  @order || @explain || @hint || @snapshot || @show_disk_loc ||
599
- @max_scan || @return_key || @comment
599
+ @max_scan || @return_key || @comment || @connection.mongos?
600
600
  end
601
601
 
602
602
  def close_cursor_if_query_complete