mongo 2.13.1 → 2.14.0.rc1

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 (161) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -4
  4. data/lib/mongo.rb +9 -0
  5. data/lib/mongo/address/ipv4.rb +1 -1
  6. data/lib/mongo/address/ipv6.rb +1 -1
  7. data/lib/mongo/bulk_write.rb +17 -0
  8. data/lib/mongo/caching_cursor.rb +74 -0
  9. data/lib/mongo/client.rb +47 -8
  10. data/lib/mongo/cluster.rb +3 -3
  11. data/lib/mongo/cluster/topology/single.rb +1 -1
  12. data/lib/mongo/collection.rb +26 -0
  13. data/lib/mongo/collection/view.rb +24 -20
  14. data/lib/mongo/collection/view/aggregation.rb +25 -4
  15. data/lib/mongo/collection/view/builder/find_command.rb +38 -18
  16. data/lib/mongo/collection/view/explainable.rb +27 -8
  17. data/lib/mongo/collection/view/iterable.rb +72 -12
  18. data/lib/mongo/collection/view/readable.rb +12 -2
  19. data/lib/mongo/collection/view/writable.rb +15 -1
  20. data/lib/mongo/crypt/encryption_io.rb +6 -6
  21. data/lib/mongo/cursor.rb +1 -0
  22. data/lib/mongo/database.rb +6 -0
  23. data/lib/mongo/error.rb +2 -0
  24. data/lib/mongo/error/invalid_read_concern.rb +28 -0
  25. data/lib/mongo/error/server_certificate_revoked.rb +22 -0
  26. data/lib/mongo/error/unsupported_option.rb +14 -12
  27. data/lib/mongo/lint.rb +2 -1
  28. data/lib/mongo/logger.rb +3 -3
  29. data/lib/mongo/operation.rb +2 -0
  30. data/lib/mongo/operation/aggregate/result.rb +9 -8
  31. data/lib/mongo/operation/collections_info/result.rb +2 -0
  32. data/lib/mongo/operation/delete/bulk_result.rb +2 -0
  33. data/lib/mongo/operation/delete/result.rb +3 -0
  34. data/lib/mongo/operation/explain/command.rb +4 -0
  35. data/lib/mongo/operation/explain/legacy.rb +4 -0
  36. data/lib/mongo/operation/explain/op_msg.rb +6 -0
  37. data/lib/mongo/operation/explain/result.rb +3 -0
  38. data/lib/mongo/operation/find/legacy/result.rb +2 -0
  39. data/lib/mongo/operation/find/result.rb +3 -0
  40. data/lib/mongo/operation/get_more/result.rb +3 -0
  41. data/lib/mongo/operation/indexes/result.rb +5 -0
  42. data/lib/mongo/operation/insert/bulk_result.rb +5 -0
  43. data/lib/mongo/operation/insert/result.rb +5 -0
  44. data/lib/mongo/operation/list_collections/result.rb +5 -0
  45. data/lib/mongo/operation/map_reduce/result.rb +10 -0
  46. data/lib/mongo/operation/parallel_scan/result.rb +4 -0
  47. data/lib/mongo/operation/result.rb +35 -6
  48. data/lib/mongo/operation/shared/bypass_document_validation.rb +1 -0
  49. data/lib/mongo/operation/shared/causal_consistency_supported.rb +1 -0
  50. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +2 -0
  51. data/lib/mongo/operation/shared/executable.rb +1 -0
  52. data/lib/mongo/operation/shared/idable.rb +2 -1
  53. data/lib/mongo/operation/shared/limited.rb +1 -0
  54. data/lib/mongo/operation/shared/object_id_generator.rb +1 -0
  55. data/lib/mongo/operation/shared/result/aggregatable.rb +1 -0
  56. data/lib/mongo/operation/shared/sessions_supported.rb +1 -0
  57. data/lib/mongo/operation/shared/specifiable.rb +1 -0
  58. data/lib/mongo/operation/shared/write.rb +1 -0
  59. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -0
  60. data/lib/mongo/operation/update/legacy/result.rb +7 -0
  61. data/lib/mongo/operation/update/result.rb +8 -0
  62. data/lib/mongo/operation/users_info/result.rb +3 -0
  63. data/lib/mongo/query_cache.rb +242 -0
  64. data/lib/mongo/retryable.rb +8 -1
  65. data/lib/mongo/server.rb +5 -1
  66. data/lib/mongo/server/connection_common.rb +2 -2
  67. data/lib/mongo/server/connection_pool.rb +3 -0
  68. data/lib/mongo/server/monitor.rb +1 -1
  69. data/lib/mongo/server/monitor/connection.rb +3 -3
  70. data/lib/mongo/server/pending_connection.rb +2 -2
  71. data/lib/mongo/server/push_monitor.rb +1 -1
  72. data/lib/mongo/server_selector/base.rb +5 -1
  73. data/lib/mongo/session.rb +3 -0
  74. data/lib/mongo/socket.rb +6 -4
  75. data/lib/mongo/socket/ocsp_cache.rb +97 -0
  76. data/lib/mongo/socket/ocsp_verifier.rb +368 -0
  77. data/lib/mongo/socket/ssl.rb +45 -24
  78. data/lib/mongo/srv/monitor.rb +7 -13
  79. data/lib/mongo/srv/resolver.rb +14 -10
  80. data/lib/mongo/timeout.rb +2 -0
  81. data/lib/mongo/uri.rb +21 -390
  82. data/lib/mongo/uri/options_mapper.rb +582 -0
  83. data/lib/mongo/uri/srv_protocol.rb +3 -2
  84. data/lib/mongo/utils.rb +12 -1
  85. data/lib/mongo/version.rb +1 -1
  86. data/spec/NOTES.aws-auth.md +12 -7
  87. data/spec/README.md +56 -1
  88. data/spec/integration/bulk_write_spec.rb +48 -0
  89. data/spec/integration/client_authentication_options_spec.rb +55 -28
  90. data/spec/integration/connection_pool_populator_spec.rb +3 -1
  91. data/spec/integration/cursor_reaping_spec.rb +53 -17
  92. data/spec/integration/ocsp_connectivity_spec.rb +26 -0
  93. data/spec/integration/ocsp_verifier_cache_spec.rb +188 -0
  94. data/spec/integration/ocsp_verifier_spec.rb +334 -0
  95. data/spec/integration/query_cache_spec.rb +1045 -0
  96. data/spec/integration/query_cache_transactions_spec.rb +179 -0
  97. data/spec/integration/retryable_writes/retryable_writes_40_and_newer_spec.rb +1 -0
  98. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -0
  99. data/spec/integration/sdam_error_handling_spec.rb +68 -0
  100. data/spec/integration/server_selection_spec.rb +36 -0
  101. data/spec/integration/srv_monitoring_spec.rb +38 -3
  102. data/spec/integration/srv_spec.rb +56 -0
  103. data/spec/lite_spec_helper.rb +3 -1
  104. data/spec/mongo/address_spec.rb +1 -1
  105. data/spec/mongo/caching_cursor_spec.rb +70 -0
  106. data/spec/mongo/client_construction_spec.rb +54 -1
  107. data/spec/mongo/client_spec.rb +40 -0
  108. data/spec/mongo/cluster/topology/single_spec.rb +14 -5
  109. data/spec/mongo/cluster_spec.rb +3 -0
  110. data/spec/mongo/collection/view/explainable_spec.rb +87 -4
  111. data/spec/mongo/collection/view/map_reduce_spec.rb +2 -0
  112. data/spec/mongo/collection_spec.rb +60 -0
  113. data/spec/mongo/crypt/auto_decryption_context_spec.rb +1 -1
  114. data/spec/mongo/crypt/auto_encryption_context_spec.rb +1 -1
  115. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +1 -1
  116. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +1 -1
  117. data/spec/mongo/database_spec.rb +44 -0
  118. data/spec/mongo/error/no_server_available_spec.rb +1 -1
  119. data/spec/mongo/logger_spec.rb +13 -11
  120. data/spec/mongo/query_cache_spec.rb +279 -0
  121. data/spec/mongo/server/connection_pool_spec.rb +7 -3
  122. data/spec/mongo/server/connection_spec.rb +14 -7
  123. data/spec/mongo/socket/ssl_spec.rb +1 -1
  124. data/spec/mongo/socket_spec.rb +1 -1
  125. data/spec/mongo/uri/srv_protocol_spec.rb +64 -33
  126. data/spec/mongo/uri_option_parsing_spec.rb +11 -11
  127. data/spec/mongo/uri_spec.rb +68 -41
  128. data/spec/mongo/utils_spec.rb +39 -0
  129. data/spec/runners/auth.rb +3 -0
  130. data/spec/runners/connection_string.rb +35 -124
  131. data/spec/spec_tests/cmap_spec.rb +7 -3
  132. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +0 -1
  133. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  134. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +6 -2
  135. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +3 -0
  136. data/spec/spec_tests/data/connection_string/valid-warnings.yml +24 -0
  137. data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -3
  138. data/spec/spec_tests/data/sdam_monitoring/standalone.yml +2 -2
  139. data/spec/spec_tests/data/sdam_monitoring/standalone_repeated.yml +2 -2
  140. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
  141. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +2 -2
  142. data/spec/spec_tests/data/uri_options/auth-options.yml +25 -0
  143. data/spec/spec_tests/data/uri_options/compression-options.yml +6 -3
  144. data/spec/spec_tests/data/uri_options/read-preference-options.yml +24 -0
  145. data/spec/spec_tests/data/uri_options/ruby-connection-options.yml +1 -0
  146. data/spec/spec_tests/data/uri_options/tls-options.yml +160 -4
  147. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +9 -1
  148. data/spec/spec_tests/uri_options_spec.rb +31 -33
  149. data/spec/support/certificates/atlas-ocsp-ca.crt +28 -0
  150. data/spec/support/certificates/atlas-ocsp.crt +41 -0
  151. data/spec/support/client_registry_macros.rb +11 -2
  152. data/spec/support/common_shortcuts.rb +45 -0
  153. data/spec/support/constraints.rb +23 -0
  154. data/spec/support/lite_constraints.rb +24 -0
  155. data/spec/support/matchers.rb +16 -0
  156. data/spec/support/ocsp +1 -0
  157. data/spec/support/session_registry.rb +52 -0
  158. data/spec/support/spec_config.rb +22 -0
  159. data/spec/support/utils.rb +19 -1
  160. metadata +38 -3
  161. metadata.gz.sig +0 -0
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Custom behavior for operations that support the bypassdocumentvalidation option.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module BypassDocumentValidation
22
23
 
