couchbase 4.2.9 → 4.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) 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 +52 -2
  5. package/deps/couchbase-cxx-client/core/bucket.hxx +1 -1
  6. package/deps/couchbase-cxx-client/core/cluster.cxx +38 -0
  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/diagnostics.cxx +294 -0
  10. package/deps/couchbase-cxx-client/core/impl/diagnostics.hxx +39 -0
  11. package/deps/couchbase-cxx-client/core/impl/scope.cxx +19 -4
  12. package/deps/couchbase-cxx-client/core/impl/search.cxx +75 -2
  13. package/deps/couchbase-cxx-client/core/impl/search.hxx +8 -0
  14. package/deps/couchbase-cxx-client/core/impl/search_index_manager.cxx +261 -18
  15. package/deps/couchbase-cxx-client/core/impl/search_request.cxx +139 -0
  16. package/deps/couchbase-cxx-client/core/impl/vector_query.cxx +42 -0
  17. package/deps/couchbase-cxx-client/core/impl/vector_search.cxx +40 -0
  18. package/deps/couchbase-cxx-client/core/io/http_session.hxx +6 -3
  19. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +24 -32
  20. package/deps/couchbase-cxx-client/core/io/mcbp_session.hxx +1 -0
  21. package/deps/couchbase-cxx-client/core/logger/logger.cxx +1 -1
  22. package/deps/couchbase-cxx-client/core/meta/features.hxx +10 -0
  23. package/deps/couchbase-cxx-client/core/operations/document_query.cxx +6 -1
  24. package/deps/couchbase-cxx-client/core/operations/document_search.cxx +37 -1
  25. package/deps/couchbase-cxx-client/core/operations/document_search.hxx +11 -0
  26. package/deps/couchbase-cxx-client/core/operations/management/collection_create.cxx +7 -6
  27. package/deps/couchbase-cxx-client/core/operations/management/collection_create.hxx +1 -1
  28. package/deps/couchbase-cxx-client/core/operations/management/collection_update.cxx +7 -8
  29. package/deps/couchbase-cxx-client/core/operations/management/collection_update.hxx +1 -1
  30. package/deps/couchbase-cxx-client/core/operations/management/error_utils.cxx +3 -0
  31. package/deps/couchbase-cxx-client/core/operations/management/search_index_analyze_document.cxx +17 -1
  32. package/deps/couchbase-cxx-client/core/operations/management/search_index_analyze_document.hxx +3 -0
  33. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_ingest.cxx +21 -1
  34. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_ingest.hxx +3 -0
  35. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_plan_freeze.cxx +21 -1
  36. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_plan_freeze.hxx +3 -0
  37. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_query.cxx +21 -1
  38. package/deps/couchbase-cxx-client/core/operations/management/search_index_control_query.hxx +3 -0
  39. package/deps/couchbase-cxx-client/core/operations/management/search_index_drop.cxx +17 -1
  40. package/deps/couchbase-cxx-client/core/operations/management/search_index_drop.hxx +3 -0
  41. package/deps/couchbase-cxx-client/core/operations/management/search_index_get.cxx +17 -1
  42. package/deps/couchbase-cxx-client/core/operations/management/search_index_get.hxx +2 -0
  43. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_all.cxx +39 -22
  44. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_all.hxx +3 -0
  45. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_documents_count.cxx +18 -1
  46. package/deps/couchbase-cxx-client/core/operations/management/search_index_get_documents_count.hxx +3 -0
  47. package/deps/couchbase-cxx-client/core/operations/management/search_index_upsert.cxx +17 -1
  48. package/deps/couchbase-cxx-client/core/operations/management/search_index_upsert.hxx +3 -0
  49. package/deps/couchbase-cxx-client/core/topology/collections_manifest.hxx +1 -1
  50. package/deps/couchbase-cxx-client/core/topology/collections_manifest_json.hxx +1 -1
  51. package/deps/couchbase-cxx-client/core/transactions/internal/utils.hxx +4 -0
  52. package/deps/couchbase-cxx-client/core/vector_query_combination.hxx +23 -0
  53. package/deps/couchbase-cxx-client/couchbase/bucket.hxx +29 -0
  54. package/deps/couchbase-cxx-client/couchbase/cluster.hxx +110 -0
  55. package/deps/couchbase-cxx-client/couchbase/codec/json_transcoder.hxx +1 -1
  56. package/deps/couchbase-cxx-client/couchbase/codec/raw_binary_transcoder.hxx +2 -1
  57. package/deps/couchbase-cxx-client/couchbase/codec/raw_json_transcoder.hxx +78 -0
  58. package/deps/couchbase-cxx-client/couchbase/codec/raw_string_transcoder.hxx +72 -0
  59. package/deps/couchbase-cxx-client/couchbase/create_collection_options.hxx +29 -1
  60. package/deps/couchbase-cxx-client/couchbase/diagnostics_options.hxx +75 -0
  61. package/deps/couchbase-cxx-client/couchbase/diagnostics_result.hxx +124 -0
  62. package/deps/couchbase-cxx-client/couchbase/endpoint_diagnostics.hxx +206 -0
  63. package/deps/couchbase-cxx-client/couchbase/endpoint_ping_report.hxx +205 -0
  64. package/deps/couchbase-cxx-client/couchbase/get_options.hxx +1 -6
  65. package/deps/couchbase-cxx-client/couchbase/management/collection_spec.hxx +1 -1
  66. package/deps/couchbase-cxx-client/couchbase/ping_options.hxx +93 -0
  67. package/deps/couchbase-cxx-client/couchbase/ping_result.hxx +118 -0
  68. package/deps/couchbase-cxx-client/couchbase/scope.hxx +24 -8
  69. package/deps/couchbase-cxx-client/couchbase/scope_search_index_manager.hxx +291 -0
  70. package/deps/couchbase-cxx-client/couchbase/search_request.hxx +120 -0
  71. package/deps/couchbase-cxx-client/couchbase/service_type.hxx +58 -0
  72. package/deps/couchbase-cxx-client/couchbase/update_collection_options.hxx +32 -3
  73. package/deps/couchbase-cxx-client/couchbase/vector_query.hxx +99 -0
  74. package/deps/couchbase-cxx-client/couchbase/vector_search.hxx +85 -0
  75. package/deps/couchbase-cxx-client/couchbase/vector_search_options.hxx +76 -0
  76. package/dist/binding.d.ts +9 -0
  77. package/dist/binding.js +4 -1
  78. package/dist/bindingutilities.d.ts +6 -1
  79. package/dist/bindingutilities.js +15 -1
  80. package/dist/cluster.d.ts +12 -1
  81. package/dist/cluster.js +22 -0
  82. package/dist/couchbase.d.ts +1 -0
  83. package/dist/couchbase.js +1 -0
  84. package/dist/searchexecutor.d.ts +2 -2
  85. package/dist/searchexecutor.js +19 -3
  86. package/dist/searchtypes.d.ts +46 -0
  87. package/dist/searchtypes.js +81 -1
  88. package/dist/vectorsearch.d.ts +99 -0
  89. package/dist/vectorsearch.js +132 -0
  90. package/package.json +7 -7
  91. package/src/constants.cpp +11 -0
  92. package/src/jstocbpp_autogen.hpp +24 -7
  93. package/tools/gen-bindings-json.py +2 -0
