couchbase 3.4.3 → 3.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +15 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +34 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +155 -0
  14. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  15. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  16. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  17. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  18. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/get_all_buckets.cxx +163 -0
  20. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  21. data/ext/couchbase/core/impl/get_bucket.cxx +153 -0
  22. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  23. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  24. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  25. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  26. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  27. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +176 -0
  28. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  29. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +167 -0
  30. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  31. data/ext/couchbase/core/impl/lookup_in_replica.cxx +97 -0
  32. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  33. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  34. data/ext/couchbase/core/impl/query.cxx +1 -0
  35. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  36. data/ext/couchbase/core/impl/update_bucket.cxx +130 -0
  37. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  38. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  39. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  40. data/ext/couchbase/core/io/http_session.hxx +24 -1
  41. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  42. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  43. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  44. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  45. data/ext/couchbase/core/logger/logger.cxx +80 -20
  46. data/ext/couchbase/core/logger/logger.hxx +31 -0
  47. data/ext/couchbase/core/meta/features.hxx +25 -0
  48. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  49. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  50. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  51. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  52. data/ext/couchbase/core/operations.hxx +2 -0
  53. data/ext/couchbase/core/origin.cxx +270 -0
  54. data/ext/couchbase/core/origin.hxx +2 -0
  55. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  56. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  57. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  58. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  59. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  60. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  61. data/ext/couchbase/core/protocol/status.cxx +2 -2
  62. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  63. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  64. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  65. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  66. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  67. data/ext/couchbase/core/scan_options.hxx +0 -19
  68. data/ext/couchbase/core/scan_result.cxx +19 -5
  69. data/ext/couchbase/core/scan_result.hxx +5 -2
  70. data/ext/couchbase/core/timeout_defaults.hxx +2 -3
  71. data/ext/couchbase/core/topology/capabilities.hxx +3 -0
  72. data/ext/couchbase/core/topology/capabilities_fmt.hxx +8 -0
  73. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  74. data/ext/couchbase/core/topology/configuration.hxx +15 -0
  75. data/ext/couchbase/core/topology/configuration_json.hxx +6 -1
  76. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  77. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  78. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  79. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  80. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  81. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  82. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  83. data/ext/couchbase/couchbase/collection.hxx +111 -0
  84. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  85. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  86. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  87. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  88. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  89. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  90. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  91. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  92. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  93. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  94. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  95. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  96. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  97. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  98. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  99. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  100. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  101. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  102. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  103. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  104. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  105. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  106. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  107. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  108. data/ext/couchbase/couchbase/management/bucket_settings.hxx +116 -0
  109. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  110. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  111. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  112. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  113. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  114. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  115. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  116. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  117. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  118. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  119. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  120. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  121. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  122. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  123. data/ext/couchbase/test/CMakeLists.txt +1 -0
  124. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  125. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  126. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  127. data/ext/couchbase/test/test_integration_management.cxx +709 -266
  128. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  129. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  130. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  131. data/ext/couchbase/test/test_integration_subdoc.cxx +655 -0
  132. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  133. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  134. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  135. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  136. data/ext/couchbase.cxx +583 -29
  137. data/ext/revisions.rb +3 -3
  138. data/lib/couchbase/cluster.rb +1 -1
  139. data/lib/couchbase/collection.rb +108 -0
  140. data/lib/couchbase/collection_options.rb +100 -0
  141. data/lib/couchbase/errors.rb +5 -0
  142. data/lib/couchbase/key_value_scan.rb +125 -0
  143. data/lib/couchbase/options.rb +151 -0
  144. data/lib/couchbase/scope.rb +1 -1
  145. data/lib/couchbase/utils/time.rb +14 -1
  146. data/lib/couchbase/version.rb +1 -1
  147. metadata +41 -7
  148. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