23
24
  private
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Custom behavior for operations that support causal consistency.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module CausalConsistencySupported
22
23
 
23
24
  private
@@ -14,6 +14,8 @@
14
14
 
15
15
  module Mongo
16
16
  module Operation
17
+
18
+ # @api private
17
19
  module CollectionsInfoOrListCollections
18
20
  include PolymorphicLookup
19
21
 
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Shared executable behavior of operations.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module Executable
22
23
 
23
24
  include ResponseHandling
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Shared behavior of operations that require its documents to each have an id.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module Idable
22
23
 
23
24
  def documents
@@ -61,4 +62,4 @@ module Mongo
61
62
  end
62
63
  end
63
64
  end
64
- end
65
+ end
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Shared behavior of operations that require its documents to each have an id.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module Limited
22
23
 
23
24
  private
@@ -18,6 +18,7 @@ module Mongo
18
18
  # The default generator of ids for documents.
19
19
  #
20
20
  # @since 2.2.0
21
+ # @api private
21
22
  class ObjectIdGenerator
22
23
 
23
24
  # Generate a new id.
@@ -19,6 +19,7 @@ module Mongo
19
19
  # Defines custom behavior of bulk write results
20
20
  #
21
21
  # @since 2.0.0
22
+ # @api private
22
23
  module Aggregatable