@@ -0,0 +1,139 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2023-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "core/impl/encoded_search_query.hxx"
19
+
20
+ #include <couchbase/search_request.hxx>
21
+
22
+ namespace couchbase
23
+ {
24
+ class search_request_impl
25
+ {
26
+ public:
27
+ explicit search_request_impl(std::optional<encoded_search_query> search_query,
28
+ std::optional<encoded_search_query> vector_search,
29
+ std::optional<vector_search_options::built> options)
30
+ : search_query_(std::move(search_query))
31
+ , vector_search_(std::move(vector_search))
32
+ , vector_search_options_(std::move(options))
33
+ {
34
+ }
35
+
36
+ static search_request_impl create(const search_query& query)
37
+ {
38
+ auto encoded = query.encode();
39
+ if (encoded.ec) {
40
+ throw std::system_error(encoded.ec, "unable to encode the search_query");
41
+ }
42
+ return search_request_impl(encoded, {}, {});
43
+ }
44
+
45
+ static search_request_impl create(const vector_search& search)
46
+ {
47
+ auto encoded = search.encode();
48
+ if (encoded.ec) {
49
+ throw std::system_error(encoded.ec, "unable to encode the vector_search");
50
+ }
51
+ return search_request_impl({}, encoded, search.options());
52
+ }
53
+
54
+ void search_query(const couchbase::search_query& query)
55
+ {
56
+ search_query_ = query.encode();
57
+ if (search_query_.value().ec) {
58
+ throw std::system_error(search_query_.value().ec, "unable to encode the search_query");
59
+ }
60
+ }
61
+
62
+ void vector_search(const couchbase::vector_search& search)
63
+ {
64
+ vector_search_ = search.encode();
65
+ if (vector_search_.value().ec) {
66
+ throw std::system_error(vector_search_.value().ec, "unable to encode the vector_search");
67
+ }
68
+ vector_search_options_ = search.options();
69
+ }
70
+
71
+ [[nodiscard]] std::optional<encoded_search_query> search_query() const
72
+ {
73
+ return search_query_;
74
+ }
75
+
76
+ [[nodiscard]] std::optional<encoded_search_query> vector_search() const
77
+ {
78
+ return vector_search_;
79
+ }
80
+
81
+ [[nodiscard]] std::optional<vector_search_options::built> vector_options() const
82
+ {
83
+ return vector_search_options_;
84
+ }
85
+
86
+ private:
87
+ std::optional<encoded_search_query> search_query_;
88
+ std::optional<encoded_search_query> vector_search_;
89
+ std::optional<vector_search_options::built> vector_search_options_;
90
+ };
91
+
92
+ search_request::search_request(const couchbase::search_query& query)
93
+ : impl_{ std::make_shared<search_request_impl>(search_request_impl::create(query)) }
94
+ {
95
+ }
96
+
97
+ search_request::search_request(const couchbase::vector_search& search)
98
+ : impl_{ std::make_shared<search_request_impl>(search_request_impl::create(search)) }
99
+ {
100
+ }
101
+
102
+ auto
103
+ search_request::search_query(const couchbase::search_query& search_query) -> search_request&
104
+ {
105
+ if (impl_->search_query().has_value()) {
106
+ throw std::invalid_argument("There can only be one search_query in a search request");
107
+ }
108
+ impl_->search_query(search_query);
109
+ return *this;
110
+ }
111
+
112
+ auto
113
+ search_request::vector_search(const couchbase::vector_search& vector_search) -> search_request&
114
+ {
115
+ if (impl_->vector_search().has_value()) {
116
+ throw std::invalid_argument("There can only be one vector_search in a search request");
117
+ }
118
+ impl_->vector_search(vector_search);
119
+ return *this;
120
+ }
121
+
122
+ [[nodiscard]] std::optional<encoded_search_query>
123
+ search_request::search_query() const
124
+ {
125
+ return impl_->search_query();
126
+ }
127
+
128
+ [[nodiscard]] std::optional<encoded_search_query>
129
+ search_request::vector_search() const
130
+ {
131
+ return impl_->vector_search();
132
+ }
133
+
134
+ [[nodiscard]] std::optional<couchbase::vector_search_options::built>
135
+ search_request::vector_options()
136
+ {
137
+ return impl_->vector_options();
138
+ }
139
+ } // namespace couchbase
@@ -0,0 +1,42 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2023-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "encoded_search_query.hxx"
19
+
20
+ #include <couchbase/vector_query.hxx>
21
+
22
+ namespace couchbase
23
+ {
24
+ auto
25
+ vector_query::encode() const -> encoded_search_query
26
+ {
27
+ encoded_search_query built;
28
+ built.query = tao::json::empty_object;
29
+ if (boost_) {
30
+ built.query["boost"] = boost_.value();
31
+ }
32
+ built.query["field"] = vector_field_name_;
33
+
34
+ tao::json::value vector_values = tao::json::empty_array;
35
+ for (const auto value : vector_query_) {
36
+ vector_values.push_back(value);
37
+ }
38
+ built.query["vector"] = vector_values;
39
+ built.query["k"] = num_candidates_;
40
+ return built;
41
+ }
42
+ } // namespace couchbase
@@ -0,0 +1,40 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2023-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "encoded_search_query.hxx"
19
+
20
+ #include <couchbase/vector_search.hxx>
21
+
22
+ namespace couchbase
23
+ {
24
+ auto
25
+ vector_search::encode() const -> encoded_search_query
26
+ {
27
+ encoded_search_query built;
28
+
29
+ built.query = tao::json::empty_array;
30
+
31
+ for (const auto& query : vector_queries_) {
32
+ auto encoded = query.encode();
33
+ if (encoded.ec) {
34
+ return { encoded.ec };
35
+ }
36
+ built.query.push_back(encoded.query);
37
+ }
38
+ return built;
39
+ }
40
+ } // namespace couchbase
@@ -370,11 +370,12 @@ class http_session : public std::enable_shared_from_this<http_session>
370
370
  return;
