couchbase 4.2.9 → 4.2.11-rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. package/deps/couchbase-cxx-client/CMakeLists.txt +4 -0
  2. package/deps/couchbase-cxx-client/cmake/CompilerWarnings.cmake +7 -3
  3. package/deps/couchbase-cxx-client/cmake/OpenSSL.cmake +1 -0
  4. package/deps/couchbase-cxx-client/core/bucket.cxx +67 -2
  5. package/deps/couchbase-cxx-client/core/bucket.hxx +2 -1
  6. package/deps/couchbase-cxx-client/core/cluster.cxx +65 -1
  7. package/deps/couchbase-cxx-client/core/impl/bucket.cxx +24 -0
  8. package/deps/couchbase-cxx-client/core/impl/cluster.cxx +73 -0
  9. package/deps/couchbase-cxx-client/core/impl/collection.cxx +2 -2
  10. package/deps/couchbase-cxx-client/core/impl/diagnostics.cxx +294 -0
  11. package/deps/couchbase-cxx-client/core/impl/diagnostics.hxx +39 -0
  12. package/deps/couchbase-cxx-client/core/impl/query_index_manager.cxx +6 -6
  13. package/deps/couchbase-cxx-client/core/impl/scope.cxx +19 -4
  14. package/deps/couchbase-cxx-client/core/impl/search.cxx +75 -2
  15. package/deps/couchbase-cxx-client/core/impl/search.hxx +8 -0
  16. package/deps/couchbase-cxx-client/core/impl/search_index_manager.cxx +261 -18
  17. package/deps/couchbase-cxx-client/core/impl/search_request.cxx +139 -0
  18. package/deps/couchbase-cxx-client/core/impl/vector_query.cxx +42 -0
  19. package/deps/couchbase-cxx-client/core/impl/vector_search.cxx +40 -0
  20. package/deps/couchbase-cxx-client/core/io/http_session.hxx +6 -3
  21. package/deps/couchbase-cxx-client/core/io/http_session_manager.hxx +7 -1
  22. package/deps/couchbase-cxx-client/core/io/mcbp_command.hxx +10 -0
  23. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +48 -33
  24. package/deps/couchbase-cxx-client/core/io/mcbp_session.hxx +2 -0
  25. package/deps/couchbase-cxx-client/core/logger/logger.cxx +1 -1
  26. package/deps/couchbase-cxx-client/core/management/design_document.hxx +1 -1
  27. package/deps/couchbase-cxx-client/core/meta/features.hxx +26 -1
  28. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +1 -1
  29. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +1 -1
  30. package/deps/couchbase-cxx-client/core/operations/document_query.cxx +8 -3
  31. package/deps/couchbase-cxx-client/core/operations/document_search.cxx +37 -1
  32. package/deps/couchbase-cxx-client/core/operations/document_search.hxx +11 -0
  33. package/deps/couchbase-cxx-client/core/operations/document_view.cxx +3 -0
  34. package/deps/couchbase-cxx-client/core/operations/document_view.hxx +1 -0
  35. package/deps/couchbase-cxx-client/core/operations/management/collection_create.cxx +7 -6
  36. package/deps/couchbase-cxx-client/core/operations/management/collection_create.hxx +1 -1
  37. package/deps/couchbase-cxx-client/core/operations/management/collection_update.cxx +7 -8
  38. package/deps/couchbase-cxx-client/core/operations/management/collection_update.hxx +1 -1
  39. package/deps/couchbase-cxx-client/core/operations/management/error_utils.cxx +3 -0
  40. package/deps/couchbase-cxx-client/core/operations/management/query_index_create.cxx +24 -13
  41. package/deps/couchbase-cxx-client/core/operations/management/query_index_create.hxx +1 -1
  42. package/deps/couchbase-cxx-client/core/operations/management/search_index_analyze_document.cxx +17 -1
  43. package/deps/couchbase-cxx-client/core/operations/management/search_index_analyze_document.hxx +3 -0
  44. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_ingest.cxx +21 -1
  45. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_ingest.hxx +3 -0
  46. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_plan_freeze.cxx +21 -1
  47. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_plan_freeze.hxx +3 -0
  48. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_query.cxx +21 -1
  49. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_query.hxx +3 -0
  50. package/deps/couchbase-cxx-client/core/operations/management/search_index_drop.cxx +17 -1
  51. package/deps/couchbase-cxx-client/core/operations/management/search_index_drop.hxx +3 -0
  52. package/deps/couchbase-cxx-client/core/operations/management/search_index_get.cxx +17 -1
  53. package/deps/couchbase-cxx-client/core/operations/management/search_index_get.hxx +2 -0
  54. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_all.cxx +39 -22
  55. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_all.hxx +3 -0
  56. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_documents_count.cxx +18 -1
  57. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_documents_count.hxx +3 -0
  58. package/deps/couchbase-cxx-client/core/operations/management/search_index_upsert.cxx +17 -1
  59. package/deps/couchbase-cxx-client/core/operations/management/search_index_upsert.hxx +3 -0
  60. package/deps/couchbase-cxx-client/core/origin.cxx +14 -0
  61. package/deps/couchbase-cxx-client/core/origin.hxx +6 -0
  62. package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -0
  63. package/deps/couchbase-cxx-client/core/protocol/status.hxx +1 -0
  64. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +70 -1
  65. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +30 -2
  66. package/deps/couchbase-cxx-client/core/topology/collections_manifest.hxx +1 -1
  67. package/deps/couchbase-cxx-client/core/topology/collections_manifest_json.hxx +1 -1
  68. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +1 -34
  69. package/deps/couchbase-cxx-client/core/topology/configuration_fmt.hxx +2 -2
  70. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +43 -20
  71. package/deps/couchbase-cxx-client/core/transactions/internal/exceptions_internal.hxx +5 -0
  72. package/deps/couchbase-cxx-client/core/transactions/internal/utils.hxx +4 -0
  73. package/deps/couchbase-cxx-client/core/vector_query_combination.hxx +23 -0
  74. package/deps/couchbase-cxx-client/couchbase/bucket.hxx +29 -0
  75. package/deps/couchbase-cxx-client/couchbase/cluster.hxx +110 -0
  76. package/deps/couchbase-cxx-client/couchbase/codec/json_transcoder.hxx +1 -1
  77. package/deps/couchbase-cxx-client/couchbase/codec/raw_binary_transcoder.hxx +2 -1
  78. package/deps/couchbase-cxx-client/couchbase/codec/raw_json_transcoder.hxx +78 -0
  79. package/deps/couchbase-cxx-client/couchbase/codec/raw_string_transcoder.hxx +72 -0
  80. package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +80 -11
  81. package/deps/couchbase-cxx-client/couchbase/create_collection_options.hxx +29 -1
  82. package/deps/couchbase-cxx-client/couchbase/diagnostics_options.hxx +75 -0
  83. package/deps/couchbase-cxx-client/couchbase/diagnostics_result.hxx +124 -0
  84. package/deps/couchbase-cxx-client/couchbase/endpoint_diagnostics.hxx +206 -0
  85. package/deps/couchbase-cxx-client/couchbase/endpoint_ping_report.hxx +205 -0
  86. package/deps/couchbase-cxx-client/couchbase/fmt/key_value_status_code.hxx +3 -1
  87. package/deps/couchbase-cxx-client/couchbase/get_options.hxx +1 -6
  88. package/deps/couchbase-cxx-client/couchbase/key_value_status_code.hxx +1 -0
  89. package/deps/couchbase-cxx-client/couchbase/management/collection_spec.hxx +1 -1
  90. package/deps/couchbase-cxx-client/couchbase/ping_options.hxx +93 -0
  91. package/deps/couchbase-cxx-client/couchbase/ping_result.hxx +118 -0
  92. package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +6 -8
  93. package/deps/couchbase-cxx-client/couchbase/scope.hxx +24 -8
  94. package/deps/couchbase-cxx-client/couchbase/scope_search_index_manager.hxx +291 -0
  95. package/deps/couchbase-cxx-client/couchbase/search_request.hxx +120 -0
  96. package/deps/couchbase-cxx-client/couchbase/service_type.hxx +58 -0
  97. package/deps/couchbase-cxx-client/couchbase/update_collection_options.hxx +32 -3
  98. package/deps/couchbase-cxx-client/couchbase/vector_query.hxx +99 -0
  99. package/deps/couchbase-cxx-client/couchbase/vector_search.hxx +85 -0
  100. package/deps/couchbase-cxx-client/couchbase/vector_search_options.hxx +76 -0
  101. package/dist/binding.d.ts +33 -2
  102. package/dist/binding.js +4 -1
  103. package/dist/bindingutilities.d.ts +16 -3
  104. package/dist/bindingutilities.js +47 -7
  105. package/dist/cluster.d.ts +12 -1
  106. package/dist/cluster.js +22 -0
  107. package/dist/couchbase.d.ts +2 -0
  108. package/dist/couchbase.js +2 -0
  109. package/dist/queryindexmanager.d.ts +4 -4
  110. package/dist/queryindexmanager.js +7 -7
  111. package/dist/scope.d.ts +21 -0
  112. package/dist/scope.js +34 -0
  113. package/dist/scopesearchindexmanager.d.ts +116 -0
  114. package/dist/scopesearchindexmanager.js +406 -0
  115. package/dist/sdspecs.js +10 -9
  116. package/dist/sdutils.d.ts +1 -0
  117. package/dist/sdutils.js +4 -0
  118. package/dist/searchexecutor.d.ts +5 -3
  119. package/dist/searchexecutor.js +27 -4
  120. package/dist/searchindexmanager.d.ts +58 -3
  121. package/dist/searchindexmanager.js +188 -104
  122. package/dist/searchtypes.d.ts +46 -0
  123. package/dist/searchtypes.js +81 -1
  124. package/dist/vectorsearch.d.ts +99 -0
  125. package/dist/vectorsearch.js +132 -0
  126. package/dist/viewexecutor.js +13 -9
  127. package/dist/viewindexmanager.d.ts +70 -7
  128. package/dist/viewindexmanager.js +236 -103
  129. package/dist/viewtypes.d.ts +26 -0
  130. package/dist/viewtypes.js +17 -1
  131. package/package.json +7 -7
  132. package/src/constants.cpp +12 -0
  133. package/src/jstocbpp_autogen.hpp +113 -13
  134. package/tools/gen-bindings-json.py +2 -0