23
24
 
24
25
  # Aggregate the write errors returned from this result.
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Shared behavior of operations that support a session.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module SessionsSupported
22
23
 
23
24
  private
@@ -19,6 +19,7 @@ module Mongo
19
19
  # various values from the spec.
20
20
  #
21
21
  # @since 2.0.0
22
+ # @api private
22
23
  module Specifiable
23
24
 
24
25
  # The field for database name.
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Shared behavior of operations that write (update, insert, delete).
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module Write
22
23
 
23
24
  include ResponseHandling
@@ -18,6 +18,7 @@ module Mongo
18
18
  # Custom behavior for operations that support write concern.
19
19
  #
20
20
  # @since 2.5.2
21
+ # @api private
21
22
  module WriteConcernSupported
22
23
 
23
24
  private
@@ -21,17 +21,20 @@ module Mongo
21
21
  # version <= 2.4.
22
22
  #
23
23
  # @since 2.0.0
24
+ # @api semiprivate
24
25
  class Result < Operation::Result
25
26
  include Operation::Result::UseLegacyErrorParser
26
27
 
27
28
  # Whether an existing document was updated.
28
29
  #
29
30
  # @since 2.0.0
31
+ # @api private
30
32
  UPDATED_EXISTING = 'updatedExisting'.freeze