371
371
  }
372
372
  if (ec) {
373
- CB_LOG_ERROR("{} error on resolve: {}", info_.log_prefix(), ec.message());
373
+ CB_LOG_ERROR("{} error on resolve \"{}:{}\": {}", info_.log_prefix(), hostname_, service_, ec.message());
374
374
  return;
375
375
  }
376
376
  last_active_ = std::chrono::steady_clock::now();
377
377
  endpoints_ = endpoints;
378
+ CB_LOG_TRACE("{} resolved \"{}:{}\" to {} endpoint(s)", info_.log_prefix(), hostname_, service_, endpoints_.size());
378
379
  do_connect(endpoints_.begin());
379
380
  deadline_timer_.async_wait(std::bind(&http_session::check_deadline, shared_from_this(), std::placeholders::_1));
380
381
  }
@@ -385,15 +386,17 @@ class http_session : public std::enable_shared_from_this<http_session>
385
386
  return;
386
387
  }
387
388
  if (it != endpoints_.end()) {
388
- CB_LOG_DEBUG("{} connecting to {}:{}, timeout={}ms",
389
+ CB_LOG_DEBUG("{} connecting to {}:{} (\"{}:{}\"), timeout={}ms",
389
390
  info_.log_prefix(),
390
391
  it->endpoint().address().to_string(),
391
392
  it->endpoint().port(),
393
+ hostname_,
394
+ service_,
392
395
  http_ctx_.options.connect_timeout.count());
393
396
  deadline_timer_.expires_after(http_ctx_.options.connect_timeout);
394
397
  stream_->async_connect(it->endpoint(), std::bind(&http_session::on_connect, shared_from_this(), std::placeholders::_1, it));
395
398
  } else {
396
- CB_LOG_ERROR("{} no more endpoints left to connect", info_.log_prefix());
399
+ CB_LOG_ERROR("{} no more endpoints left to connect, \"{}:{}\" is not reachable", info_.log_prefix(), hostname_, service_);
397
400
  stop();
398
401
  }
399
402
  }