@@ -455,6 +455,16 @@ class mcbp_session_impl
455
455
  case protocol::client_opcode::get_cluster_config: {
456
456
  protocol::cmd_info info{ session_->connection_endpoints_.remote_address,
457
457
  session_->connection_endpoints_.remote.port() };
458
+ if (session_->origin_.options().dump_configuration) {
459
+ std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()), msg.body.size() };
460
+ CB_LOG_TRACE(
461
+ "{} configuration from get_cluster_config request (bootstrap, size={}, endpoint=\"{}:{}\"), {}",
462
+ session_->log_prefix_,
463
+ config_text.size(),
464
+ info.endpoint_address,
465
+ info.endpoint_port,
466
+ config_text);
467
+ }
458
468
  protocol::client_response<protocol::get_cluster_config_response_body> resp(std::move(msg), info);
459
469
  if (resp.status() == key_value_status_code::success) {
460
470
  session_->update_configuration(resp.body().config());
@@ -542,17 +552,11 @@ class mcbp_session_impl
542
552
  {
543
553
  private:
544
554
  std::shared_ptr<mcbp_session_impl> session_;
545
- asio::steady_timer heartbeat_timer_;
546
- std::chrono::milliseconds heartbeat_interval_;
547
555
  std::atomic_bool stopped_{ false };
548
556
 
549
557
  public:
550
558
  explicit message_handler(std::shared_ptr<mcbp_session_impl> session)
551
559
  : session_(std::move(session))
552
- , heartbeat_timer_(session_->ctx_)
553
- , heartbeat_interval_{ session_->origin_.options().config_poll_floor > session_->origin_.options().config_poll_interval
554
- ? session_->origin_.options().config_poll_floor
555
- : session_->origin_.options().config_poll_interval }
556
560
  {
557
561
  }
558
562
 
@@ -563,16 +567,10 @@ class mcbp_session_impl
563
567
 
564
568
  void start()
565
569
  {
566
- if (session_->supports_gcccp_) {
567
- fetch_config({});
568
- }
569
570
  }
570
571
 
571
572
  void stop()
572
573
  {
573
- if (bool expected_state{ false }; stopped_.compare_exchange_strong(expected_state, true)) {
574
- heartbeat_timer_.cancel();
575
- }
576
574
  }
577
575
 
578
576
  void handle(mcbp_message&& msg)
@@ -709,23 +707,6 @@ class mcbp_session_impl
709
707
  break;
710
708
  }
711
709
  }
