couchbase 3.5.3-arm64-darwin

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