couchbase 3.9.0-aarch64-mingw-ucrt

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/README.md +154 -0
  4. data/ext/extconf.rb +0 -0
  5. data/lib/active_support/cache/couchbase_store.rb +407 -0
  6. data/lib/couchbase/3.4/libcouchbase.so +0 -0
  7. data/lib/couchbase/4.0/libcouchbase.so +0 -0
  8. data/lib/couchbase/analytics_options.rb +109 -0
  9. data/lib/couchbase/authenticator.rb +80 -0
  10. data/lib/couchbase/binary_collection.rb +145 -0
  11. data/lib/couchbase/binary_collection_options.rb +26 -0
  12. data/lib/couchbase/bucket.rb +173 -0
  13. data/lib/couchbase/cluster.rb +555 -0
  14. data/lib/couchbase/cluster_registry.rb +49 -0
  15. data/lib/couchbase/collection.rb +770 -0
  16. data/lib/couchbase/collection_options.rb +401 -0
  17. data/lib/couchbase/config_profiles.rb +57 -0
  18. data/lib/couchbase/configuration.rb +58 -0
  19. data/lib/couchbase/datastructures/couchbase_list.rb +192 -0
  20. data/lib/couchbase/datastructures/couchbase_map.rb +231 -0
  21. data/lib/couchbase/datastructures/couchbase_queue.rb +161 -0
  22. data/lib/couchbase/datastructures/couchbase_set.rb +145 -0
  23. data/lib/couchbase/datastructures.rb +26 -0
  24. data/lib/couchbase/deprecations.rb +61 -0
  25. data/lib/couchbase/diagnostics.rb +183 -0
  26. data/lib/couchbase/errors.rb +438 -0
  27. data/lib/couchbase/fork_hooks.rb +41 -0
  28. data/lib/couchbase/get_replica_strategy.rb +89 -0
  29. data/lib/couchbase/json_transcoder.rb +41 -0
  30. data/lib/couchbase/key_value_scan.rb +119 -0
  31. data/lib/couchbase/libcouchbase.rb +6 -0
  32. data/lib/couchbase/logger.rb +87 -0
  33. data/lib/couchbase/management/analytics_index_manager.rb +1160 -0
  34. data/lib/couchbase/management/bucket_manager.rb +479 -0
  35. data/lib/couchbase/management/collection_manager.rb +515 -0
  36. data/lib/couchbase/management/collection_query_index_manager.rb +247 -0
  37. data/lib/couchbase/management/query_index_manager.rb +671 -0
  38. data/lib/couchbase/management/scope_search_index_manager.rb +267 -0
  39. data/lib/couchbase/management/search_index_manager.rb +649 -0
  40. data/lib/couchbase/management/user_manager.rb +639 -0
  41. data/lib/couchbase/management/view_index_manager.rb +332 -0
  42. data/lib/couchbase/management.rb +31 -0
  43. data/lib/couchbase/metrics/logging_meter.rb +108 -0
  44. data/lib/couchbase/metrics/logging_value_recorder.rb +50 -0
  45. data/lib/couchbase/metrics/meter.rb +27 -0
  46. data/lib/couchbase/metrics/noop_meter.rb +30 -0
  47. data/lib/couchbase/metrics/noop_value_recorder.rb +27 -0
  48. data/lib/couchbase/metrics/value_recorder.rb +25 -0
  49. data/lib/couchbase/mutation_state.rb +65 -0
  50. data/lib/couchbase/options.rb +3097 -0
  51. data/lib/couchbase/protostellar/binary_collection.rb +55 -0
  52. data/lib/couchbase/protostellar/bucket.rb +59 -0
  53. data/lib/couchbase/protostellar/client.rb +97 -0
  54. data/lib/couchbase/protostellar/cluster.rb +182 -0
  55. data/lib/couchbase/protostellar/collection.rb +156 -0
  56. data/lib/couchbase/protostellar/connect_options.rb +63 -0
  57. data/lib/couchbase/protostellar/error_handling.rb +204 -0
  58. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_pb.rb +54 -0
  59. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_services_pb.rb +51 -0
  60. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +42 -0
  61. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +51 -0
  62. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +38 -0
  63. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +52 -0
  64. data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +42 -0
  65. data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +55 -0
  66. data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +51 -0
  67. data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +67 -0
  68. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +31 -0
  69. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +40 -0
  70. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +51 -0
  71. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +54 -0
  72. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_pb.rb +46 -0
  73. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_services_pb.rb +56 -0
  74. data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +74 -0
  75. data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +93 -0
  76. data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +35 -0
  77. data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +40 -0
  78. data/lib/couchbase/protostellar/generated/routing/v2/routing_pb.rb +26 -0
  79. data/lib/couchbase/protostellar/generated/routing/v2/routing_services_pb.rb +43 -0
  80. data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +78 -0
  81. data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +41 -0
  82. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +36 -0
  83. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +66 -0
  84. data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +30 -0
  85. data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +39 -0
  86. data/lib/couchbase/protostellar/generated.rb +9 -0
  87. data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
  88. data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
  89. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
  90. data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
  91. data/lib/couchbase/protostellar/management.rb +24 -0
  92. data/lib/couchbase/protostellar/request.rb +79 -0
  93. data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
  94. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
  95. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +96 -0
  96. data/lib/couchbase/protostellar/request_generator/admin/query.rb +132 -0
  97. data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
  98. data/lib/couchbase/protostellar/request_generator/kv.rb +488 -0
  99. data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
  100. data/lib/couchbase/protostellar/request_generator/search.rb +403 -0
  101. data/lib/couchbase/protostellar/request_generator.rb +26 -0
  102. data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
  103. data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
  104. data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
  105. data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
  106. data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
  107. data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
  108. data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
  109. data/lib/couchbase/protostellar/response_converter.rb +26 -0
  110. data/lib/couchbase/protostellar/retry/action.rb +38 -0
  111. data/lib/couchbase/protostellar/retry/orchestrator.rb +61 -0
  112. data/lib/couchbase/protostellar/retry/reason.rb +67 -0
  113. data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
  114. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  115. data/lib/couchbase/protostellar/retry/strategies.rb +27 -0
  116. data/lib/couchbase/protostellar/retry.rb +28 -0
  117. data/lib/couchbase/protostellar/scope.rb +61 -0
  118. data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
  119. data/lib/couchbase/protostellar/timeouts.rb +83 -0
  120. data/lib/couchbase/protostellar.rb +29 -0
  121. data/lib/couchbase/query_options.rb +122 -0
  122. data/lib/couchbase/railtie.rb +47 -0
  123. data/lib/couchbase/raw_binary_transcoder.rb +39 -0
  124. data/lib/couchbase/raw_json_transcoder.rb +40 -0
  125. data/lib/couchbase/raw_string_transcoder.rb +42 -0
  126. data/lib/couchbase/scope.rb +272 -0
  127. data/lib/couchbase/search_options.rb +1744 -0
  128. data/lib/couchbase/subdoc.rb +293 -0
  129. data/lib/couchbase/tracing/noop_span.rb +29 -0
  130. data/lib/couchbase/tracing/noop_tracer.rb +29 -0
  131. data/lib/couchbase/tracing/request_span.rb +34 -0
  132. data/lib/couchbase/tracing/request_tracer.rb +28 -0
  133. data/lib/couchbase/tracing/threshold_logging_span.rb +112 -0
  134. data/lib/couchbase/tracing/threshold_logging_tracer.rb +231 -0
  135. data/lib/couchbase/transcoder_flags.rb +64 -0
  136. data/lib/couchbase/utils/generic_logger_adapter.rb +40 -0
  137. data/lib/couchbase/utils/hdr_histogram.rb +55 -0
  138. data/lib/couchbase/utils/observability.rb +263 -0
  139. data/lib/couchbase/utils/observability_constants.rb +200 -0
  140. data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
  141. data/lib/couchbase/utils/time.rb +71 -0
  142. data/lib/couchbase/utils.rb +23 -0
  143. data/lib/couchbase/version.rb +25 -0
  144. data/lib/couchbase/view_options.rb +67 -0
  145. data/lib/couchbase.rb +32 -0
  146. data/lib/rails/generators/couchbase/config/config_generator.rb +29 -0
  147. metadata +209 -0
