couchbase 3.4.0-arm64-darwin-20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) 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 +339 -0
  6. data/lib/couchbase/analytics_options.rb +107 -0
  7. data/lib/couchbase/authenticator.rb +65 -0
  8. data/lib/couchbase/binary_collection.rb +128 -0
  9. data/lib/couchbase/binary_collection_options.rb +24 -0
  10. data/lib/couchbase/bucket.rb +144 -0
  11. data/lib/couchbase/cluster.rb +439 -0
  12. data/lib/couchbase/cluster_registry.rb +44 -0
  13. data/lib/couchbase/collection.rb +589 -0
  14. data/lib/couchbase/collection_options.rb +300 -0
  15. data/lib/couchbase/config_profiles.rb +55 -0
  16. data/lib/couchbase/configuration.rb +57 -0
  17. data/lib/couchbase/datastructures/couchbase_list.rb +160 -0
  18. data/lib/couchbase/datastructures/couchbase_map.rb +194 -0
  19. data/lib/couchbase/datastructures/couchbase_queue.rb +134 -0
  20. data/lib/couchbase/datastructures/couchbase_set.rb +128 -0
  21. data/lib/couchbase/datastructures.rb +24 -0
  22. data/lib/couchbase/diagnostics.rb +181 -0
  23. data/lib/couchbase/errors.rb +351 -0
  24. data/lib/couchbase/json_transcoder.rb +32 -0
  25. data/lib/couchbase/libcouchbase.bundle +0 -0
  26. data/lib/couchbase/logger.rb +85 -0
  27. data/lib/couchbase/management/analytics_index_manager.rb +1127 -0
  28. data/lib/couchbase/management/bucket_manager.rb +436 -0
  29. data/lib/couchbase/management/collection_manager.rb +321 -0
  30. data/lib/couchbase/management/query_index_manager.rb +520 -0
  31. data/lib/couchbase/management/search_index_manager.rb +408 -0
  32. data/lib/couchbase/management/user_manager.rb +468 -0
  33. data/lib/couchbase/management/view_index_manager.rb +237 -0
  34. data/lib/couchbase/management.rb +27 -0
  35. data/lib/couchbase/mutation_state.rb +63 -0
  36. data/lib/couchbase/options.rb +2580 -0
  37. data/lib/couchbase/query_options.rb +120 -0
  38. data/lib/couchbase/railtie.rb +45 -0
  39. data/lib/couchbase/scope.rb +232 -0
  40. data/lib/couchbase/search_options.rb +1570 -0
  41. data/lib/couchbase/subdoc.rb +290 -0
  42. data/lib/couchbase/utils/generic_logger_adapter.rb +38 -0
  43. data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
  44. data/lib/couchbase/utils/time.rb +56 -0
  45. data/lib/couchbase/utils.rb +21 -0
  46. data/lib/couchbase/version.rb +23 -0
  47. data/lib/couchbase/view_options.rb +65 -0
  48. data/lib/couchbase.rb +20 -0
  49. data/lib/rails/generators/couchbase/config/config_generator.rb +27 -0
  50. metadata +101 -0