31
33
 
32
34
  # The upserted docs field in the result.
33
35
  #
34
36
  # @since 2.0.0
37
+ # @api private
35
38
  UPSERTED = 'upserted'.freeze
36
39
 
37
40
  # Get the number of documents matched.
@@ -42,6 +45,7 @@ module Mongo
42
45
  # @return [ Integer ] The matched count.
43
46
  #
44
47
  # @since 2.0.0
48
+ # @api public
45
49
  def matched_count
46
50
  return 0 unless acknowledged?
47
51
  if upsert?
@@ -59,6 +63,7 @@ module Mongo
59
63
  # @return [ nil ] Always omitted for legacy versions.
60
64
  #
61
65
  # @since 2.0.0
66
+ # @api public
62
67
  def modified_count; end
63
68
 
64
69
  # The identifier of the inserted document if an upsert
@@ -70,6 +75,7 @@ module Mongo
70
75
  # @return [ Object ] The upserted id.
71
76
  #
72
77
  # @since 2.0.0
78
+ # @api public
73
79
  def upserted_id
74
80
  first[UPSERTED] if upsert?
75
81
  end
@@ -82,6 +88,7 @@ module Mongo
82
88
  # @return [ Integer ] The number upserted.
83
89
  #
84
90
  # @since 2.4.2
91
+ # @api public
85
92
  def upserted_count
86
93
  upsert? ? n : 0
87
94
  end
@@ -19,16 +19,19 @@ module Mongo
19
19
  # Defines custom behavior of results for an update.
20
20
  #
21
21
  # @since 2.0.0
22
+ # @api semiprivate
22
23
  class Result < Operation::Result
23
24
 
24
25
  # The number of modified docs field in the result.
25
26
  #
26
27
  # @since 2.0.0
28
+ # @api private
27
29
  MODIFIED = 'nModified'.freeze
28
30
 
29
31
  # The upserted docs field in the result.
30
32
  #
31
33
  # @since 2.0.0
34
+ # @api private
32
35
  UPSERTED = 'upserted'.freeze
33
36
 
34
37
  # Get the number of documents matched.
@@ -39,6 +42,7 @@ module Mongo
39
42
  # @return [ Integer ] The matched count.
40
43
  #
41
44
  # @since 2.0.0
45
+ # @api public
42
46
  def matched_count
43
47
  return 0 unless acknowledged?
44
48
  if upsert?
@@ -56,6 +60,7 @@ module Mongo
56
60
  # @return [ Integer ] The modified count.
57
61
  #
58
62
  # @since 2.0.0
63
+ # @api public
59
64
  def modified_count
60
65
  return 0 unless acknowledged?
61
66
  first[MODIFIED]
@@ -70,6 +75,7 @@ module Mongo
70
75
  # @return [ Object ] The upserted id.
71
76
  #
72
77
  # @since 2.0.0
78
+ # @api public
73
79
  def upserted_id
74
80
  return nil unless upsert?
75
81
  upsert?.first['_id']
@@ -83,10 +89,12 @@ module Mongo
83
89
  # @return [ Integer ] The number upserted.
84
90
  #
85
91
  # @since 2.4.2
92
+ # @api public
86
93
  def upserted_count
87
94
  upsert? ? n : 0
88
95
  end
89
96
 
97
+ # @api public
90
98
  def bulk_result
91
99
  BulkResult.new(@replies, connection_description)
92
100
  end
@@ -20,13 +20,16 @@ module Mongo
20
20
  # usersInfo command.
21
21
  #
22
22
  # @since 2.1.0
23
+ # @api semiprivate
23
24
  class Result < Operation::Result
24
25
 
25
26
  # The field name for the users document in a usersInfo result.
26
27
  #
27
28
  # @since 2.1.0
29
+ # @api private
28
30
  USERS = 'users'.freeze
29
31
 
32
+ # @api public
30
33
  def documents
31
34
  reply.documents.first[USERS]
32
35
  end