@@ -0,0 +1,770 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020-2021 Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require "couchbase/errors"
18
+ require "couchbase/collection_options"
19
+ require "couchbase/binary_collection"
20
+ require "couchbase/key_value_scan"
21
+ require "couchbase/get_replica_strategy"
22
+
23
+ module Couchbase
24
+ # Provides access to all collection APIs
25
+ class Collection
26
+ attr_reader :bucket_name
27
+ attr_reader :scope_name
28
+ attr_reader :name
29
+
30
+ alias inspect to_s
31
+
32
+ # @param [Couchbase::Backend] backend
33
+ # @param [String] bucket_name name of the bucket
34
+ # @param [String] scope_name name of the scope
35
+ # @param [String] collection_name name of the collection
36
+ # @param [Observability::Wrapper] observability wrapper containing tracer and meter
37
+ #
38
+ # @api private
39
+ def initialize(backend, bucket_name, scope_name, collection_name, observability)
40
+ @backend = backend
41
+ @bucket_name = bucket_name
42
+ @scope_name = scope_name
43
+ @name = collection_name
44
+ @observability = observability
45
+ end
46
+
47
+ # Provides access to the binary APIs, not used for JSON documents
48
+ #
49
+ # @return [BinaryCollection]
50
+ def binary
51
+ BinaryCollection.new(self)
52
+ end
53
+
54
+ # @return [Management::CollectionQueryIndexManager]
55
+ def query_indexes
56
+ Management::CollectionQueryIndexManager.new(@backend, @bucket_name, @scope_name, @name, @observability)
57
+ end
58
+
59
+ # Fetches the full document from the collection
60
+ #
61
+ # @param [String] id the document id which is used to uniquely identify it
62
+ # @param [Options::Get] options request customization
63
+ #
64
+ # @example Get document contents
65
+ # res = collection.get("customer123")
66
+ # res.content["addresses"]
67
+ #
68
+ # # {"billing"=>
69
+ # # {"line1"=>"123 Any Street", "line2"=>"Anytown", "country"=>"United Kingdom"},
70
+ # # "delivery"=>
71
+ # # {"line1"=>"123 Any Street", "line2"=>"Anytown", "country"=>"United Kingdom"}}
72
+ #
73
+ # @example Get partial document using projections
74
+ # res = collection.get("customer123", Options::Get(projections: ["name", "addresses.billing"]))
75
+ # res.content
76
+ #
77
+ # # {"addresses"=>
78
+ # # {"billing"=>
79
+ # # {"country"=>"United Kingdom",
80
+ # # "line1"=>"123 Any Street",
81
+ # # "line2"=>"Anytown"}},
82
+ # # "name"=>"Douglas Reynholm"}
83
+ #
84
+ # @return [GetResult]
85
+ def get(id, options = Options::Get::DEFAULT)
86
+ @observability.record_operation(Observability::OP_GET, options.parent_span, self, :kv) do |obs_handler|
87
+ resp = if options.need_projected_get?
88
+ @backend.document_get_projected(bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
89
+ else
90
+ @backend.document_get(bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
91
+ end
92
+ GetResult.new do |res|
93
+ res.transcoder = options.transcoder
94
+ res.cas = resp[:cas]
95
+ res.flags = resp[:flags]
96
+ res.encoded = resp[:content]
97
+ res.expiry = resp[:expiry] if resp.key?(:expiry)
98
+ end
99
+ end
100
+ end
101
+
102
+ # Fetches multiple documents from the collection.
103
+ #
104
+ # @note that it will not generate {Error::DocumentNotFound} exceptions in this case. The caller should check
105
+ # {GetResult#error} property of the result
106
+ #
107
+ # @param [Array<String>] ids the array of document identifiers
108
+ # @param [Options::GetMulti] options request customization
109
+ #
110
+ # @example Fetch "foo" and "bar" in a batch
111
+ # res = collection.get(["foo", "bar"], Options::GetMulti(timeout: 3_000))
112
+ # res[0].content #=> content of "foo"
113
+ # res[1].content #=> content of "bar"
114
+ #
115
+ # @return [Array<GetResult>]
116
+ def get_multi(ids, options = Options::GetMulti::DEFAULT)
117
+ @observability.record_operation(Observability::OP_GET_MULTI, options.parent_span, self, :kv) do |_obs_handler|
118
+ resp = @backend.document_get_multi(ids.map { |id| [bucket_name, @scope_name, @name, id] }, options.to_backend)
119
+ resp.map do |entry|
120
+ GetResult.new do |res|
121
+ res.transcoder = options.transcoder
122
+ res.id = entry[:id]
123
+ res.cas = entry[:cas]
124
+ res.flags = entry[:flags]
125
+ res.encoded = entry[:content]
126
+ res.error = entry[:error]
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # Fetches the full document and write-locks it for the given duration
133
+ #
134
+ # @param [String] id the document id which is used to uniquely identify it.
135
+ # @param [Integer, #in_seconds] lock_time how long to lock the document (values over 30 seconds will be capped)
136
+ # @param [Options::GetAndLock] options request customization
137
+ #
138
+ # @example Retrieve document and lock for 10 seconds
139
+ # collection.get_and_lock("customer123", 10, Options::GetAndLock(timeout: 3_000))
140
+ #
141
+ # @example Update document pessimistically
142
+ # res = collection.get_and_lock("customer123", 10)
143
+ # user_data = res.content
144
+ # user_data["admin"] = true
145
+ # collection.replace("user", user_data, Options::Upsert(cas: res.cas))
146
+ #
147
+ # @return [GetResult]
148
+ def get_and_lock(id, lock_time, options = Options::GetAndLock::DEFAULT)
149
+ @observability.record_operation(Observability::OP_GET_AND_LOCK, options.parent_span, self, :kv) do |obs_handler|
150
+ resp = @backend.document_get_and_lock(bucket_name, @scope_name, @name, id,
151
+ lock_time.respond_to?(:in_seconds) ? lock_time.in_seconds : lock_time,
152
+ options.to_backend, obs_handler)
153
+ GetResult.new do |res|
154
+ res.transcoder = options.transcoder
155
+ res.cas = resp[:cas]
156
+ res.flags = resp[:flags]
157
+ res.encoded = resp[:content]
158
+ end
159
+ end
160
+ end
161
+
162
+ # Fetches a full document and resets its expiration time to the duration provided
163
+ #
164
+ # @param [String] id the document id which is used to uniquely identify it.
165
+ # @param [Integer, #in_seconds, Time] expiry the new expiration time for the document
166
+ # @param [Options::GetAndTouch] options request customization
167
+ #
168
+ # @example Retrieve document and prolong its expiration for another 10 seconds
169
+ # collection.get_and_touch("customer123", 10)
170
+ #
171
+ # @return [GetResult]
172
+ def get_and_touch(id, expiry, options = Options::GetAndTouch::DEFAULT)
173
+ @observability.record_operation(Observability::OP_GET_AND_TOUCH, options.parent_span, self, :kv) do |obs_handler|
174
+ resp = @backend.document_get_and_touch(bucket_name, @scope_name, @name, id,
175
+ Utils::Time.extract_expiry_time(expiry),
176
+ options.to_backend, obs_handler)
177
+ GetResult.new do |res|
178
+ res.transcoder = options.transcoder
179
+ res.cas = resp[:cas]
180
+ res.flags = resp[:flags]
181
+ res.encoded = resp[:content]
182
+ end
183
+ end
184
+ end
185
+
186
+ # Reads from all available replicas and the active node and returns the results
187
+ #
188
+ # @param [String] id the document id which is used to uniquely identify it.
189
+ # @param [Options::GetAllReplicas] options request customization
190
+ #
191
+ # @return [Array<GetReplicaResult>]
192
+ def get_all_replicas(id, options = Options::GetAllReplicas::DEFAULT)
193
+ @observability.record_operation(Observability::OP_GET_ALL_REPLICAS, options.parent_span, self, :kv) do |obs_handler|
194
+ resp = @backend.document_get_all_replicas(@bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
195
+ resp.map do |entry|
196
+ GetReplicaResult.new do |res|
197
+ res.transcoder = options.transcoder
198
+ res.cas = entry[:cas]
199
+ res.flags = entry[:flags]
200
+ res.encoded = entry[:content]
201
+ res.is_replica = entry[:is_replica]
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ # Reads the document from the replica the strategy selects.
208
+ #
209
+ # @param [String] id the document id which is used to uniquely identify it.
210
+ # @param [GetReplicaStrategy] strategy selects the replica to read from
211
+ # @param [Options::GetReplica] options request customization
212
+ #
213
+ # @example Read the document from the second replica
214
+ # res = collection.get_replica("airline_10", GetReplicaStrategy.from_index(:second))
215
+ # res.replica? #=> true
216
+ # res.content["name"]
217
+ #
218
+ # @raise [Error::DocumentNotFoundOnReplica] if the replica does not hold the document
219
+ # @raise [Error::ReplicaIndexOutOfBounds] if the bucket is not configured with the requested replica
220
+ # @raise [Error::ReplicaIndexCurrentlyUnavailable] if the vbucket map places the requested replica on no node
221
+ # @raise [Error::UnambiguousTimeout] if the replica does not answer before the timeout
222
+ # @raise [Error::FeatureNotAvailable] over +couchbase2://+, which does not carry the operation
223
+ #
224
+ # @since 3.9.0
225
+ #
226
+ # @return [GetReplicaResult]
227
+ def get_replica(id, strategy, options = Options::GetReplica::DEFAULT)
228
+ @observability.record_operation(Observability::OP_GET_REPLICA, options.parent_span, self, :kv) do |obs_handler|
229
+ resp = @backend.document_get_replica(@bucket_name, @scope_name, @name, id, strategy.to_backend,
230
+ options.to_backend, obs_handler)
231
+ GetReplicaResult.new do |res|
232
+ res.transcoder = options.transcoder
233
+ res.cas = resp[:cas]
234
+ res.flags = resp[:flags]
235
+ res.encoded = resp[:content]
236
+ res.is_replica = true
237
+ end
238
+ end
239
+ end
240
+
241
+ # Reads all available replicas and active, and returns the first found.
242
+ #
243
+ # @param [String] id the document id which is used to uniquely identify it.
244
+ # @param [Options::GetAnyReplica] options request customization
245
+ #
246
+ # @example Get document contents
247
+ # res = collection.get_any_replica("customer123")
248
+ # res.replica? #=> true when a replica answered first, false for the active copy
249
+ # res.content["addresses"]
250
+ #
251
+ # # {"billing"=>
252
+ # # {"line1"=>"123 Any Street", "line2"=>"Anytown", "country"=>"United Kingdom"},
253
+ # # "delivery"=>
254
+ # # {"line1"=>"123 Any Street", "line2"=>"Anytown", "country"=>"United Kingdom"}}
255
+ #
256
+ #
257
+ # @return [GetReplicaResult]
258
+ def get_any_replica(id, options = Options::GetAnyReplica::DEFAULT)
259
+ @observability.record_operation(Observability::OP_GET_ANY_REPLICA, options.parent_span, self, :kv) do |obs_handler|
260
+ resp = @backend.document_get_any_replica(@bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
261
+ GetReplicaResult.new do |res|
262
+ res.transcoder = options.transcoder
263
+ res.cas = resp[:cas]
264
+ res.flags = resp[:flags]
265
+ res.encoded = resp[:content]
266
+ res.is_replica = resp[:is_replica]
267
+ end
268
+ end
269
+ end
270
+
271
+ # Checks if the given document ID exists on the active partition.
272
+ #
273
+ # @param [String] id the document id which is used to uniquely identify it.
274
+ # @param [Options::Exists] options request customization
275
+ #
276
+ # @example Check if the document exists without fetching its contents
277
+ # res = collection.exists("customer123")
278
+ # res.exists? #=> true
279
+ #
280
+ # @return [ExistsResult]
281
+ def exists(id, options = Options::Exists::DEFAULT)
282
+ @observability.record_operation(Observability::OP_EXISTS, options.parent_span, self, :kv) do |obs_handler|
283
+ resp = @backend.document_exists(bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
284
+ ExistsResult.new do |res|
285
+ res.deleted = resp[:deleted]
286
+ res.exists = resp[:exists]
287
+ res.expiry = resp[:expiry]
288
+ res.flags = resp[:flags]
289
+ res.sequence_number = resp[:sequence_number]
290
+ res.datatype = resp[:datatype]
291
+ res.cas = resp[:cas]
292
+ end
293
+ end
294
+ end
295
+
296
+ # Removes a document from the collection
297
+ #
298
+ # @param [String] id the document id which is used to uniquely identify it.
299
+ # @param [Options::Remove] options request customization
300
+ #
301
+ # @example Remove the document in collection
302
+ # res = collection.remove("customer123")
303
+ # res.cas #=> 241994216651798
304
+ #
305
+ # @example Remove the document in collection, but apply optimistic lock
306
+ # res = collection.upsert("mydoc", {"foo" => 42})
307
+ # res.cas #=> 7751414725654
308
+ #
309
+ # begin
310
+ # res = collection.remove("mydoc", Options::Remove(cas: 3735928559))
311
+ # rescue Error::CasMismatch
312
+ # puts "Failed to remove the document, it might be changed by other application"
313
+ # end
314
+ #
315
+ # @return [MutationResult]
316
+ def remove(id, options = Options::Remove::DEFAULT)
317
+ @observability.record_operation(Observability::OP_REMOVE, options.parent_span, self, :kv) do |obs_handler|
318
+ obs_handler.add_durability_level(options.durability_level)
319
+ resp = @backend.document_remove(bucket_name, @scope_name, @name, id, options.to_backend, obs_handler)
320
+ MutationResult.new do |res|
321
+ res.cas = resp[:cas]
322
+ res.mutation_token = extract_mutation_token(resp)
323
+ end
324
+ end
325
+ end
326
+
327
+ # Removes a list of the documents from the collection
328
+ #
329
+ # @note that it will not generate {Error::DocumentNotFound} or {Error::CasMismatch} exceptions in this case.
330
+ # The caller should check {MutationResult#error} property of the result
331
+ #
332
+ # @param [Array<String, Array>] ids the array of document ids, or ID/CAS pairs +[String,Integer]+
333
+ # @param [Options::RemoveMulti] options request customization
334
+ #
335
+ # @example Remove two documents in collection. For "mydoc" apply optimistic lock
336
+ # res = collection.upsert("mydoc", {"foo" => 42})
337
+ # res.cas #=> 7751414725654
338
+ #
339
+ # res = collection.remove_multi(["foo", ["mydoc", res.cas]])
340
+ # if res[1].error.is_a?(Error::CasMismatch)
341
+ # puts "Failed to remove the document, it might be changed by other application"
342
+ # end
343
+ #
344
+ # @return [Array<MutationResult>]
345
+ def remove_multi(ids, options = Options::RemoveMulti::DEFAULT)
346
+ @observability.record_operation(Observability::OP_REMOVE_MULTI, options.parent_span, self, :kv) do |obs_handler|
347
+ obs_handler.add_durability_level(options.durability_level)
348
+ resp = @backend.document_remove_multi(bucket_name, @scope_name, @name, ids.map do |id|
349
+ case id
350
+ when String
351
+ [id, nil]
352
+ when Array
353
+ id
354
+ else
355
+ raise ArgumentError, "id argument of remove_multi must be a String or Array<String, Integer>, given: #{id.inspect}"
356
+ end
357
+ end, options.to_backend)
358
+ resp.map do |entry|
359
+ MutationResult.new do |res|
360
+ res.cas = entry[:cas]
361
+ res.mutation_token = extract_mutation_token(entry)
362
+ res.error = entry[:error]
363
+ res.id = entry[:id]
364
+ end
365
+ end
366
+ end
367
+ end
368
+
369
+ # Inserts a full document which does not exist yet
370
+ #
371
+ # @param [String] id the document id which is used to uniquely identify it.
372
+ # @param [Object] content the document content to insert
373
+ # @param [Options::Insert] options request customization
374
+ #
375
+ # @example Insert new document in collection
376
+ # res = collection.insert("mydoc", {"foo" => 42}, Options::Insert(expiry: 20))
377
+ # res.cas #=> 242287264414742
378
+ #
379
+ # @example Handle error when the document already exists
380
+ # collection.exists("mydoc").exists? #=> true
381
+ # begin
382
+ # res = collection.insert("mydoc", {"foo" => 42})
383
+ # rescue Error::DocumentExists
384
+ # puts "Failed to insert the document, it already exists in the collection"
385
+ # end
386
+ #
387
+ # @return [MutationResult]
388
+ def insert(id, content, options = Options::Insert::DEFAULT)
389
+ @observability.record_operation(Observability::OP_INSERT, options.parent_span, self, :kv) do |obs_handler|
390
+ obs_handler.add_durability_level(options.durability_level)
391
+ blob, flags = encode_content(content, options, obs_handler)
392
+ resp = @backend.document_insert(bucket_name, @scope_name, @name, id, blob, flags, options.to_backend, obs_handler)
393
+ MutationResult.new do |res|
394
+ res.cas = resp[:cas]
395
+ res.mutation_token = extract_mutation_token(resp)
396
+ end
397
+ end
398
+ end
399
+
400
+ # Upserts (inserts or updates) a full document which might or might not exist yet
401
+ #
402
+ # @param [String] id the document id which is used to uniquely identify it.
403
+ # @param [Object] content the document content to upsert
404
+ # @param [Options::Upsert] options request customization
405
+ #
406
+ # @example Upsert new document in collection
407
+ # res = collection.upsert("mydoc", {"foo" => 42}, Options::Upsert(expiry: 20))
408
+ # res.cas #=> 242287264414742
409
+ #
410
+ # @return [MutationResult]
411
+ def upsert(id, content, options = Options::Upsert::DEFAULT)
412
+ @observability.record_operation(Observability::OP_UPSERT, options.parent_span, self, :kv) do |obs_handler|
413
+ obs_handler.add_durability_level(options.durability_level)
414
+ blob, flags = encode_content(content, options, obs_handler)
415
+ resp = @backend.document_upsert(bucket_name, @scope_name, @name, id, blob, flags, options.to_backend, obs_handler)
416
+ MutationResult.new do |res|
417
+ res.cas = resp[:cas]
418
+ res.mutation_token = extract_mutation_token(resp)
419
+ end
420
+ end
421
+ end
422
+
423
+ # Upserts (inserts or updates) a list of documents which might or might not exist yet
424
+ #
425
+ # @note that it will not generate exceptions in this case. The caller should check {MutationResult#error} property of the
426
+ # result
427
+ #
428
+ # @param [Array<Array>] id_content array of tuples +String,Object+, where first entry treated as document key,
429
+ # and the second as value to upsert.
430
+ # @param [Options::UpsertMulti] options request customization
431
+ #
432
+ # @example Upsert two documents with IDs "foo" and "bar" into a collection
433
+ # res = collection.upsert_multi([
434
+ # "foo", {"foo" => 42},
435
+ # "bar", {"bar" => "some value"}
436
+ # ])
437
+ # res[0].cas #=> 7751414725654
438
+ # res[1].cas #=> 7751418925851
439
+ #
440
+ # @return [Array<MutationResult>]
441
+ def upsert_multi(id_content, options = Options::UpsertMulti::DEFAULT)
442
+ @observability.record_operation(Observability::OP_UPSERT, options.parent_span, self, :kv) do |obs_handler|
443
+ encoded_id_content = encode_content_multi(id_content, options, obs_handler)
444
+ resp = @backend.document_upsert_multi(bucket_name, @scope_name, @name, encoded_id_content, options.to_backend)
445
+ resp.map do |entry|
446
+ MutationResult.new do |res|
447
+ res.cas = entry[:cas]
448
+ res.mutation_token = extract_mutation_token(entry)
449
+ res.error = entry[:error]
450
+ res.id = entry[:id]
451
+ end
452
+ end
453
+ end
454
+ end
455
+
456
+ # Replaces a full document which already exists
457
+ #
458
+ # @param [String] id the document id which is used to uniquely identify it.
459
+ # @param [Object] content the document content to upsert
460
+ # @param [Options::Replace] options request customization
461
+ #
462
+ # @example Replace new document in collection with optimistic locking
463
+ # res = collection.get("mydoc")
464
+ # res = collection.replace("mydoc", {"foo" => 42}, Options::Replace(cas: res.cas))
465
+ # res.cas #=> 242287264414742
466
+ #
467
+ # @return [MutationResult]
468
+ def replace(id, content, options = Options::Replace::DEFAULT)
469
+ @observability.record_operation(Observability::OP_REPLACE, options.parent_span, self, :kv) do |obs_handler|
470
+ obs_handler.add_durability_level(options.durability_level)
471
+ blob, flags = encode_content(content, options, obs_handler)
472
+ resp = @backend.document_replace(bucket_name, @scope_name, @name, id, blob, flags, options.to_backend, obs_handler)
473
+ MutationResult.new do |res|
474
+ res.cas = resp[:cas]
475
+ res.mutation_token = extract_mutation_token(resp)
476
+ end
477
+ end
478
+ end
479
+
480
+ # Update the expiration of the document with the given id
481
+ #
482
+ # @param [String] id the document id which is used to uniquely identify it.
483
+ # @param [Integer, #in_seconds, Time] expiry new expiration time for the document
484
+ # @param [Options::Touch] options request customization
485
+ #
486
+ # @example Reset expiration timer for document to 30 seconds
487
+ # res = collection.touch("customer123", 30)
488
+ #
489
+ # @return [MutationResult]
490
+ def touch(id, expiry, options = Options::Touch::DEFAULT)
491
+ @observability.record_operation(Observability::OP_TOUCH, options.parent_span, self, :kv) do |obs_handler|
492
+ resp = @backend.document_touch(bucket_name, @scope_name, @name, id,
493
+ Utils::Time.extract_expiry_time(expiry),
494
+ options.to_backend, obs_handler)
495
+ MutationResult.new do |res|
496
+ res.cas = resp[:cas]
497
+ end
498
+ end
499
+ end
500
+
501
+ # Unlocks a document if it has been locked previously
502
+ #
503
+ # @param [String] id the document id which is used to uniquely identify it.
504
+ # @param [Integer] cas CAS value which is needed to unlock the document
505
+ # @param [Options::Unlock] options request customization
506
+ #
507
+ # @example Lock (pessimistically) and unlock document
508
+ # res = collection.get_and_lock("customer123", 10)
509
+ # collection.unlock("customer123", res.cas)
510
+ #
511
+ # @return [void]
512
+ #
513
+ # @raise [Error::DocumentNotFound]
514
+ def unlock(id, cas, options = Options::Unlock::DEFAULT)
515
+ @observability.record_operation(Observability::OP_UNLOCK, options.parent_span, self, :kv) do |obs_handler|
516
+ @backend.document_unlock(bucket_name, @scope_name, @name, id, cas, options.to_backend, obs_handler)
517
+ end
518
+ end
519
+
520
+ # Performs lookups to document fragments
521
+ #
522
+ # @param [String] id the document id which is used to uniquely identify it.
523
+ # @param [Array<LookupInSpec>] specs the list of specifications which describe the types of the lookups to perform
524
+ # @param [Options::LookupIn] options request customization
525
+ #
526
+ # @example Get list of IDs of completed purchases
527
+ # lookup_specs = [
528
+ # LookupInSpec::get("purchases.complete")
529
+ # ]
530
+ # collection.lookup_in("customer123", lookup_specs)
531
+ #
532
+ # @example Retrieve country name and check if pending purchases array is empty
533
+ # collection.lookup_in "customer123", [
534
+ # LookupInSpec.get("addresses.delivery.country"),
535
+ # LookupInSpec.exists("purchases.pending[-1]"),
536
+ # ]
537
+ # @return [LookupInResult]
538
+ def lookup_in(id, specs, options = Options::LookupIn::DEFAULT)
539
+ @observability.record_operation(Observability::OP_LOOKUP_IN, options.parent_span, self, :kv) do |obs_handler|
540
+ resp = @backend.document_lookup_in(
541
+ bucket_name, @scope_name, @name, id,
542
+ specs.map do |s|
543
+ {
544
+ opcode: s.type,
545
+ xattr: s.xattr?,
546
+ path: s.path,
547
+ }
548
+ end, options.to_backend, obs_handler
549
+ )
550
+ LookupInResult.new do |res|
551
+ res.transcoder = options.transcoder
552
+ res.cas = resp[:cas]
553
+ res.deleted = resp[:deleted]
554
+ res.encoded = resp[:fields].map do |field|
555
+ SubDocumentField.new do |f|
556
+ f.exists = field[:exists]
557
+ f.index = field[:index]
558
+ f.path = field[:path]
559
+ f.value = field[:value]
560
+ f.error = field[:error]
561
+ end
562
+ end
563
+ end
564
+ end
565
+ end
566
+
567
+ # Performs lookups to document fragments. Reads from the active node and all available replicas and returns the
568
+ # first result found
569
+ #
570
+ # @param [String] id the document id which is used to uniquely identify it.
571
+ # @param [Array<LookupInSpec>] specs the list of specifications which describe the types of the lookups to perform
572
+ # @param [Options::LookupInAnyReplica] options request customization
573
+ #
574
+ # @return [LookupInReplicaResult]
575
+ #
576
+ # @raise [Error::DocumentIrretrievable]
577
+ # @raise [Error::Timeout]
578
+ # @raise [Error::CouchbaseError]
579
+ # @raise [Error::FeatureNotAvailable]
580
+ def lookup_in_any_replica(id, specs, options = Options::LookupInAnyReplica::DEFAULT)
581
+ @observability.record_operation(Observability::OP_LOOKUP_IN_ANY_REPLICA, options.parent_span, self, :kv) do |obs_handler|
582
+ resp = @backend.document_lookup_in_any_replica(
583
+ bucket_name, @scope_name, @name, id,
584
+ specs.map do |s|
585
+ {
586
+ opcode: s.type,
587
+ xattr: s.xattr?,
588
+ path: s.path,
589
+ }
590
+ end, options.to_backend, obs_handler
591
+ )
592
+ extract_lookup_in_replica_result(resp, options)
593
+ end
594
+ end
595
+
596
+ # Performs lookups to document fragments. Reads from the active node and all available replicas and returns all of
597
+ # the results
598
+ #
599
+ # @param [String] id the document id which is used to uniquely identify it.
600
+ # @param [Array<LookupInSpec>] specs the list of specifications which describe the types of the lookups to perform
601
+ # @param [Options::LookupInAllReplicas] options request customization
602
+ #
603
+ # @return [Array<LookupInReplicaResult>]
604
+ #
605
+ # @raise [Error::DocumentNotFound]
606
+ # @raise [Error::Timeout]
607
+ # @raise [Error::CouchbaseError]
608
+ # @raise [Error::FeatureNotAvailable]
609
+ def lookup_in_all_replicas(id, specs, options = Options::LookupInAllReplicas::DEFAULT)
610
+ @observability.record_operation(Observability::OP_LOOKUP_IN_ALL_REPLICAS, options.parent_span, self, :kv) do |obs_handler|
611
+ resp = @backend.document_lookup_in_all_replicas(
612
+ bucket_name, @scope_name, @name, id,
613
+ specs.map do |s|
614
+ {
615
+ opcode: s.type,
616
+ xattr: s.xattr?,
617
+ path: s.path,
618
+ }
619
+ end, options.to_backend, obs_handler
620
+ )
621
+ resp.map do |entry|
622
+ extract_lookup_in_replica_result(entry, options)
623
+ end
624
+ end
625
+ end
626
+
627
+ # Performs mutations to document fragments
628
+ #
629
+ # @param [String] id the document id which is used to uniquely identify it.
630
+ # @param [Array<MutateInSpec>] specs the list of specifications which describe the types of the lookups to perform
631
+ # @param [Options::MutateIn] options request customization
632
+ #
633
+ # @example Append number into subarray of the document
634
+ # mutation_specs = [
635
+ # MutateInSpec::array_append("purchases.complete", [42])
636
+ # ]
637
+ # collection.mutate_in("customer123", mutation_specs, Options::MutateIn(expiry: 10))
638
+ #
639
+ # @example Write meta attribute, remove array entry and replace email field
640
+ # collection.mutate_in("customer123", [
641
+ # MutateInSpec.upsert("_framework.model_type", "Customer").xattr,
642
+ # MutateInSpec.remove("addresses.billing[2]"),
643
+ # MutateInSpec.replace("email", "dougr96@hotmail.com"),
644
+ # ])
645
+ #
646
+ # @return [MutateInResult]
647
+ def mutate_in(id, specs, options = Options::MutateIn::DEFAULT)
648
+ @observability.record_operation(Observability::OP_MUTATE_IN, options.parent_span, self, :kv) do |obs_handler|
649
+ obs_handler.add_durability_level(options.durability_level)
650
+ resp = @backend.document_mutate_in(
651
+ bucket_name, @scope_name, @name, id,
652
+ specs.map do |s|
653
+ {
654
+ opcode: s.type,
655
+ path: s.path,
656
+ param: s.param,
657
+ xattr: s.xattr?,
658
+ expand_macros: s.expand_macros?,
659
+ create_path: s.create_path?,
660
+ }
661
+ end, options.to_backend, obs_handler
662
+ )
663
+ MutateInResult.new do |res|
664
+ res.transcoder = options.transcoder
665
+ res.cas = resp[:cas]
666
+ res.deleted = resp[:deleted]
667
+ res.mutation_token = extract_mutation_token(resp)
668
+ res.encoded = resp[:fields].map do |field|
669
+ SubDocumentField.new do |f|
670
+ f.index = field[:index]
671
+ f.path = field[:path]
672
+ f.value = field[:value]
673
+ end
674
+ end
675
+ end
676
+ end
677
+ end
678
+
679
+ # Performs a key-value scan operation on the collection
680
+ #
681
+ # @param [RangeScan, PrefixScan, SamplingScan] scan_type the type of the scan
682
+ # @param [Options::Scan] options request customization
683
+ #
684
+ # @return [ScanResults]
685
+ #
686
+ # @example Get a sample of up to 5 documents from the collection and store their IDs in an array
687
+ # result = collection.scan(SamplingScan.new(5), Options::Scan.new(ids_only: true))
688
+ # ids = result.map { |item| item.id }
689
+ #
690
+ # @example Get all documents whose ID starts with 'customer_1' and output their content
691
+ # result = collection.scan(PrefixScan.new("customer_1"))
692
+ # result.each { |item| puts item.content }
693
+ #
694
+ # @example Get all documents with ID between 'customer_1' and 'customer_2', excluding 'customer_2' and output their content
695
+ # result = collection.scan(RangeScan.new(
696
+ # from: ScanTerm.new("customer_1"),
697
+ # to: ScanTerm.new("customer_2", exclusive: true)
698
+ # ))
699
+ # result.each { |item| puts item.content }
700
+ #
701
+ # @note
702
+ # Use this API for low concurrency batch queries where latency is not a critical as the system may have to scan
703
+ # a lot of documents to find the matching documents. For low latency range queries, it is recommended that you use
704
+ # SQL++ with the necessary indexes.
705
+ def scan(scan_type, options = Options::Scan::DEFAULT)
706
+ @observability.record_operation(Observability::OP_SCAN, options.parent_span, self, :kv) do |_obs_handler|
707
+ ScanResults.new(
708
+ core_scan_result: @backend.document_scan_create(
709
+ @bucket_name, @scope_name, @name, scan_type.to_backend, options.to_backend
710
+ ),
711
+ transcoder: options.transcoder,
712
+ )
713
+ end
714
+ end
715
+
716
+ private
717
+
718
+ def encode_content(content, options, obs_handler)
719
+ return [content, 0] unless options.transcoder
720
+
721
+ obs_handler.with_request_encoding_span do
722
+ options.transcoder.encode(content)
723
+ end
724
+ end
725
+
726
+ def encode_content_multi(id_content_pairs, options, obs_handler)
727
+ unless options.transcoder
728
+ return id_content_pairs.map do |(id, content)|
729
+ [id, content, 0]
730
+ end
731
+ end
732
+
733
+ obs_handler.with_request_encoding_span do
734
+ id_content_pairs.map do |(id, content)|
735
+ blob, flags = options.transcoder.encode(content)
736
+ [id, blob, flags]
737
+ end
738
+ end
739
+ end
740
+
741
+ def extract_mutation_token(resp)
742
+ return unless resp.key?(:mutation_token)
743
+
744
+ MutationToken.new do |token|
745
+ token.partition_id = resp[:mutation_token][:partition_id]
746
+ token.partition_uuid = resp[:mutation_token][:partition_uuid]
747
+ token.sequence_number = resp[:mutation_token][:sequence_number]
748
+ token.bucket_name = resp[:mutation_token][:bucket_name]
749
+ end
750
+ end
751
+
752
+ def extract_lookup_in_replica_result(resp, options)
753
+ LookupInReplicaResult.new do |res|
754
+ res.transcoder = options.transcoder
755
+ res.cas = resp[:cas]
756
+ res.deleted = resp[:deleted]
757
+ res.is_replica = resp[:is_replica]
758
+ res.encoded = resp[:fields].map do |field|
759
+ SubDocumentField.new do |f|
760
+ f.exists = field[:exists]
761
+ f.index = field[:index]
762
+ f.path = field[:path]
763
+ f.value = field[:value]
764
+ f.error = field[:error]
765
+ end
766
+ end
767
+ end
768
+ end
769
+ end
770
+ end