712
-
713
- void fetch_config(std::error_code ec)
714
- {
715
- if (ec == asio::error::operation_aborted || stopped_ || !session_) {
716
- return;
717
- }
718
- protocol::client_request<protocol::get_cluster_config_request_body> req;
719
- req.opaque(session_->next_opaque());
720
- session_->write_and_flush(req.data());
721
- heartbeat_timer_.expires_after(heartbeat_interval_);
722
- heartbeat_timer_.async_wait([self = shared_from_this()](std::error_code e) {
723
- if (e == asio::error::operation_aborted) {
724
- return;
725
- }
726
- self->fetch_config(e);
727
- });
728
- }
729
710
  };
730
711
 
731
712
  public:
@@ -889,6 +870,8 @@ class mcbp_session_impl
889
870
  if (stopped_) {
890
871
  return;
891
872
  }
873
+ bootstrapped_ = false;
874
+ bootstrap_handler_ = nullptr;
892
875
  state_ = diag::endpoint_state::connecting;
893
876
  if (stream_->is_open()) {
894
877
  std::string old_id = stream_->id();
@@ -1168,6 +1151,11 @@ class mcbp_session_impl
1168
1151
  return supports_gcccp_;
1169
1152
  }
1170
1153
 
1154
+ std::optional<topology::configuration> config() const
1155
+ {
1156
+ return config_;
1157
+ }
1158
+
1171
1159
  [[nodiscard]] bool has_config() const
1172
1160
  {
1173
1161
  return configured_;
@@ -1400,6 +1388,7 @@ class mcbp_session_impl
1400
1388
  return initiate_bootstrap();
1401
1389
  }
1402
1390
  endpoints_ = endpoints;
1391
+ CB_LOG_TRACE("{} resolved \"{}:{}\" to {} endpoint(s)", log_prefix_, bootstrap_hostname_, bootstrap_port_, endpoints_.size());
1403
1392
  do_connect(endpoints_.begin());
1404
1393
  connection_deadline_.expires_after(origin_.options().resolve_timeout);