data/ext/revisions.rb CHANGED
@@ -1,3 +1,3 @@
1
- cmake_flags << "-DEXT_GIT_REVISION=ef79cc86f921face060319b31c7cacbecfdefaa6"
2
- cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_REVISION=2c6a065499329f39963cc3a45c87f58c6bea5f28"
3
- cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_DESCRIBE=1.0.0-dp.5-0-g2c6a065"
1
+ cmake_flags << "-DEXT_GIT_REVISION=167d2e58559c91239675f1e7f7ffa2f6176f0512"
2
+ cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_REVISION=43cf66a592d1f8112141a73e5a563d7187ee0ee6"
3
+ cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_DESCRIBE=1.0.0-dp.8-0-g43cf66a"
@@ -124,7 +124,7 @@ module Couchbase
124
124
  metrics.warning_count = resp[:meta][:metrics][:warning_count]
125
125
  end
126
126
  end
127
- res[:warnings] = resp[:warnings].map { |warn| QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
127
+ meta.warnings = resp[:warnings].map { |warn| QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
128
128
  end
129
129
  res.instance_variable_set(:@rows, resp[:rows])
130
130
  end
@@ -15,6 +15,7 @@
15
15
  require "couchbase/errors"
16
16
  require "couchbase/collection_options"
17
17
  require "couchbase/binary_collection"
18
+ require "couchbase/key_value_scan"
18
19
 
19
20
  module Couchbase
20
21
  # Provides access to all collection APIs
@@ -481,11 +482,68 @@ module Couchbase
481
482
  f.index = field[:index]
482
483
  f.path = field[:path]
483
484
  f.value = field[:value]
485
+ f.error = field[:error]
484
486
  end
485
487
  end
486
488
  end
487
489
  end
488
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
+
489
547
  # Performs mutations to document fragments
490
548
  #
491
549
  # @param [String] id the document id which is used to uniquely identify it.
@@ -535,6 +593,38 @@ module Couchbase
535
593
  end
536
594
  end
537
595
 
596
+ # Performs a key-value scan operation on the collection
597
+ #
598
+ # @api uncommitted
599
+ #
600
+ # @param [RangeScan, PrefixScan, SamplingScan] scan_type the type of the scan
601
+ # @param [Options::Scan] options request customization
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
+ # @return [ScanResults]
619
+ def scan(scan_type, options = Options::Scan::DEFAULT)
620
+ ScanResults.new(
621
+ core_scan_result: @backend.document_scan_create(
622
+ @bucket_name, @scope_name, @name, scan_type.to_backend, options.to_backend
623
+ ),
624
+ transcoder: options.transcoder
625
+ )
626
+ end
627
+
538
628
  private
539
629
 
540
630
  def extract_mutation_token(resp)
@@ -548,6 +638,24 @@ module Couchbase
548
638
  end
549
639
  end
550
640
 
641
+ def extract_lookup_in_replica_result(resp, options)
642
+ LookupInReplicaResult.new do |res|
643
+ res.transcoder = options.transcoder
644
+ res.cas = resp[:cas]
645
+ res.deleted = resp[:deleted]
646
+ res.is_replica = resp[:is_replica]
647
+ res.encoded = resp[:fields].map do |field|
648
+ SubDocumentField.new do |f|
649
+ f.exists = field[:exists]
650
+ f.index = field[:index]
651
+ f.path = field[:path]
652
+ f.value = field[:value]
653
+ f.error = field[:error]
654
+ end
655
+ end
656
+ end
657
+ end
658
+
551
659
  # @api private
552
660
  # TODO: deprecate in 3.1
553
661
  GetOptions = ::Couchbase::Options::Get
@@ -15,6 +15,9 @@
15
15
  require "rubygems/deprecate"
16
16
 
17
17
  require "couchbase/json_transcoder"
18
+ require "couchbase/raw_string_transcoder"
19
+ require "couchbase/raw_json_transcoder"
20
+ require "couchbase/raw_binary_transcoder"
18
21
  require "couchbase/subdoc"
19
22
  require "couchbase/mutation_state"
20
23
 
@@ -164,6 +167,8 @@ module Couchbase
164
167
  # @return [Object] the decoded
165
168
  def content(path_or_index, transcoder = self.transcoder)
166
169
  field = get_field_at_index(path_or_index)
170
+
171
+ raise field.error unless field.error.nil?
167
172
  raise Error::PathNotFound, "Path is not found: #{path_or_index}" unless field.exists
168
173
 
169
174
  transcoder.decode(field.value, :json)
@@ -186,6 +191,8 @@ module Couchbase
186
191
  end
187
192
  return false unless field
188
193
 
194
+ raise field.error unless field.error.nil?
195
+
189
196
  field.exists
190
197
  end
191
198
 
@@ -227,6 +234,18 @@ module Couchbase
227
234
  end
228
235
  end
229
236
 
237
+ class LookupInReplicaResult < LookupInResult
238
+ # @return [Boolean] true if the document was read from a replica node
239
+ attr_accessor :is_replica
240
+ alias replica? is_replica
241
+
242
+ # @yieldparam [LookupInReplicaResult] self
243
+ def initialize
244
+ super
245
+ yield self if block_given?
246
+ end
247
+ end
248
+
230
249
  class MutateInResult < MutationResult
231
250
  # Decodes the content at the given index
232
251
  #
@@ -291,10 +310,91 @@ module Couchbase
291
310
  # @return [String] path
292
311
  attr_accessor :path
293
312
 
313
+ # @return [CouchbaseError] error
314
+ attr_accessor :error
315
+
294
316
  # @yieldparam [SubDocumentField] self
295
317
  def initialize
296
318
  yield self if block_given?
297
319
  end
298
320
  end
321
+
322
+ class ScanResult
323
+ # @return [String] identifier of the document
324
+ attr_accessor :id
325
+
326
+ # @return [Boolean] whether only ids are returned from this scan
327
+ attr_accessor :id_only
328
+
329
+ # @return [Integer, nil] holds the CAS value of the fetched document
330
+ attr_accessor :cas
331
+
332
+ # @return [Integer, nil] the expiration if fetched and present
333
+ attr_accessor :expiry
334
+
335
+ # @return [JsonTranscoder, RawBinaryTranscoder, RawJsonTranscoder, RawStringTranscoder, #decode] The default
336
+ # transcoder which should be used
337
+ attr_accessor :transcoder
338
+
339
+ def initialize(id:, id_only:, cas: nil, expiry: nil, encoded: nil, flags: nil, transcoder: JsonTranscoder.new)
340
+ @id = id
341
+ @id_only = id_only
342
+ @cas = cas
343
+ @expiry = expiry
344
+ @encoded = encoded
345
+ @flags = flags
346
+ @transcoder = transcoder
347
+
348
+ yield self if block_given?
349
+ end
350
+
351
+ # Decodes the content of the document using given (or default transcoder)
352
+ #
353
+ # @param [JsonTranscoder, RawJsonTranscoder, RawBinaryTranscoder, RawStringTranscoder] transcoder custom transcoder
354
+ #
355
+ # @return [Object, nil]
356
+ def content(transcoder = self.transcoder)
357
+ return nil if @encoded.nil?
358
+
359
+ transcoder ? transcoder.decode(@encoded, @flags) : @encoded
360
+ end
361
+ end
362
+
363
+ class ScanResults
364
+ include Enumerable
365
+
366
+ def initialize(core_scan_result:, transcoder:)
367
+ @core_scan_result = core_scan_result
368
+ @transcoder = transcoder
369
+ end
370
+
371
+ def each
372
+ return enum_for(:each) unless block_given?
373
+
374
+ loop do
375
+ resp = @core_scan_result.next_item
376
+
377
+ break if resp.nil?
378
+
379
+ if resp[:id_only]
380
+ yield ScanResult.new(
381
+ id: resp[:id],
382
+ id_only: resp[:id_only],
383
+ transcoder: @transcoder
384
+ )
385
+ else
386
+ yield ScanResult.new(
387
+ id: resp[:id],
388
+ id_only: resp[:id_only],
389
+ cas: resp[:cas],
390
+ expiry: resp[:expiry],
391
+ encoded: resp[:encoded],
392
+ flags: resp[:flags],
393
+ transcoder: @transcoder
394
+ )
395
+ end
396
+ end
397
+ end
398
+ end
299
399
  end
300
400
  end
@@ -183,6 +183,11 @@ module Couchbase
183
183
  class DurableWriteReCommitInProgress < CouchbaseError
184
184
  end
185
185
 
186
+ # Happens when consistency requirements are specified but the partition uuid of the requirements do not align
187
+ # with the server
188
+ class MutationTokenOutdated < CouchbaseError
189
+ end
190
+
186
191
  # Subdocument exception thrown when a path does not exist in the document. The exact meaning of path existence
187
192
  # depends on the operation and inputs.
188
193
  class PathNotFound < CouchbaseError
@@ -0,0 +1,125 @@
1
+ # Copyright 2023. 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
+ # A scan term used to specify the bounds of a range scan
17
+ class ScanTerm
18
+ attr_accessor :term # @return [ScanTerm]
19
+ attr_accessor :exclusive # @return [Boolean]
20
+
21
+ # Creates an instance of a ScanTerm
22
+ #
23
+ # @api uncommitted
24
+ #
25
+ # @param [String] term the key pattern of this term
26
+ # @param [Boolean] exclusive specifies if this term is excluded while scanning, the bounds are included by default
27
+ def initialize(term, exclusive: false)
28
+ @term = term
29
+ @exclusive = exclusive
30
+ end
31
+
32
+ # @api private
33
+ def to_backend
34
+ {
35
+ term: @term,
36
+ exclusive: @exclusive,
37
+ }
38
+ end
39
+ end
40
+
41
+ # A range scan performs a scan on a range of keys
42
+ class RangeScan
43
+ attr_accessor :from # @return [ScanTerm, nil]
44
+ attr_accessor :to # @return [ScanTerm, nil]
45
+
46
+ # Creates an instance of a RangeScan scan type
47
+ #
48
+ # @api uncommitted
49
+ #
50
+ # @param [ScanTerm, String, nil] from the lower bound of the range, if set
51
+ # @param [ScanTerm, String, nil] to the upper bound of the range, if set
52
+ def initialize(from: nil, to: nil)
53
+ @from =
54
+ if from.nil? || from.instance_of?(ScanTerm)
55
+ from
56
+ else
57
+ ScanTerm(from)
58
+ end
59
+ @to =
60
+ if to.nil? || to.instance_of?(ScanTerm)
61
+ to
62
+ else
63
+ ScanTerm(to)
64
+ end
65
+ end
66
+
67
+ # @api private
68
+ def to_backend
69
+ {
70
+ scan_type: :range,
71
+ from: @from&.to_backend,
72
+ to: @to&.to_backend,
73
+ }
74
+ end
75
+ end
76
+
77
+ # A prefix scan performs a scan that includes all documents whose keys start with the given prefix
78
+ class PrefixScan
79
+ attr_accessor :prefix # @return [String]
80
+
81
+ # Creates an instance of a PrefixScan scan type
82
+ #
83
+ # @api uncommitted
84
+ #
85
+ # @param [String, nil] prefix the prefix all document keys should start with
86
+ def initialize(prefix)
87
+ @prefix = prefix
88
+ end
89
+
90
+ # @api private
91
+ def to_backend
92
+ {
93
+ scan_type: :prefix,
94
+ prefix: @prefix,
95
+ }
96
+ end
97
+ end
98
+
99
+ # A sampling scan performs a scan that randomly selects documents up to a configured limit
100
+ class SamplingScan
101
+ attr_accessor :limit # @return [Integer]
102
+ attr_accessor :seed # @return [Integer, nil]
103
+
104
+ # Creates an instance of a SamplingScan scan type
105
+ #
106
+ # @api uncommitted
107
+ #
108
+ # @param [Integer] limit the maximum number of documents the sampling scan can return
109
+ # @param [Integer, nil] seed the seed used for the random number generator that selects the documents. If not set,
110
+ # a seed is generated at random
111
+ def initialize(limit, seed = nil)
112
+ @limit = limit
113
+ @seed = seed
114
+ end
115
+
116
+ # @api private
117
+ def to_backend
118
+ {
119
+ scan_type: :sampling,
120
+ limit: @limit,
121
+ seed: @seed,
122
+ }
123
+ end
124
+ end
125
+ end
@@ -1026,6 +1026,151 @@ module Couchbase
1026
1026
  DEFAULT = LookupIn.new.freeze
1027
1027
  end
1028
1028
 
1029
+ # Options for {Collection#lookup_in_any_replica}
1030
+ class LookupInAnyReplica < Base
1031
+ attr_accessor :transcoder # @return [JsonTranscoder, #decode(String)]
1032
+
1033
+ # Creates an instance of options for {Collection#lookup_in_any_replica}
1034
+ #
1035
+ # @param [JsonTranscoder, #decode(String)] transcoder used for encoding
1036
+ #
1037
+ # @param [Integer, #in_milliseconds, nil] timeout
1038
+ # @param [Proc, nil] retry_strategy the custom retry strategy, if set
1039
+ # @param [Hash, nil] client_context the client context data, if set
1040
+ # @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
1041
+ #
1042
+ # @yieldparam [LookupIn] self
1043
+ def initialize(transcoder: JsonTranscoder.new,
1044
+ timeout: nil,
1045
+ retry_strategy: nil,
1046
+ client_context: nil,
1047
+ parent_span: nil)
1048
+ super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
1049
+ @transcoder = transcoder
1050
+ yield self if block_given?
1051
+ end
1052
+
1053
+ # @api private
1054
+ def to_backend
1055
+ {
1056
+ timeout: Utils::Time.extract_duration(@timeout),
1057
+ }
1058
+ end
1059
+
1060
+ # @api private
1061
+ # @return [Boolean]
1062
+ attr_accessor :access_deleted
1063
+
1064
+ # @api private
1065
+ DEFAULT = LookupInAnyReplica.new.freeze
1066
+ end
1067
+
1068
+ # Options for {Collection#lookup_in_all_replicas}
1069
+ class LookupInAllReplicas < Base
1070
+ attr_accessor :transcoder # @return [JsonTranscoder, #decode(String)]
1071
+
1072
+ # Creates an instance of options for {Collection#lookup_in_all_replicas}
1073
+ #
1074
+ # @param [JsonTranscoder, #decode(String)] transcoder used for encoding
1075
+ #
1076
+ # @param [Integer, #in_milliseconds, nil] timeout
1077
+ # @param [Proc, nil] retry_strategy the custom retry strategy, if set
1078
+ # @param [Hash, nil] client_context the client context data, if set
1079
+ # @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
1080
+ #
1081
+ # @yieldparam [LookupInAllReplicas] self
1082
+ def initialize(transcoder: JsonTranscoder.new,
1083
+ timeout: nil,
1084
+ retry_strategy: nil,
1085
+ client_context: nil,
1086
+ parent_span: nil)
1087
+ super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
1088
+ @transcoder = transcoder
1089
+ yield self if block_given?
1090
+ end
1091
+
1092
+ # @api private
1093
+ def to_backend
1094
+ {
1095
+ timeout: Utils::Time.extract_duration(@timeout),
1096
+ }
1097
+ end
1098
+
1099
+ # @api private
1100
+ DEFAULT = LookupInAllReplicas.new.freeze
1101
+ end
1102
+
1103
+ # Options for {Collection#scan}
1104
+ class Scan < Base
1105
+ attr_accessor :ids_only # @return [Boolean]
1106
+ attr_accessor :transcoder # @return [JsonTranscoder, #decode(String)]
1107
+ attr_accessor :mutation_state # @return [MutationState, nil]
1108
+ attr_accessor :batch_byte_limit # @return [Integer, nil]
1109
+ attr_accessor :batch_item_limit # @return [Integer, nil]
1110
+ attr_accessor :concurrency # @return [Integer, nil]
1111
+
1112
+ # Creates an instance of options for {Collection#scan}
1113
+ #
1114
+ # @param [Boolean] ids_only if set to true, the content of the documents is not included in the results
1115
+ # @param [JsonTranscoder, #decode(String)] transcoder used for decoding
1116
+ # @param [MutationState, nil] mutation_state sets the mutation tokens this scan should be consistent with
1117
+ # @param [Integer, nil] batch_byte_limit allows to limit the maximum amount of bytes that are sent from the server
1118
+ # to the client on each partition batch, defaults to 15,000
1119
+ # @param [Integer, nil] batch_item_limit allows to limit the maximum amount of items that are sent from the server
1120
+ # to the client on each partition batch, defaults to 50
1121
+ # @param [Integer, nil] concurrency specifies the maximum number of partitions that can be scanned concurrently,
1122
+ # defaults to 1
1123
+ #
1124
+ # @param [Integer, #in_milliseconds, nil] timeout
1125
+ # @param [Proc, nil] retry_strategy the custom retry strategy, if set
1126
+ # @param [Hash, nil] client_context the client context data, if set
1127
+ # @param [Span, nil] parent_span if set holds the parent span, that should be used for this request
1128
+ #
1129
+ # @yieldparam [LookupIn] self
1130
+ def initialize(ids_only: false,
1131
+ transcoder: JsonTranscoder.new,
1132
+ mutation_state: nil,
1133
+ batch_byte_limit: nil,
1134
+ batch_item_limit: nil,
1135
+ concurrency: nil,
1136
+ timeout: nil,
1137
+ retry_strategy: nil,
1138
+ client_context: nil,
1139
+ parent_span: nil)
1140
+ super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
1141
+ @ids_only = ids_only
1142
+ @transcoder = transcoder
1143
+ @mutation_state = mutation_state
1144
+ @batch_byte_limit = batch_byte_limit
1145
+ @batch_item_limit = batch_item_limit
1146
+ @concurrency = concurrency
1147
+ yield self if block_given?
1148
+ end
1149
+
1150
+ # Sets the mutation tokens this query should be consistent with
1151
+ #
1152
+ # @note overrides consistency level set by {#scan_consistency=}
1153
+ #
1154
+ # @param [MutationState] mutation_state the mutation state containing the mutation tokens
1155
+ def consistent_with(mutation_state)
1156
+ @mutation_state = mutation_state
1157
+ end
1158
+
1159
+ # @api private
1160
+ def to_backend
1161
+ {
1162
+ timeout: Utils::Time.extract_duration(@timeout),
1163
+ ids_only: @ids_only,
1164
+ mutation_state: @mutation_state.to_a,
1165
+ batch_byte_limit: @batch_byte_limit,
1166
+ batch_item_limit: @batch_item_limit,
1167
+ concurrency: @concurrency,
1168
+ }
1169
+ end
1170
+
1171
+ DEFAULT = Scan.new.freeze
1172
+ end
1173
+
1029
1174
  # Options for {BinaryCollection#append}
1030
1175
  class Append < Base
1031
1176
  attr_accessor :cas # @return [Integer]
@@ -1849,6 +1994,7 @@ module Couchbase
1849
1994
  attr_accessor :profile # @return [Symbol]
1850
1995
  attr_accessor :flex_index # @return [Boolean]
1851
1996
  attr_accessor :preserve_expiry # @return [Boolean]
1997
+ attr_accessor :use_replica # @return [Boolean, nil]
1852
1998
  attr_accessor :scope_qualifier # @return [String]
1853
1999
  attr_accessor :transcoder # @return [JsonTranscoder, #decode(String)]
1854
2000
 
@@ -1894,6 +2040,8 @@ module Couchbase
1894
2040
  # @param [Boolean, nil] flex_index Tells the query engine to use a flex index (utilizing the search service)
1895
2041
  # @param [Boolean, nil] preserve_expiry Tells the query engine to preserve expiration values set on any documents
1896
2042
  # modified by this query.
2043
+ # @param [Boolean, nil] use_replica Specifies that the query engine should use replica nodes for KV fetches if
2044
+ # the active node is down. If not provided, the server default will be used
1897
2045
  # @param [String, nil] scope_qualifier Associate scope qualifier (also known as +query_context+) with the query.
1898
2046
  # The qualifier must be in form +{bucket_name}.{scope_name}+ or +default:{bucket_name}.{scope_name}+.
1899
2047
  # @param [JsonTranscoder] transcoder to decode rows
@@ -1925,6 +2073,7 @@ module Couchbase
1925
2073
  profile: :off,
1926
2074
  flex_index: nil,
1927
2075
  preserve_expiry: nil,
2076
+ use_replica: nil,
1928
2077
  scope_qualifier: nil,
1929
2078
  scan_consistency: :not_bounded,
1930
2079
  mutation_state: nil,
@@ -1950,6 +2099,7 @@ module Couchbase
1950
2099
  @profile = profile
1951
2100
  @flex_index = flex_index
1952
2101
  @preserve_expiry = preserve_expiry
2102
+ @use_replica = use_replica
1953
2103
  @scope_qualifier = scope_qualifier
1954
2104
  @scan_consistency = scan_consistency
1955
2105
  @mutation_state = mutation_state
@@ -2043,6 +2193,7 @@ module Couchbase
2043
2193
  readonly: @readonly,
2044
2194
  flex_index: @flex_index,
2045
2195
  preserve_expiry: @preserve_expiry,
2196
+ use_replica: @use_replica,
2046
2197
  scan_wait: Utils::Time.extract_duration(@scan_wait),
2047
2198
  scan_cap: @scan_cap,
2048
2199
  pipeline_batch: @pipeline_batch,
@@ -79,7 +79,7 @@ module Couchbase
79
79
  metrics.warning_count = resp[:meta][:metrics][:warning_count]
80
80
  end
81
81
  end
82
- res[:warnings] = resp[:warnings].map { |warn| Cluster::QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
82
+ meta.warnings = resp[:warnings].map { |warn| Cluster::QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
83
83
  end
84
84
  res.instance_variable_set(:@rows, resp[:rows])
85
85
  end
@@ -48,8 +48,21 @@ module Couchbase
48
48
  end
49
49
  end
50
50
 
51
+ # This method converts its argument to milliseconds
52
+ #
53
+ # 1. Integer values are interpreted as a number of milliseconds
54
+ # 2. If the argument is a Duration-like object and responds to #in_milliseconds,
55
+ # then use it and convert result to Integer
56
+ # 3. Otherwise invoke #to_i on the argument and interpret it as a number of milliseconds
51
57
  def extract_duration(number_or_duration)
52
- number_or_duration.respond_to?(:in_milliseconds) ? number_or_duration.public_send(:in_milliseconds) : number_or_duration
58
+ return unless number_or_duration
59
+ return number_or_duration if number_or_duration.class == Integer # rubocop:disable Style/ClassEqualityComparison avoid overrides of #is_a?, #kind_of?
60
+
61
+ if number_or_duration.respond_to?(:in_milliseconds)
62
+ number_or_duration.public_send(:in_milliseconds)
63
+ else
64
+ number_or_duration
65
+ end.to_i
53
66
  end
54
67
  end
55
68
  end
@@ -19,5 +19,5 @@ module Couchbase
19
19
  # $ ruby -rcouchbase -e 'pp Couchbase::VERSION'
20
20
  # {:sdk=>"3.4.0", :ruby_abi=>"3.1.0", :revision=>"416fe68e6029ec8a4c40611cf6e6b30d3b90d20f"}
21
21
  VERSION = {} unless defined?(VERSION) # rubocop:disable Style/MutableConstant
22
- VERSION.update(:sdk => "3.4.3".freeze)
22
+ VERSION.update(:sdk => "3.4.4".freeze)
23
23
  end