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