@@ -542,17 +542,11 @@ class mcbp_session_impl
542
542
  {
543
543
  private:
544
544
  std::shared_ptr<mcbp_session_impl> session_;
545
- asio::steady_timer heartbeat_timer_;
546
- std::chrono::milliseconds heartbeat_interval_;
547
545
  std::atomic_bool stopped_{ false };
548
546
 
549
547
  public:
550
548
  explicit message_handler(std::shared_ptr<mcbp_session_impl> session)
551
549
  : 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
550
  {
557
551
  }
558
552
 
@@ -563,16 +557,10 @@ class mcbp_session_impl
563
557
 
564
558
  void start()
565
559
  {
566
- if (session_->supports_gcccp_) {
567
- fetch_config({});
568
- }
569
560
  }
570
561
 
571
562
  void stop()
572
563
  {
573
- if (bool expected_state{ false }; stopped_.compare_exchange_strong(expected_state, true)) {
574
- heartbeat_timer_.cancel();
575
- }
576
564
  }
577
565
 
578
566
  void handle(mcbp_message&& msg)
@@ -709,23 +697,6 @@ class mcbp_session_impl
709
697
  break;
710
698
  }
711
699
  }
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
700
  };
730
701
 
731
702
  public:
@@ -1400,6 +1371,7 @@ class mcbp_session_impl
1400
1371
  return initiate_bootstrap();
