couchbase 4.2.9 → 4.2.10

Sign up to get free protection for your applications and to get access to all the features.
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