1405
1394
  connection_deadline_.async_wait([self = shared_from_this()](const auto timer_ec) {
@@ -1419,19 +1408,33 @@ class mcbp_session_impl
1419
1408
  if (it != endpoints_.end()) {
1420
1409
  auto hostname = it->endpoint().address().to_string();
1421
1410
  auto port = it->endpoint().port();
1422
- CB_LOG_DEBUG("{} connecting to {}:{}, timeout={}ms", log_prefix_, hostname, port, origin_.options().connect_timeout.count());
1411
+ CB_LOG_DEBUG("{} connecting to {}:{} (\"{}:{}\"), timeout={}ms",
1412
+ log_prefix_,
1413
+ hostname,
1414
+ port,
1415
+ bootstrap_hostname_,
1416
+ bootstrap_port_,
1417
+ origin_.options().connect_timeout.count());
1423
1418
  connection_deadline_.expires_after(origin_.options().connect_timeout);
1424
1419
  connection_deadline_.async_wait([self = shared_from_this(), hostname, port](const auto timer_ec) {
1425
1420
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
1426
1421
  return;
1427
1422
  }
1428
- CB_LOG_DEBUG("{} unable to connect to {}:{} in time, reconnecting", self->log_prefix_, hostname, port);
1423
+ CB_LOG_DEBUG("{} unable to connect to {}:{} (\"{}:{}\") in time, reconnecting",
1424
+ self->log_prefix_,
1425
+ hostname,
1426
+ port,
1427
+ self->bootstrap_hostname_,
1428
+ self->bootstrap_port_);
1429
1429
  return self->stream_->close([self](std::error_code) { self->initiate_bootstrap(); });
1430
1430
  });
1431
1431
  stream_->async_connect(it->endpoint(),
1432
1432
  std::bind(&mcbp_session_impl::on_connect, shared_from_this(), std::placeholders::_1, it));
1433
1433
  } else {
1434
- CB_LOG_ERROR("{} no more endpoints left to connect, will try another address", log_prefix_);
1434
+ CB_LOG_ERROR("{} no more endpoints left to connect to \"{}:{}\", will try another address",
1435
+ log_prefix_,
1436
+ bootstrap_hostname_,
1437
+ bootstrap_port_);
1435
1438
  if (state_listener_) {
1436
1439
  state_listener_->report_bootstrap_error(fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_),
1437
1440
  errc::network::no_endpoints_left);
@@ -1553,7 +1556,7 @@ class mcbp_session_impl
1553
1556
  CB_LOG_TRACE("{} MCBP recv {}", self->log_prefix_, mcbp_header_view(msg.header_data()));
1554
1557
  if (self->bootstrapped_) {
1555
1558
  self->handler_->handle(std::move(msg));
1556
- } else {
1559
+ } else if (self->bootstrap_handler_) {
1557
1560
  self->bootstrap_handler_->handle(std::move(msg));
1558
1561
  }
1559
1562
  if (self->stopped_) {
@@ -1832,6 +1835,12 @@ mcbp_session::index() const
1832
1835
  return impl_->index();
1833
1836
  }
1834
1837
 
1838
+ std::optional<topology::configuration>
1839
+ mcbp_session::config() const
1840
+ {
1841
+ return impl_->config();
1842
+ }
1843
+
1835
1844
  bool
1836
1845
  mcbp_session::has_config() const
1837
1846
  {
@@ -1892,4 +1901,10 @@ mcbp_session::write_and_subscribe(std::shared_ptr<mcbp::queue_request> request,
1892
1901
  return impl_->write_and_subscribe(std::move(request), std::move(handler));
1893
1902
  }
1894
1903
 
1904
+ void
1905
+ mcbp_session::write_and_flush(std::vector<std::byte>&& buffer)
1906
+ {
1907
+ return impl_->write_and_flush(std::move(buffer));
1908
+ }
1909
+
1895
1910
  } // namespace couchbase::core::io
@@ -109,6 +109,7 @@ class mcbp_session
109
109
  [[nodiscard]] const std::string& bootstrap_hostname() const;
110
110
  [[nodiscard]] const std::string& bootstrap_port() const;
111
111
  [[nodiscard]] std::uint16_t bootstrap_port_number() const;
112
+ void write_and_flush(std::vector<std::byte>&& buffer);
112
113
  void write_and_subscribe(std::shared_ptr<mcbp::queue_request>, std::shared_ptr<response_handler> handler);
113
114
  void write_and_subscribe(std::uint32_t opaque, std::vector<std::byte>&& data, command_handler&& handler);
114
115
  void bootstrap(utils::movable_function<void(std::error_code, topology::configuration)>&& handler,
@@ -117,6 +118,7 @@ class mcbp_session
117
118
  void stop(retry_reason reason);
118
119
  [[nodiscard]] std::size_t index() const;
119
120
  [[nodiscard]] bool has_config() const;
121
+ [[nodiscard]] std::optional<topology::configuration> config() const;
120
122
  [[nodiscard]] diag::endpoint_diag_info diag_info() const;
121
123
  void on_configuration_update(std::shared_ptr<config_listener> handler);
122
124
  void ping(std::shared_ptr<diag::ping_reporter> handler, std::optional<std::chrono::milliseconds> = {}) const;
@@ -30,7 +30,7 @@ static const std::string protocol_logger_name{ "couchbase_cxx_client_protocol_lo
30
30
  * This pattern is duplicated for some test cases. If you need to update it,
31
31
  * please also update in all relevant places.
32
32
  */
33
- static const std::string log_pattern{ "[%Y-%m-%d %T.%e] [%P,%t] [%^%l%$] %oms, %v" };
33
+ static const std::string log_pattern{ "[%Y-%m-%d %T.%e] %4oms [%^%4!l%$] [%P,%t] %v" };
34
34
 
35
35
  /**
36
36
  * Instances of spdlog (async) file logger.
@@ -32,7 +32,7 @@ struct design_document {
32
32
  std::optional<std::string> reduce{};
33
33
  };
34
34
 
35
- std::string rev;
35
+ std::optional<std::string> rev;
36
36
  std::string name;
37
37
  design_document_namespace ns;
38
38
  std::map<std::string, view> views;
@@ -72,7 +72,7 @@
72
72
  */
73
73
  #define COUCHBASE_CXX_CLIENT_HAS_COLLECTION_QUERY_INDEX_MANAGEMENT 1
74
74
 
75
- #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_EXT_PARALLEL_UNSTAGING
75
+ #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_EXT_PARALLEL_UNSTAGING 1
76
76
 
77
77
  /**
78
78
  * core cluster implementation has been hidden and not accessible through the public API
@@ -101,3 +101,28 @@
101
101
  * The document not locked (couchbase::errc::key_value::document_not_locked) error code is supported
102
102
  */
103
103
  #define COUCHBASE_CXX_CLIENT_HAS_ERRC_DOCUMENT_NOT_LOCKED 1
104
+
105
+ /**
106
+ * Vector search is supported via couchbase::cluster::search() or couchbase::scope::search()
107
+ */
108
+ #define COUCHBASE_CXX_CLIENT_HAS_VECTOR_SEARCH 1
109
+
110
+ /**
111
+ * Scope level search index management is supported via couchbase::scope::search_indexes()
112
+ */
113
+ #define COUCHBASE_CXX_CLIENT_HAS_SCOPE_SEARCH_INDEX_MANAGEMENT 1
114
+
115
+ /**
116
+ * Support for couchbase::codec::raw_json_transcoder
117
+ */
118
+ #define COUCHBASE_CXX_CLIENT_HAS_RAW_JSON_TRANSCODER 1
119
+
120
+ /**
121
+ * Support for couchbase::codec::raw_string_transcoder
122
+ */
123
+ #define COUCHBASE_CXX_CLIENT_HAS_RAW_STRING_TRANSCODER 1
124
+
125
+ /**
126
+ * Transaction's transaction_operation_failed has a public getter for its final_error
127
+ */
128
+ #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_CAN_FETCH_TO_RAISE 1
@@ -69,7 +69,7 @@ struct lookup_in_all_replicas_request {
69
69
  id.bucket(),
70
70
  [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
71
71
  std::error_code ec, const topology::configuration& config) mutable {
72
- if (!config.supports_subdoc_read_replica()) {
72
+ if (!config.capabilities.supports_subdoc_read_replica()) {
73
73
  ec = errc::common::feature_not_available;
74
74
  }
75
75
 
@@ -66,7 +66,7 @@ struct lookup_in_any_replica_request {
66
66
  id.bucket(),
67
67
  [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
68
68
  std::error_code ec, const topology::configuration& config) mutable {
69
- if (!config.supports_subdoc_read_replica()) {
69
+ if (!config.capabilities.supports_subdoc_read_replica()) {
70
70
  ec = errc::common::feature_not_available;
71
71
  }
72
72
  if (specs.size() > 16) {
@@ -47,7 +47,7 @@ query_request::encode_to(query_request::encoded_request_type& encoded, http_cont
47
47
  }
48
48
  } else {
49
49
  body["statement"] = "PREPARE " + statement;
50
- if (context.config.supports_enhanced_prepared_statements()) {
50
+ if (context.config.capabilities.supports_enhanced_prepared_statements()) {
51
51
  body["auto_execute"] = true;
52
52
  } else {
53
53
  extract_encoded_plan_ = true;
@@ -87,7 +87,7 @@ query_request::encode_to(query_request::encoded_request_type& encoded, http_cont
87
87
  break;
88
88
  }
89
89
  if (use_replica.has_value()) {
90
- if (context.config.supports_read_from_replica()) {
90
+ if (context.config.capabilities.supports_read_from_replica()) {
91
91
  if (use_replica.value()) {
92
92
  body["use_replica"] = "on";
93
93
  } else {
@@ -327,7 +327,12 @@ query_request::make_response(error_context::query&& ctx, const encoded_response_
327
327
  response.ctx.first_error_message = response.meta.errors->front().message;
328
328
  switch (response.ctx.first_error_code) {
329
329
  case 1065: /* IKey: "service.io.request.unrecognized_parameter" */
330
- response.ctx.ec = errc::common::invalid_argument;
330
+ if ((response.ctx.first_error_message.find("Unrecognized parameter in request") != std::string::npos) &&
331
+ (response.ctx.first_error_message.find("preserve_expiry") != std::string::npos)) {
332
+ response.ctx.ec = errc::common::feature_not_available;
333
+ } else {
334
+ response.ctx.ec = errc::common::invalid_argument;
335
+ }
331
336
  break;
332
337
  case 1080: /* IKey: "timeout" */
333
338
  response.ctx.ec = errc::common::unambiguous_timeout;
@@ -34,6 +34,25 @@ search_request::encode_to(search_request::encoded_request_type& encoded, http_co
34
34
  { "query", utils::json::parse(query) },
35
35
  { "ctl", { { "timeout", encoded.timeout.count() } } },
36
36
  };
37
+
38
+ if (show_request.has_value()) {
39
+ body["showrequest"] = show_request.value();
40
+ }
41
+
42
+ if (vector_search.has_value()) {
43
+ body["knn"] = utils::json::parse(vector_search.value());
44
+ if (vector_query_combination.has_value()) {
45
+ switch (*vector_query_combination) {
46
+ case couchbase::core::vector_query_combination::combination_or:
47
+ body["knn_operator"] = "or";
48
+ break;
49
+ case couchbase::core::vector_query_combination::combination_and:
50
+ body["knn_operator"] = "and";
51
+ break;
52
+ }
53
+ }
54
+ }
55
+
37
56
  if (explain) {
38
57
  body["explain"] = *explain;
39
58
  }
@@ -103,10 +122,15 @@ search_request::encode_to(search_request::encoded_request_type& encoded, http_co
103
122
  body[key] = utils::json::parse(value);
104
123
  }
105
124
 
125
+ if (bucket_name.has_value() && scope_name.has_value()) {
126
+ encoded.path = fmt::format("/api/bucket/{}/scope/{}/index/{}/query", bucket_name.value(), scope_name.value(), index_name);
127
+ } else {
128
+ encoded.path = fmt::format("/api/index/{}/query", index_name);
129
+ }
130
+
106
131
  encoded.type = type;
107
132
  encoded.headers["content-type"] = "application/json";
108
133
  encoded.method = "POST";
109
- encoded.path = fmt::format("/api/index/{}/query", index_name);
110
134
  body_str = utils::json::generate(body);
111
135
  encoded.body = body_str;
112
136
  if (context.options.show_queries) {
@@ -310,6 +334,18 @@ search_request::make_response(error_context::search&& ctx, const encoded_respons
310
334
  response.ctx.ec = errc::common::rate_limited;
311
335
  return response;
312
336
  }
337
+ } else if (encoded.status_code == 404) {
338
+ tao::json::value payload{};
339
+ try {
340
+ payload = utils::json::parse(encoded.body.data());
341
+ } catch (const tao::pegtl::parse_error&) {
342
+ response.ctx.ec = errc::common::parsing_failure;
343
+ return response;
344
+ }
345
+ response.status = payload.at("status").get_string();
346
+ response.error = payload.at("error").get_string();
347
+ response.ctx.ec = errc::common::feature_not_available;
348
+ return response;
313
349
  }
314
350
  response.ctx.ec = errc::common::internal_server_failure;
315
351
  }
@@ -27,6 +27,7 @@
27
27
  #include "core/search_highlight_style.hxx"
28
28
  #include "core/search_scan_consistency.hxx"
29
29
  #include "core/timeout_defaults.hxx"
30
+ #include "core/vector_query_combination.hxx"
30
31
 
31
32
  #include <couchbase/mutation_token.hxx>
32
33
 
@@ -118,6 +119,16 @@ struct search_request {
118
119
 
119
120
  std::string index_name;
120
121
  couchbase::core::json_string query;
122
+ std::optional<std::string> bucket_name;
123
+ std::optional<std::string> scope_name;
124
+
125
+ /**
126
+ * UNCOMMITTED: This should be set to false if using the .search() API, leave unset for old .search_query() API
127
+ */
128
+ std::optional<bool> show_request;
129
+
130
+ std::optional<couchbase::core::json_string> vector_search;
131
+ std::optional<couchbase::core::vector_query_combination> vector_query_combination;
121
132
 
122
133
  std::optional<std::uint32_t> limit{};
123
134
  std::optional<std::uint32_t> skip{};
@@ -77,6 +77,9 @@ document_view_request::encode_to(document_view_request::encoded_request_type& en
77
77
  if (group_level) {
78
78
  query_string.emplace_back(fmt::format("group_level={}", *group_level));
79
79
  }
80
+ if (full_set) {
81
+ query_string.emplace_back(fmt::format("full_set={}", full_set.value() ? "true" : "false"));
82
+ }
80
83
  if (order) {
81
84
  switch (*order) {
82
85
  case couchbase::core::view_sort_order::descending:
@@ -86,6 +86,7 @@ struct document_view_request {
86
86
  std::optional<std::uint32_t> group_level;
87
87
  bool debug{ false };
88
88
  std::map<std::string, std::string> raw{};
89
+ std::optional<bool> full_set;
89
90
 
90
91
  std::optional<couchbase::core::view_sort_order> order;
91
92
  std::optional<couchbase::core::view_on_error> on_error;
@@ -28,14 +28,18 @@
28
28
  namespace couchbase::core::operations::management
29
29
  {
30
30
  std::error_code
31
- collection_create_request::encode_to(encoded_request_type& encoded, http_context& /* context */) const
31
+ collection_create_request::encode_to(encoded_request_type& encoded, http_context& /*context*/) const
32
32
  {
33
33
  encoded.method = "POST";
34
34
  encoded.path = fmt::format("/pools/default/buckets/{}/scopes/{}/collections", bucket_name, scope_name);
35
35
  encoded.headers["content-type"] = "application/x-www-form-urlencoded";
36
36
  encoded.body = fmt::format("name={}", utils::string_codec::form_encode(collection_name));
37
- if (max_expiry > 0) {
38
- encoded.body.append(fmt::format("&maxTTL={}", max_expiry));
37
+ if (max_expiry >= -1) {
38
+ if (max_expiry != 0) {
39
+ encoded.body.append(fmt::format("&maxTTL={}", max_expiry));
40
+ }
41
+ } else {
42
+ return couchbase::errc::common::invalid_argument;
39
43
  }
40
44
  if (history.has_value()) {
41
45
  encoded.body.append(fmt::format("&history={}", history.value()));
@@ -53,9 +57,6 @@ collection_create_request::make_response(error_context::http&& ctx, const encode
53
57
  std::regex collection_exists("Collection with name .+ already exists");
54
58
  if (std::regex_search(encoded.body.data(), collection_exists)) {
55
59
  response.ctx.ec = errc::management::collection_exists;
56
- } else if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
57
- encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
58
- response.ctx.ec = errc::common::feature_not_available;
59
60
  } else {
60
61
  response.ctx.ec = errc::common::invalid_argument;
61
62
  }
@@ -41,7 +41,7 @@ struct collection_create_request {
41
41
  std::string bucket_name;
42
42
  std::string scope_name;
43
43
  std::string collection_name;
44
- std::uint32_t max_expiry{ 0 };
44
+ std::int32_t max_expiry{ 0 };
45
45
  std::optional<bool> history{};
46
46
 
47
47
  std::optional<std::string> client_context_id{};
@@ -28,14 +28,18 @@
28
28
  namespace couchbase::core::operations::management
29
29
  {
30
30
  std::error_code
31
- collection_update_request::encode_to(encoded_request_type& encoded, http_context& /* context */) const
31
+ collection_update_request::encode_to(encoded_request_type& encoded, http_context& /*context*/) const
32
32
  {
33
33
  encoded.method = "PATCH";
34
34
  encoded.path = fmt::format("/pools/default/buckets/{}/scopes/{}/collections/{}", bucket_name, scope_name, collection_name);
35
35
  encoded.headers["content-type"] = "application/x-www-form-urlencoded";
36
36
  std::map<std::string, std::string> values{};
37
37
  if (max_expiry.has_value()) {
38
- values["maxTTL"] = std::to_string(max_expiry.value());
38
+ if (max_expiry.value() >= -1) {
39
+ values["maxTTL"] = std::to_string(max_expiry.value());
40
+ } else {
41
+ return errc::common::invalid_argument;
42
+ }
39
43
  }
40
44
  if (history.has_value()) {
41
45
  values["history"] = history.value() ? "true" : "false";
@@ -51,12 +55,7 @@ collection_update_request::make_response(error_context::http&& ctx, const encode
51
55
  if (!response.ctx.ec) {
52
56
  switch (encoded.status_code) {
53
57
  case 400: {
54
- if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
55
- encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
56
- response.ctx.ec = errc::common::feature_not_available;
57
- } else {
58
- response.ctx.ec = errc::common::invalid_argument;
59
- }
58
+ response.ctx.ec = errc::common::invalid_argument;
60
59
  } break;
61
60
  case 404: {
62
61
  std::regex scope_not_found("Scope with name .+ is not found");
@@ -41,7 +41,7 @@ struct collection_update_request {
41
41
  std::string bucket_name;
42
42
  std::string scope_name;
43
43
  std::string collection_name;
44
- std::optional<std::uint32_t> max_expiry{};
44
+ std::optional<std::int32_t> max_expiry{};
45
45
  std::optional<bool> history{};
46
46
 
47
47
  std::optional<std::string> client_context_id{};
@@ -47,6 +47,9 @@ extract_common_query_error_code(std::uint64_t code, const std::string& message)
47
47
  case 1194: /* ICode: E_SERVICE_USER_RESULT_SIZE_EXCEEDED, IKey: "service.result.size.exceeded" */
48
48
  return errc::common::rate_limited;
49
49
 
50
+ case 13014: /*ICode: E_DATASTORE_INSUFFICIENT_CREDENTIALS, IKey: "datastore.couchbase.insufficient_credentials" */
51
+ return errc::common::authentication_failure;
52
+
50
53
  case 5000:
51
54
  if (message.find("Limit for number of indexes that can be created per scope has been reached") != std::string::npos) {
52
55
  return errc::common::quota_limited;
@@ -17,7 +17,6 @@
17
17
 
18
18
  #include "query_index_create.hxx"
19
19
 
20
- #include "core/utils/join_strings.hxx"
21
20
  #include "core/utils/json.hxx"
22
21
  #include "core/utils/keyspace.hxx"
23
22
  #include "error_utils.hxx"
@@ -49,19 +48,31 @@ query_index_create_request::encode_to(encoded_request_type& encoded, http_contex
49
48
  if (with) {
50
49
  with_clause = fmt::format("WITH {}", utils::json::generate(with));
51
50
  }
51
+ std::string encoded_keys{};
52
+ for (std::size_t i = 0; i < keys.size(); i++) {
53
+ if (i != 0) {
54
+ encoded_keys += ", ";
55
+ }
56
+ auto key = keys.at(i);
57
+
58
+ // Add backticks around the key unless they are already present
59
+ if (key.at(0) == '`' && key.at(key.size() - 1) == '`') {
60
+ encoded_keys += key;
61
+ } else {
62
+ encoded_keys += fmt::format("`{}`", key);
63
+ }
64
+ }
52
65
  std::string keyspace = utils::build_keyspace(*this);
53
- tao::json::value body{ { "statement",
54
- is_primary ? fmt::format(R"(CREATE PRIMARY INDEX {} ON {} USING GSI {})",
55
- index_name.empty() ? "" : fmt::format("`{}`", index_name),
56
- keyspace,
57
- with_clause)
58
- : fmt::format(R"(CREATE INDEX `{}` ON {}({}) {} USING GSI {})",
59
- index_name,
60
- keyspace,
61
- utils::join_strings(fields, ", "),
62
- where_clause,
63
- with_clause) },
64
- { "client_context_id", encoded.client_context_id } };
66
+ tao::json::value body{
67
+ { "statement",
68
+ is_primary ? fmt::format(R"(CREATE PRIMARY INDEX {} ON {} USING GSI {})",
69
+ index_name.empty() ? "" : fmt::format("`{}`", index_name),
70
+ keyspace,
71
+ with_clause)
72
+ : fmt::format(
73
+ R"(CREATE INDEX `{}` ON {}({}) {} USING GSI {})", index_name, keyspace, encoded_keys, where_clause, with_clause) },
74
+ { "client_context_id", encoded.client_context_id }
75
+ };
65
76
  if (query_ctx.has_value()) {
66
77
  body["query_context"] = query_ctx.value();
67
78
  }
@@ -49,7 +49,7 @@ struct query_index_create_request {
49
49
  std::string scope_name;
50
50
  std::string collection_name;
51
51
  std::string index_name{};
52
- std::vector<std::string> fields;
52
+ std::vector<std::string> keys;
53
53
  query_context query_ctx;
54
54
  bool is_primary{ false };
55
55
  bool ignore_if_exists{ false };
@@ -33,7 +33,11 @@ search_index_analyze_document_request::encode_to(encoded_request_type& encoded,
33
33
  encoded.method = "POST";
34
34
  encoded.headers["cache-control"] = "no-cache";
35
35
  encoded.headers["content-type"] = "application/json";
36
- encoded.path = fmt::format("/api/index/{}/analyzeDoc", index_name);
36
+ if (bucket_name.has_value() && scope_name.has_value()) {
37
+ encoded.path = fmt::format("/api/bucket/{}/scope/{}/index/{}/analyzeDoc", bucket_name.value(), scope_name.value(), index_name);
38
+ } else {
39
+ encoded.path = fmt::format("/api/index/{}/analyzeDoc", index_name);
40
+ }
37
41
  encoded.body = encoded_document;
38
42
  return {};
39
43
  }
@@ -78,6 +82,18 @@ search_index_analyze_document_request::make_response(error_context::http&& ctx,
78
82
  response.ctx.ec = errc::common::index_exists;
79
83
  return response;
80
84
  }
85
+ } else if (encoded.status_code == 404) {
86
+ tao::json::value payload{};
87
+ try {
88
+ payload = utils::json::parse(encoded.body.data());
89
+ } catch (const tao::pegtl::parse_error&) {
90
+ response.ctx.ec = errc::common::parsing_failure;
91
+ return response;
92
+ }
93
+ response.status = payload.at("status").get_string();
94
+ response.error = payload.at("error").get_string();
95
+ response.ctx.ec = errc::common::feature_not_available;
96
+ return response;
81
97
  }
82
98
  response.ctx.ec = extract_common_error_code(encoded.status_code, encoded.body.data());
83
99
  }
@@ -43,6 +43,9 @@ struct search_index_analyze_document_request {
43
43
  std::string index_name;
44
44
  std::string encoded_document;
45
45
 
46
+ std::optional<std::string> bucket_name;
47
+ std::optional<std::string> scope_name;
48
+
46
49
  std::optional<std::string> client_context_id{};
47
50
  std::optional<std::chrono::milliseconds> timeout{};
48
51
 
@@ -31,7 +31,15 @@ search_index_control_ingest_request::encode_to(encoded_request_type& encoded, ht
31
31
  return errc::common::invalid_argument;
32
32
  }
33
33
  encoded.method = "POST";
34
- encoded.path = fmt::format("/api/index/{}/ingestControl/{}", index_name, pause ? "pause" : "resume");
34
+ if (bucket_name.has_value() && scope_name.has_value()) {
35
+ encoded.path = fmt::format("/api/bucket/{}/scope/{}/index/{}/ingestControl/{}",
36
+ bucket_name.value(),
37
+ scope_name.value(),
38
+ index_name,
39
+ pause ? "pause" : "resume");
40
+ } else {
41
+ encoded.path = fmt::format("/api/index/{}/ingestControl/{}", index_name, pause ? "pause" : "resume");
42
+ }
35
43
  return {};
36
44
  }
37
45
 
@@ -66,6 +74,18 @@ search_index_control_ingest_request::make_response(error_context::http&& ctx, co
66
74
  response.ctx.ec = errc::common::index_not_found;
67
75
  return response;
68
76
  }
77
+ } else if (encoded.status_code == 404) {
78
+ tao::json::value payload{};
79
+ try {
80
+ payload = utils::json::parse(encoded.body.data());
81
+ } catch (const tao::pegtl::parse_error&) {
82
+ response.ctx.ec = errc::common::parsing_failure;
83
+ return response;
84
+ }
85
+ response.status = payload.at("status").get_string();
86
+ response.error = payload.at("error").get_string();
87
+ response.ctx.ec = errc::common::feature_not_available;
88
+ return response;
69
89
  }
70
90
  response.ctx.ec = extract_common_error_code(encoded.status_code, encoded.body.data());
71
91
  }