1401
1372
  }
1402
1373
  endpoints_ = endpoints;
1374
+ CB_LOG_TRACE("{} resolved \"{}:{}\" to {} endpoint(s)", log_prefix_, bootstrap_hostname_, bootstrap_port_, endpoints_.size());
1403
1375
  do_connect(endpoints_.begin());
1404
1376
  connection_deadline_.expires_after(origin_.options().resolve_timeout);
1405
1377
  connection_deadline_.async_wait([self = shared_from_this()](const auto timer_ec) {
@@ -1419,19 +1391,33 @@ class mcbp_session_impl
1419
1391
  if (it != endpoints_.end()) {
1420
1392
  auto hostname = it->endpoint().address().to_string();
1421
1393
  auto port = it->endpoint().port();
1422
- CB_LOG_DEBUG("{} connecting to {}:{}, timeout={}ms", log_prefix_, hostname, port, origin_.options().connect_timeout.count());
1394
+ CB_LOG_DEBUG("{} connecting to {}:{} (\"{}:{}\"), timeout={}ms",
1395
+ log_prefix_,
1396
+ hostname,
1397
+ port,
1398
+ bootstrap_hostname_,
1399
+ bootstrap_port_,
1400
+ origin_.options().connect_timeout.count());
1423
1401
  connection_deadline_.expires_after(origin_.options().connect_timeout);
1424
1402
  connection_deadline_.async_wait([self = shared_from_this(), hostname, port](const auto timer_ec) {
1425
1403
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
1426
1404
  return;
1427
1405
  }
1428
- CB_LOG_DEBUG("{} unable to connect to {}:{} in time, reconnecting", self->log_prefix_, hostname, port);
1406
+ CB_LOG_DEBUG("{} unable to connect to {}:{} (\"{}:{}\") in time, reconnecting",
1407
+ self->log_prefix_,
1408
+ hostname,
1409
+ port,
1410
+ self->bootstrap_hostname_,
1411
+ self->bootstrap_port_);
1429
1412
  return self->stream_->close([self](std::error_code) { self->initiate_bootstrap(); });
1430
1413
  });
1431
1414
  stream_->async_connect(it->endpoint(),
1432
1415
  std::bind(&mcbp_session_impl::on_connect, shared_from_this(), std::placeholders::_1, it));
1433
1416
  } else {
1434
- CB_LOG_ERROR("{} no more endpoints left to connect, will try another address", log_prefix_);
1417
+ CB_LOG_ERROR("{} no more endpoints left to connect to \"{}:{}\", will try another address",
1418
+ log_prefix_,
1419
+ bootstrap_hostname_,
1420
+ bootstrap_port_);
1435
1421
  if (state_listener_) {
1436
1422
  state_listener_->report_bootstrap_error(fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_),
1437
1423
  errc::network::no_endpoints_left);
@@ -1892,4 +1878,10 @@ mcbp_session::write_and_subscribe(std::shared_ptr<mcbp::queue_request> request,
1892
1878
  return impl_->write_and_subscribe(std::move(request), std::move(handler));
1893
1879
  }
1894
1880
 
1881
+ void
1882
+ mcbp_session::write_and_flush(std::vector<std::byte>&& buffer)
1883
+ {
1884
+ return impl_->write_and_flush(std::move(buffer));
1885
+ }
1886
+
1895
1887
  } // 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,
@@ -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.
@@ -101,3 +101,13 @@
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
@@ -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() ? "true" : "false";
40
+ }
41
+
42
+ if (vector_search.has_value()) {
43
+ body["knn"] = utils::json::parse(vector_search.value());
44
+ }
45
+ if (vector_query_combination.has_value()) {
46
+ switch (*vector_query_combination) {
47
+ case couchbase::core::vector_query_combination::combination_or:
48
+ body["knn_operator"] = "or";
49
+ break;
50
+ case couchbase::core::vector_query_combination::combination_and:
51
+ body["knn_operator"] = "and";
52
+ break;
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{};
@@ -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;
@@ -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