@@ -0,0 +1,339 @@
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"
16
+ require "digest/sha2"
17
+ require "active_support/cache"
18
+
19
+ module ActiveSupport
20
+ module Cache
21
+ # A cache store implementation which stores data in Couchbase: https://couchbase.com
22
+ #
23
+ # * Local cache. Hot in-memory primary cache within block/middleware scope.
24
+ # * +read_multi+ and +write_multi+ support.
25
+ # * +delete_matched+ support using N1QL queries.
26
+ # * +clear+ for erasing whole collection (optionally can flush the bucket).
27
+ #
28
+ # To use this store, add the select it in application config
29
+ #
30
+ # config.cache_store = :couchbase_store, {
31
+ # connection_string: "couchbase://localhost",
32
+ # username: "app_cache_user",
33
+ # password: "s3cret",
34
+ # bucket: "app_cache"
35
+ # }
36
+ #
37
+ # @see https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
38
+ class CouchbaseStore < Store
39
+ MAX_KEY_BYTESIZE = 250
40
+ DEFAULT_ERROR_HANDLER = lambda do |method:, returning:, exception:, logger: CouchbaseStore.logger|
41
+ logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
42
+ end
43
+
44
+ # Advertise cache versioning support.
45
+ def self.supports_cache_versioning?
46
+ true
47
+ end
48
+
49
+ prepend Strategy::LocalCache
50
+
51
+ def initialize(options = nil)
52
+ super
53
+ @error_handler = @options.delete(:error_handler) { DEFAULT_ERROR_HANDLER }
54
+ @couchbase_options = {}
55
+ @couchbase_options[:connection_string] =
56
+ @options.delete(:connection_string) do
57
+ raise ArgumentError, "Missing connection string for Couchbase cache store. Use :connection_string in the store options"
58
+ end
59
+ @couchbase_options[:username] =
60
+ @options.delete(:username) do
61
+ raise ArgumentError, "Missing username for Couchbase cache store. Use :username in the store options"
62
+ end
63
+ @couchbase_options[:password] =
64
+ @options.delete(:password) do
65
+ raise ArgumentError, "Missing password for Couchbase cache store. Use :password in the store options"
66
+ end
67
+ @couchbase_options[:bucket] =
68
+ @options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
69
+ @couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
70
+ @couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
71
+ @last_mutation_token = nil
72
+ end
73
+
74
+ def collection
75
+ @collection ||= build_collection
76
+ end
77
+
78
+ def cluster
79
+ @cluster ||= build_cluster
80
+ end
81
+
82
+ def inspect
83
+ "#<#{self.class} options=#{options.inspect} collection=#{@collection.inspect}>"
84
+ end
85
+
86
+ # Deletes all entries with keys matching the regular expression.
87
+ #
88
+ # The +matcher+ must be valid pattern for N1QL +REGEXP_MATCHES+ function. More info at
89
+ # https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches
90
+ #
91
+ # Because the operation performed on query engine, and it might take time to propagate changes
92
+ # from key/value engine to the indexer. Therefore the keys, that were created a moment ago
93
+ # might not be deleted.
94
+ #
95
+ # Also this method assumes, that primary index created on the target bucket
96
+ def delete_matched(matcher, _options = nil)
97
+ pattern =
98
+ case matcher
99
+ when Regexp
100
+ matcher.inspect[1..-2]
101
+ when String
102
+ matcher.tr("?", ".").gsub("*", ".*")
103
+ else
104
+ raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
105
+ end
106
+ operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern}, metrics: true)
107
+ operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
108
+ begin
109
+ result = cluster.query("DELETE FROM #{scope_qualifier} cache WHERE REGEXP_MATCHES(META(cache).id, $pattern)", operation_options)
110
+ result.meta_data.metrics.mutation_count
111
+ rescue ::Couchbase::Error::ParsingFailure, ::Couchbase::Error::ServiceNotAvailable
112
+ raise NotImplementedError, "The server does not support delete_matched operation"
113
+ end
114
+ end
115
+
116
+ # Increments an integer value in the cache.
117
+ #
118
+ # Note that it uses binary collection interface, therefore will fail if the value is not
119
+ # represented as ASCII-encoded number using +:raw+.
120
+ def increment(name, amount = 1, expires_in: nil, initial: nil, **options)
121
+ instrument :increment, name, amount: amount do
122
+ failsafe :increment do
123
+ key = normalize_key(name, options)
124
+ res = collection.binary.increment(
125
+ key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
126
+ )
127
+ @last_mutation_token = res.mutation_token
128
+ res.content
129
+ end
130
+ end
131
+ end
132
+
133
+ # Decrements an integer value in the cache.
134
+ #
135
+ # Note that it uses binary collection interface, therefore will fail if the value is not
136
+ # represented as ASCII-encoded number using +:raw+.
137
+ #
138
+ # Note that the counter represented by non-negative number on the server, and it will not
139
+ # cycle to maximum integer when decrementing zero.
140
+ def decrement(name, amount = 1, expires_in: nil, initial: nil, **_options)
141
+ instrument :decrement, name, amount: amount do
142
+ failsafe :decrement do
143
+ key = normalize_key(name, options)
144
+ res = collection.binary.decrement(
145
+ key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
146
+ )
147
+ @last_mutation_token = res.mutation_token
148
+ res.content
149
+ end
150
+ end
151
+ end
152
+
153
+ # Clears the entire cache. Be careful with this method since it could
154
+ # affect other processes if shared cache is being used.
155
+ #
156
+ # When +use_flush+ option set to +true+ it will flush the bucket. Otherwise, it uses N1QL query
157
+ # and relies on default index.
158
+ def clear(use_flush: false, **_options)
159
+ failsafe(:clear) do
160
+ if use_flush
161
+ cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
162
+ else
163
+ operation_options = ::Couchbase::Options::Query.new
164
+ operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
165
+ cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
166
+ end
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ def deserialize_entry(payload, raw: false, **)
173
+ if payload && raw
174
+ Entry.new(payload, compress: false)
175
+ else
176
+ super(payload)
177
+ end
178
+ end
179
+
180
+ def serialize_entry(entry, raw: false, **options)
181
+ if raw
182
+ entry.value.to_s
183
+ else
184
+ super(entry, **options)
185
+ end
186
+ end
187
+
188
+ def serialize_entries(entries, **options)
189
+ entries.transform_values do |entry|
190
+ serialize_entry(entry, **options)
191
+ end
192
+ end
193
+
194
+ # Reads an entry from the cache
195
+ def read_entry(key, **options)
196
+ deserialize_entry(read_serialized_entry(key, **options), **options)
197
+ end
198
+
199
+ def read_serialized_entry(key, **)
200
+ failsafe(:read_entry, returning: nil) do
201
+ res = collection.get(key, ::Couchbase::Options::Get(transcoder: nil))
202
+ res.content
203
+ end
204
+ end
205
+
206
+ # Reads multiple entries from the cache implementation. Subclasses MAY
207
+ # implement this method.
208
+ def read_multi_entries(names, **options)
209
+ return {} if names.empty?
210
+
211
+ keys = names.map { |name| normalize_key(name, options) }
212
+ return_value = {}
213
+ failsafe(:read_multi_entries, returning: return_value) do
214
+ results = collection.get_multi(keys, ::Couchbase::Options::GetMulti(transcoder: nil))
215
+ results.each_with_index do |result, index|
216
+ next unless result.success?
217
+
218
+ entry = deserialize_entry(result.content, raw: options[:raw])
219
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(names[index], options))
220
+ return_value[names[index]] = entry.value
221
+ end
222
+ end
223
+ return_value
224
+ end
225
+ end
226
+
227
+ # Writes an entry to the cache
228
+ def write_entry(key, entry, raw: false, **options)
229
+ write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
230
+ end
231
+
232
+ def write_serialized_entry(key, payload, expires_in: nil, race_condition_ttl: nil, raw: false, **)
233
+ if race_condition_ttl && expires_in && expires_in.positive? && !raw
234
+ # Add few minutes to expiry in the future to support race condition TTLs on read
235
+ expires_in += 5.minutes
236
+ end
237
+ failsafe(:write_entry, returning: false) do
238
+ res = collection.upsert(key, payload, ::Couchbase::Options::Upsert(transcoder: nil, expiry: expires_in))
239
+ @last_mutation_token = res.mutation_token
240
+ true
241
+ end
242
+ end
243
+
244
+ def write_multi_entries(entries, **options)
245
+ return 0 if entries.empty?
246
+
247
+ return super if local_cache
248
+
249
+ expires_in = options[:expires_in]
250
+ if options[:race_condition_ttl] && expires_in && expires_in.positive?
251
+ # Add few minutes to expiry in the future to support race condition TTLs on read
252
+ expires_in += 5.minutes
253
+ end
254
+ operation_options = ::Couchbase::Options::UpsertMulti(transcoder: nil, expiry: expires_in)
255
+ failsafe(:write_multi_entries, returning: nil) do
256
+ successful = collection.upsert_multi(serialize_entries(entries, **options), operation_options).select(&:success?)
257
+ return 0 if successful.empty?
258
+
259
+ @last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
260
+ successful.count
261
+ end
262
+ end
263
+
264
+ # Deletes an entry from the cache implementation. Subclasses must
265
+ # implement this method.
266
+ def delete_entry(key, **_options)
267
+ failsafe(:delete_entry, returning: false) do
268
+ res = collection.remove(key)
269
+ @last_mutation_token = res.mutation_token
270
+ true
271
+ end
272
+ end
273
+
274
+ # Deletes multiple entries in the cache. Returns the number of entries deleted.
275
+ def delete_multi_entries(entries, **_options)
276
+ return if entries.empty?
277
+
278
+ failsafe(:delete_multi_entries, returning: nil) do
279
+ successful = collection.remove_multi(entries).select(&:success?)
280
+ return 0 if successful.empty?
281
+
282
+ @last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
283
+ successful.count
284
+ end
285
+ end
286
+
287
+ def failsafe(method, returning: nil)
288
+ yield
289
+ rescue ::Couchbase::Error::CouchbaseError => e
290
+ ActiveSupport.error_reporter&.report(e, handled: true, severity: :warning)
291
+ @error_handler&.call(method: method, exception: e, returning: returning)
292
+ returning
293
+ end
294
+
295
+ # Truncate keys that exceed 250 characters
296
+ def normalize_key(key, options)
297
+ truncate_key super&.b
298
+ end
299
+
300
+ def truncate_key(key)
301
+ if key && key.bytesize > MAX_KEY_BYTESIZE
302
+ suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
303
+ truncate_at = MAX_KEY_BYTESIZE - suffix.bytesize
304
+ "#{key.byteslice(0, truncate_at)}#{suffix}"
305
+ else
306
+ key
307
+ end
308
+ end
309
+
310
+ # Connects to the Couchbase cluster
311
+ def build_cluster
312
+ ::Couchbase::Cluster.connect(
313
+ @couchbase_options[:connection_string],
314
+ ::Couchbase::Options::Cluster(authenticator: ::Couchbase::PasswordAuthenticator.new(
315
+ @couchbase_options[:username], @couchbase_options[:password]
316
+ ))
317
+ )
318
+ end
319
+
320
+ # Connects to the Couchbase cluster, opens specified bucket and returns collection object.
321
+ def build_collection
322
+ bucket = cluster.bucket(@couchbase_options[:bucket])
323
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
324
+ bucket.scope(@couchbase_options[:scope]).collection(@couchbase_options[:collection])
325
+ else
326
+ bucket.default_collection
327
+ end
328
+ end
329
+
330
+ def scope_qualifier
331
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
332
+ "`#{@couchbase_options[:bucket]}`.#{@couchbase_options[:scope]}.#{@couchbase_options[:collection]}"
333
+ else
334
+ "`#{@couchbase_options[:bucket]}`"
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,107 @@
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/json_transcoder"
16
+
17
+ module Couchbase
18
+ class Cluster
19
+ class AnalyticsWarning
20
+ # @return [Integer]
21
+ attr_accessor :code
22
+
23
+ # @return [String]
24
+ attr_accessor :message
25
+
26
+ # @param [Integer] code
27
+ # @param [String] message
28
+ def initialize(code, message)
29
+ @code = code
30
+ @message = message
31
+ end
32
+ end
33
+
34
+ class AnalyticsMetrics
35
+ # @return [Integer] duration in milliseconds
36
+ attr_accessor :elapsed_time
37
+
38
+ # @return [Integer] duration in milliseconds
39
+ attr_accessor :execution_time
40
+
41
+ # @return [Integer]
42
+ attr_accessor :result_count
43
+
44
+ # @return [Integer]
45
+ attr_accessor :result_size
46
+
47
+ # @return [Integer]
48
+ attr_accessor :error_count
49
+
50
+ # @return [Integer]
51
+ attr_accessor :processed_objects
52
+
53
+ # @return [Integer]
54
+ attr_accessor :warning_count
55
+
56
+ # @yieldparam [AnalyticsMetrics] self
57
+ def initialize
58
+ yield self if block_given?
59
+ end
60
+ end
61
+
62
+ class AnalyticsMetaData
63
+ # @return [String]
64
+ attr_accessor :request_id
65
+
66
+ # @return [String]
67
+ attr_accessor :client_context_id
68
+
69
+ # @return [:running, :success, :errors, :completed, :stopped, :timeout, :closed, :fatal, :aborted, :unknown]
70
+ attr_accessor :status
71
+
72
+ # @return [Hash] returns the signature as returned by the query engine which is then decoded as JSON object
73
+ attr_accessor :signature
74
+
75
+ # @return [Array<AnalyticsWarning>]
76
+ attr_accessor :warnings
77
+
78
+ # @return [AnalyticsMetrics]
79
+ attr_accessor :metrics
80
+
81
+ # @yieldparam [AnalyticsMetaData] self
82
+ def initialize
83
+ yield self if block_given?
84
+ end
85
+ end
86
+
87
+ class AnalyticsResult
88
+ # @return [AnalyticsMetaData]
89
+ attr_accessor :meta_data
90
+
91
+ attr_accessor :transcoder
92
+
93
+ # Returns all rows converted using a transcoder
94
+ #
95
+ # @return [Array]
96
+ def rows(transcoder = self.transcoder)
97
+ @rows.lazy.map { |row| transcoder.decode(row, 0) }
98
+ end
99
+
100
+ # @yieldparam [AnalyticsResult] self
101
+ def initialize
102
+ @transcoder = JsonTranscoder.new
103
+ yield self if block_given?
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,65 @@
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
+ module Couchbase
16
+ # Authenticator for username/password credentials
17
+ class PasswordAuthenticator
18
+ DEFAULT_SASL_MECHANISMS = [:scram_sha512, :scram_sha256, :scram_sha1].freeze
19
+
20
+ attr_accessor :username
21
+ attr_accessor :password
22
+ attr_accessor :allowed_sasl_mechanisms
23
+
24
+ # Creates a new password authenticator with the default settings.
25
+ #
26
+ # @param [String] password the username to use for all authentication requests
27
+ # @param [String] username the password
28
+ def initialize(username, password)
29
+ @username = username
30
+ @password = password
31
+ @allowed_sasl_mechanisms = DEFAULT_SASL_MECHANISMS
32
+ end
33
+
34
+ # Creates a LDAP compatible password authenticator which is INSECURE if not used with TLS.
35
+ #
36
+ # Please note that this is INSECURE and will leak user credentials on the wire to eavesdroppers. This should
37
+ # only be enabled in trusted environments.
38
+ #
39
+ # @param [String] username the username to use for all authentication.
40
+ # @param [String] password the password to use alongside the username.
41
+ # @return [PasswordAuthenticator]
42
+ def self.ldap_compatible(username, password)
43
+ new(username, password).tap do |auth|
44
+ auth.allowed_sasl_mechanisms = [:plain]
45
+ end
46
+ end
47
+ end
48
+
49
+ # Authenticator for TLS client certificate
50
+ #
51
+ # @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
52
+ class CertificateAuthenticator
53
+ attr_accessor :certificate_path
54
+ attr_accessor :key_path
55
+
56
+ # Creates a new authenticator with certificate and key paths
57
+ #
58
+ # @param [String] certificate_path path to certificate
59
+ # @param [String] key_path path to private key
60
+ def initialize(certificate_path, key_path)
61
+ @certificate_path = certificate_path
62
+ @key_path = key_path
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,128 @@
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/options"
16
+ require "couchbase/binary_collection_options"
17
+
18
+ module Couchbase
19
+ # Allows to perform certain operations on non-JSON documents.
20
+ class BinaryCollection
21
+ alias inspect to_s
22
+
23
+ # @param [Couchbase::Collection] collection parent collection
24
+ def initialize(collection)
25
+ @collection = collection
26
+ @backend = collection.instance_variable_get(:@backend)
27
+ end
28
+
29
+ # Appends binary content to the document
30
+ #
31
+ # @param [String] id the document id which is used to uniquely identify it
32
+ # @param [String] content the binary content to append to the document
33
+ # @param [Options::Append] options custom options to customize the request
34
+ #
35
+ # @example Append "bar" to the content of the existing document
36
+ # collection.upsert("mydoc", "foo")
37
+ # collection.binary.append("mydoc", "bar", Options::Append(timeout: 3_000))
38
+ # collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "foobar"
39
+ #
40
+ # @return [Collection::MutationResult]
41
+ def append(id, content, options = Options::Append.new)
42
+ resp = @backend.document_append(@collection.bucket_name, @collection.scope_name, @collection.name,
43
+ id, content, options.to_backend)
44
+ Collection::MutationResult.new do |res|
45
+ res.cas = resp[:cas]
46
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
47
+ end
48
+ end
49
+
50
+ # Prepends binary content to the document
51
+ #
52
+ # @param [String] id the document id which is used to uniquely identify it
53
+ # @param [String] content the binary content to prepend to the document
54
+ # @param [Options::Prepend] options custom options to customize the request
55
+ #
56
+ # @example Prepend "bar" to the content of the existing document
57
+ # collection.upsert("mydoc", "foo")
58
+ # collection.binary.prepend("mydoc", "bar", Options::Prepend(timeout: 3_000))
59
+ # collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "barfoo"
60
+ #
61
+ # @return [Collection::MutationResult]
62
+ def prepend(id, content, options = Options::Prepend.new)
63
+ resp = @backend.document_prepend(@collection.bucket_name, @collection.scope_name, @collection.name,
64
+ id, content, options.to_backend)
65
+ Collection::MutationResult.new do |res|
66
+ res.cas = resp[:cas]
67
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
68
+ end
69
+ end
70
+
71
+ # Increments the counter document by one of the number defined in the options
72
+ #
73
+ # @param [String] id the document id which is used to uniquely identify it
74
+ # @param [Options::Increment] options custom options to customize the request
75
+ #
76
+ # @example Increment value by 10, and initialize to 0 if it does not exist
77
+ # res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
78
+ # res.content #=> 0
79
+ # res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
80
+ # res.content #=> 10
81
+ #
82
+ # @return [CounterResult]
83
+ def increment(id, options = Options::Increment.new)
84
+ resp = @backend.document_increment(@collection.bucket_name, @collection.scope_name, @collection.name, id,
85
+ options.to_backend)
86
+ CounterResult.new do |res|
87
+ res.cas = resp[:cas]
88
+ res.content = resp[:content]
89
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
90
+ end
91
+ end
92
+
93
+ # Decrements the counter document by one of the number defined in the options
94
+ #
95
+ # @param [String] id the document id which is used to uniquely identify it
96
+ # @param [Options::Decrement] options custom options to customize the request
97
+ #
98
+ # @example Decrement value by 2, and initialize to 100 if it does not exist
99
+ # res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
100
+ # res.value #=> 100
101
+ # res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
102
+ # res.value #=> 98
103
+ #
104
+ # @return [CounterResult]
105
+ def decrement(id, options = Options::Decrement.new)
106
+ resp = @backend.document_decrement(@collection.bucket_name, @collection.scope_name, @collection.name, id,
107
+ options.to_backend)
108
+ CounterResult.new do |res|
109
+ res.cas = resp[:cas]
110
+ res.content = resp[:content]
111
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
112
+ end
113
+ end
114
+
115
+ # @api private
116
+ # TODO: deprecate in 3.1
117
+ AppendOptions = ::Couchbase::Options::Append
118
+ # @api private
119
+ # TODO: deprecate in 3.1
120
+ PrependOptions = ::Couchbase::Options::Prepend
121
+ # @api private
122
+ # TODO: deprecate in 3.1
123
+ IncrementOptions = ::Couchbase::Options::Increment
124
+ # @api private
125
+ # TODO: deprecate in 3.1
126
+ DecrementOptions = ::Couchbase::Options::Decrement
127
+ end
128
+ end
@@ -0,0 +1,24 @@
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/collection_options"
16
+
17
+ module Couchbase
18
+ class BinaryCollection
19
+ class CounterResult < ::Couchbase::Collection::MutationResult
20
+ # @return [Integer] current value of the counter
21
+ attr_accessor :content
22
+ end
23
+ end
24
+ end