@@ -0,0 +1,242 @@
1
+ # Copyright (C) 2020 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module QueryCache
17
+ class << self
18
+
19
+ # Set whether the cache is enabled.
20
+ #
21
+ # @example Set if the cache is enabled.
22
+ # QueryCache.enabled = true
23
+ #
24
+ # @param [ true, false ] value The enabled value.
25
+ def enabled=(value)
26
+ Thread.current["[mongo]:query_cache:enabled"] = value
27
+ end
28
+
29
+ # Is the query cache enabled on the current thread?
30
+ #
31
+ # @example Is the query cache enabled?
32
+ # QueryCache.enabled?
33
+ #
34
+ # @return [ true, false ] If the cache is enabled.
35
+ def enabled?
36
+ !!Thread.current["[mongo]:query_cache:enabled"]
37
+ end
38
+
39
+ # Execute the block while using the query cache.
40
+ #
41
+ # @example Execute with the cache.
42
+ # QueryCache.cache { collection.find }
43
+ #
44
+ # @return [ Object ] The result of the block.
45
+ def cache
46
+ enabled = enabled?
47
+ self.enabled = true
48
+ begin
49
+ yield
50
+ ensure
51
+ self.enabled = enabled
52
+ end
53
+ end
54
+
55
+ # Execute the block with the query cache disabled.
56
+ #
57
+ # @example Execute without the cache.
58
+ # QueryCache.uncached { collection.find }
59
+ #
60
+ # @return [ Object ] The result of the block.
61
+ def uncached
62
+ enabled = enabled?
63
+ self.enabled = false
64
+ begin
65
+ yield
66
+ ensure
67
+ self.enabled = enabled
68
+ end
69
+ end
70
+
71
+ # Get the cached queries.
72
+ #
73
+ # @example Get the cached queries from the current thread.
74
+ # QueryCache.cache_table
75
+ #
76
+ # @return [ Hash ] The hash of cached queries.
77
+ private def cache_table
78
+ Thread.current["[mongo]:query_cache"] ||= {}
79
+ end
80
+
81
+ # Clear the query cache.
82
+ #
83
+ # @example Clear the cache.
84
+ # QueryCache.clear
85
+ #
86
+ # @return [ nil ] Always nil.
87
+ def clear
88
+ Thread.current["[mongo]:query_cache"] = nil
89
+ end
90
+
91
+ # Clear the section of the query cache storing cursors with results
92
+ # from this namespace.
93
+ #
94
+ # @param [ String ] namespace The namespace to be cleared, in the format
95
+ # "database.collection".
96
+ #
97
+ # @return [ nil ] Always nil.
98
+ #
99
+ # @api private
100
+ def clear_namespace(namespace)
101
+ cache_table.delete(namespace)
102
+ # The nil key is where cursors are stored that could potentially read from
103
+ # multiple collections. This key should be cleared on every write operation
104
+ # to prevent returning stale data.
105
+ cache_table.delete(nil)
106
+ nil
107
+ end
108
+
109
+ # Store a CachingCursor instance in the query cache associated with the
110
+ # specified query options.
111
+ #
112
+ # @param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.
113
+ #
114
+ # @option opts [ String | nil ] namespace The namespace of the query,
115
+ # in the format "database_name.collection_name".
116
+ # @option opts [ Array, Hash ] selector The selector passed to the query.
117
+ # For most queries, this will be a Hash, but for aggregations, this
118
+ # will be an Array representing the aggregation pipeline. May not be nil.
119
+ # @option opts [ Integer | nil ] skip The skip value of the query.
120
+ # @option opts [ Hash | nil ] sort The order of the query results
121
+ # (e.g. { name: -1 }).
122
+ # @option opts [ Integer | nil ] limit The limit value of the query.
123
+ # @option opts [ Hash | nil ] projection The projection of the query
124
+ # results (e.g. { name: 1 }).
125
+ # @option opts [ Hash | nil ] collation The collation of the query
126
+ # (e.g. { "locale" => "fr_CA" }).
127
+ # @option opts [ Hash | nil ] read_concern The read concern of the query
128
+ # (e.g. { level: :majority }).
129
+ # @option opts [ Hash | nil ] read_preference The read preference of
130
+ # the query (e.g. { mode: :secondary }).
131
+ # @option opts [ Boolean | nil ] multi_collection Whether the query
132
+ # results could potentially come from multiple collections. When true,
133
+ # these results will be stored under the nil namespace key and cleared
134
+ # on every write command.
135
+ #
136
+ # @return [ true ] Always true.
137
+ #
138
+ # @api private
139
+ def set(cursor, **opts)
140
+ _cache_key = cache_key(**opts)
141
+ _namespace_key = namespace_key(**opts)
142
+
143
+ cache_table[_namespace_key] ||= {}
144
+ cache_table[_namespace_key][_cache_key] = cursor
145
+
146
+ true
147
+ end
148
+
149
+ # For the given query options, retrieve a cached cursor that can be used
150
+ # to obtain the correct query results, if one exists in the cache.
151
+ #
152
+ # @option opts [ String | nil ] namespace The namespace of the query,
153
+ # in the format "database_name.collection_name".
154
+ # @option opts [ Array, Hash ] selector The selector passed to the query.
155
+ # For most queries, this will be a Hash, but for aggregations, this
156
+ # will be an Array representing the aggregation pipeline. May not be nil.
157
+ # @option opts [ Integer | nil ] skip The skip value of the query.
158
+ # @option opts [ Hash | nil ] sort The order of the query results
159
+ # (e.g. { name: -1 }).
160
+ # @option opts [ Integer | nil ] limit The limit value of the query.
161
+ # @option opts [ Hash | nil ] projection The projection of the query
162
+ # results (e.g. { name: 1 }).
163
+ # @option opts [ Hash | nil ] collation The collation of the query
164
+ # (e.g. { "locale" => "fr_CA" }).
165
+ # @option opts [ Hash | nil ] read_concern The read concern of the query
166
+ # (e.g. { level: :majority }).
167
+ # @option opts [ Hash | nil ] read_preference The read preference of
168
+ # the query (e.g. { mode: :secondary }).
169
+ # @option opts [ Boolean | nil ] multi_collection Whether the query
170
+ # results could potentially come from multiple collections. When true,
171
+ # these results will be stored under the nil namespace key and cleared
172
+ # on every write command.
173
+ #
174
+ # @return [ Mongo::CachingCursor | nil ] Returns a CachingCursor if one
175
+ # exists in the query cache, otherwise returns nil.
176
+ #
177
+ # @api private
178
+ def get(**opts)
179
+ limit = opts[:limit]
180
+ _namespace_key = namespace_key(**opts)
181
+ _cache_key = cache_key(**opts)
182
+
183
+ namespace_hash = cache_table[_namespace_key]
184
+ return nil unless namespace_hash
185
+
186
+ caching_cursor = namespace_hash[_cache_key]
187
+ return nil unless caching_cursor
188
+
189
+ caching_cursor_limit = caching_cursor.view.limit
190
+
191
+ # There are two scenarios in which a caching cursor could fulfill the
192
+ # query:
193
+ # 1. The query has a limit, and the stored cursor has no limit or
194
+ # a larger limit.
195
+ # 2. The query has no limit and the stored cursor has no limit.
196
+ #
197
+ # Otherwise, return nil because the stored cursor will not satisfy
198
+ # the query.
199
+ if limit && (caching_cursor_limit.nil? || caching_cursor_limit >= limit)
200
+ caching_cursor
201
+ elsif limit.nil? && caching_cursor_limit.nil?
202
+ caching_cursor
203
+ else
204
+ nil
205
+ end
206
+ end
207
+
208
+ private
209
+
210
+ def cache_key(**opts)
211
+ unless opts[:namespace]
212
+ raise ArgumentError.new("Cannot generate cache key without namespace")
213
+ end
214
+ unless opts[:selector]
215
+ raise ArgumentError.new("Cannot generate cache key without selector")
216
+ end
217
+
218
+ [
219
+ opts[:namespace],
220
+ opts[:selector],
221
+ opts[:skip],
222
+ opts[:sort],
223
+ opts[:projection],
224
+ opts[:collation],
225
+ opts[:read_concern],
226
+ opts[:read_preference]
227
+ ]
228
+ end
229
+
230
+ # If the cached results can come from multiple collections, store this
231
+ # cursor under the nil namespace to be cleared on every write operation.
232
+ # Otherwise, store it under the specified namespace.
233
+ def namespace_key(**opts)
234
+ if opts[:multi_collection]
235
+ nil
236
+ else
237
+ opts[:namespace]
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end