mongo 1.8.0 → 1.8.2

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.
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