couchbase 3.0.0.alpha.3-universal-darwin-19 → 3.0.0.alpha.4-universal-darwin-19

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests-6.0.3.yml +4 -1
  3. data/.github/workflows/tests-dev-preview.yml +4 -1
  4. data/.github/workflows/tests.yml +4 -1
  5. data/README.md +1 -1
  6. data/bin/check-cluster +31 -0
  7. data/bin/init-cluster +16 -4
  8. data/examples/analytics.rb +221 -0
  9. data/examples/managing_analytics_indexes.rb +72 -0
  10. data/examples/managing_view_indexes.rb +54 -0
  11. data/examples/search_with_consistency.rb +84 -0
  12. data/examples/view.rb +50 -0
  13. data/ext/.clang-tidy +1 -0
  14. data/ext/build_version.hxx.in +1 -1
  15. data/ext/couchbase/bucket.hxx +0 -1
  16. data/ext/couchbase/couchbase.cxx +1421 -55
  17. data/ext/couchbase/io/dns_client.hxx +215 -0
  18. data/ext/couchbase/io/dns_codec.hxx +207 -0
  19. data/ext/couchbase/io/dns_config.hxx +116 -0
  20. data/ext/couchbase/io/dns_message.hxx +558 -0
  21. data/ext/couchbase/io/http_session.hxx +16 -4
  22. data/ext/couchbase/io/mcbp_session.hxx +2 -1
  23. data/ext/couchbase/mutation_token.hxx +1 -1
  24. data/ext/couchbase/operations.hxx +19 -0
  25. data/ext/couchbase/operations/analytics_dataset_create.hxx +117 -0
  26. data/ext/couchbase/operations/analytics_dataset_drop.hxx +103 -0
  27. data/ext/couchbase/operations/analytics_dataset_get_all.hxx +107 -0
  28. data/ext/couchbase/operations/analytics_dataverse_create.hxx +104 -0
  29. data/ext/couchbase/operations/analytics_dataverse_drop.hxx +104 -0
  30. data/ext/couchbase/operations/analytics_get_pending_mutations.hxx +91 -0
  31. data/ext/couchbase/operations/analytics_index_create.hxx +128 -0
  32. data/ext/couchbase/operations/analytics_index_drop.hxx +110 -0
  33. data/ext/couchbase/operations/analytics_index_get_all.hxx +106 -0
  34. data/ext/couchbase/operations/analytics_link_connect.hxx +102 -0
  35. data/ext/couchbase/operations/analytics_link_disconnect.hxx +101 -0
  36. data/ext/couchbase/operations/design_document.hxx +59 -0
  37. data/ext/couchbase/operations/document_analytics.hxx +293 -0
  38. data/ext/couchbase/operations/document_query.hxx +2 -2
  39. data/ext/couchbase/operations/document_search.hxx +19 -1
  40. data/ext/couchbase/operations/document_view.hxx +227 -0
  41. data/ext/couchbase/operations/search_index.hxx +17 -0
  42. data/ext/couchbase/operations/search_index_control_ingest.hxx +3 -1
  43. data/ext/couchbase/operations/view_index_drop.hxx +67 -0
  44. data/ext/couchbase/operations/view_index_get.hxx +90 -0
  45. data/ext/couchbase/operations/view_index_get_all.hxx +125 -0
  46. data/ext/couchbase/operations/view_index_upsert.hxx +87 -0
  47. data/ext/couchbase/service_type.hxx +38 -1
  48. data/ext/couchbase/timeout_defaults.hxx +3 -1
  49. data/ext/couchbase/utils/connection_string.hxx +231 -0
  50. data/ext/couchbase/version.hxx +1 -1
  51. data/ext/test/main.cxx +3 -12
  52. data/lib/couchbase/analytics_options.rb +165 -0
  53. data/lib/couchbase/bucket.rb +49 -0
  54. data/lib/couchbase/cluster.rb +46 -207
  55. data/lib/couchbase/management/analytics_index_manager.rb +138 -24
  56. data/lib/couchbase/management/view_index_manager.rb +63 -10
  57. data/lib/couchbase/query_options.rb +219 -0
  58. data/lib/couchbase/search_options.rb +6 -6
  59. data/lib/couchbase/version.rb +1 -1
  60. data/lib/couchbase/view_options.rb +155 -0
  61. metadata +34 -2
@@ -0,0 +1,125 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 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
+ #pragma once
19
+
20
+ #include <tao/json.hpp>
21
+ #include <operations/design_document.hxx>
22
+
23
+ namespace couchbase::operations
24
+ {
25
+ struct view_index_get_all_response {
26
+ std::string client_context_id;
27
+ std::error_code ec;
28
+ std::vector<design_document> design_documents{};
29
+ };
30
+
31
+ struct view_index_get_all_request {
32
+ using response_type = view_index_get_all_response;
33
+ using encoded_request_type = io::http_request;
34
+ using encoded_response_type = io::http_response;
35
+
36
+ static const inline service_type type = service_type::management;
37
+
38
+ std::string client_context_id{ uuid::to_string(uuid::random()) };
39
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
40
+
41
+ std::string bucket_name;
42
+ design_document::name_space name_space;
43
+
44
+ void encode_to(encoded_request_type& encoded)
45
+ {
46
+ encoded.method = "GET";
47
+ encoded.path = fmt::format("/pools/default/buckets/{}/ddocs", bucket_name);
48
+ }
49
+ };
50
+
51
+ view_index_get_all_response
52
+ make_response(std::error_code ec, view_index_get_all_request& request, view_index_get_all_request::encoded_response_type encoded)
53
+ {
54
+ view_index_get_all_response response{ request.client_context_id, ec };
55
+ if (!ec) {
56
+ if (encoded.status_code == 200) {
57
+ auto payload = tao::json::from_string(encoded.body);
58
+ auto* rows = payload.find("rows");
59
+ if (rows != nullptr && rows->is_array()) {
60
+ for (const auto& entry : rows->get_array()) {
61
+ const auto* dd = entry.find("doc");
62
+ if (dd == nullptr || !dd->is_object()) {
63
+ continue;
64
+ }
65
+ const auto* meta = dd->find("meta");
66
+ if (meta == nullptr || !meta->is_object()) {
67
+ continue;
68
+ }
69
+
70
+ design_document document{};
71
+ document.rev = meta->at("rev").get_string();
72
+ auto id = meta->at("id").get_string();
73
+ static const std::string prefix = "_design/";
74
+ if (id.find(prefix) == 0) {
75
+ document.name = id.substr(prefix.size());
76
+ } else {
77
+ document.name = id; // fall back, should not happen
78
+ }
79
+ static const std::string name_space_prefix = "dev_";
80
+ if (document.name.find(name_space_prefix) == 0) {
81
+ document.name = document.name.substr(name_space_prefix.size());
82
+ document.ns = couchbase::operations::design_document::name_space::development;
83
+ } else {
84
+ document.ns = couchbase::operations::design_document::name_space::production;
85
+ }
86
+ if (document.ns != request.name_space) {
87
+ continue;
88
+ }
89
+
90
+ const auto *json = dd->find("json");
91
+ if (json == nullptr || !json->is_object()) {
92
+ continue;
93
+ }
94
+ const auto* views = json->find("views");
95
+ if (views != nullptr && views->is_object()) {
96
+ for (const auto& view_entry : views->get_object()) {
97
+ couchbase::operations::design_document::view view;
98
+ view.name = view_entry.first;
99
+ if (view_entry.second.is_object()) {
100
+ const auto* map = view_entry.second.find("map");
101
+ if (map != nullptr && map->is_string()) {
102
+ view.map = map->get_string();
103
+ }
104
+ const auto* reduce = view_entry.second.find("reduce");
105
+ if (reduce != nullptr && reduce->is_string()) {
106
+ view.reduce = reduce->get_string();
107
+ }
108
+ }
109
+ document.views[view.name] = view;
110
+ }
111
+ }
112
+
113
+ response.design_documents.emplace_back(document);
114
+ }
115
+ }
116
+ } else if (encoded.status_code == 404) {
117
+ response.ec = std::make_error_code(error::common_errc::bucket_not_found);
118
+ } else {
119
+ response.ec = std::make_error_code(error::common_errc::internal_server_failure);
120
+ }
121
+ }
122
+ return response;
123
+ }
124
+
125
+ } // namespace couchbase::operations
@@ -0,0 +1,87 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 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
+ #pragma once
19
+
20
+ #include <tao/json.hpp>
21
+
22
+ namespace couchbase::operations
23
+ {
24
+ struct view_index_upsert_response {
25
+ std::string client_context_id;
26
+ std::error_code ec;
27
+ };
28
+
29
+ struct view_index_upsert_request {
30
+ using response_type = view_index_upsert_response;
31
+ using encoded_request_type = io::http_request;
32
+ using encoded_response_type = io::http_response;
33
+
34
+ static const inline service_type type = service_type::views;
35
+
36
+ std::string client_context_id{ uuid::to_string(uuid::random()) };
37
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
38
+
39
+ std::string bucket_name;
40
+ design_document document;
41
+
42
+ void encode_to(encoded_request_type& encoded)
43
+ {
44
+ tao::json::value body;
45
+ body["views"] = tao::json::empty_object;
46
+ for (const auto& view : document.views) {
47
+ tao::json::value view_def;
48
+ if (view.second.map) {
49
+ view_def["map"] = *view.second.map;
50
+ }
51
+ if (view.second.reduce) {
52
+ view_def["reduce"] = *view.second.reduce;
53
+ }
54
+ body["views"][view.first] = view_def;
55
+ }
56
+
57
+ encoded.headers["content-type"] = "application/json";
58
+ encoded.method = "PUT";
59
+ encoded.path = fmt::format(
60
+ "/{}/_design/{}{}", bucket_name, document.ns == design_document::name_space::development ? "dev_" : "", document.name);
61
+ encoded.body = tao::json::to_string(body);
62
+ }
63
+ };
64
+
65
+ view_index_upsert_response
66
+ make_response(std::error_code ec, view_index_upsert_request& request, view_index_upsert_request::encoded_response_type encoded)
67
+ {
68
+ view_index_upsert_response response{ request.client_context_id, ec };
69
+ if (!ec) {
70
+ switch (encoded.status_code) {
71
+ case 200:
72
+ case 201:
73
+ break;
74
+ case 400:
75
+ response.ec = std::make_error_code(error::common_errc::invalid_argument);
76
+ break;
77
+ case 404:
78
+ response.ec = std::make_error_code(error::view_errc::design_document_not_found);
79
+ break;
80
+ default:
81
+ response.ec = std::make_error_code(error::common_errc::internal_server_failure);
82
+ }
83
+ }
84
+ return response;
85
+ }
86
+
87
+ } // namespace couchbase::operations
@@ -19,5 +19,42 @@
19
19
 
20
20
  namespace couchbase
21
21
  {
22
- enum class service_type { kv, query, analytics, search, views, management };
22
+ enum class service_type {
23
+ kv,
24
+ query,
25
+ analytics,
26
+ search,
27
+ views,
28
+ management,
29
+ };
23
30
  }
31
+
32
+ template<>
33
+ struct fmt::formatter<couchbase::service_type> : formatter<std::string_view> {
34
+ template<typename FormatContext>
35
+ auto format(couchbase::service_type type, FormatContext& ctx)
36
+ {
37
+ string_view name = "unknown";
38
+ switch (type) {
39
+ case couchbase::service_type::kv:
40
+ name = "kv";
41
+ break;
42
+ case couchbase::service_type::query:
43
+ name = "query";
44
+ break;
45
+ case couchbase::service_type::analytics:
46
+ name = "analytics";
47
+ break;
48
+ case couchbase::service_type::search:
49
+ name = "search";
50
+ break;
51
+ case couchbase::service_type::views:
52
+ name = "views";
53
+ break;
54
+ case couchbase::service_type::management:
55
+ name = "management";
56
+ break;
57
+ }
58
+ return formatter<string_view>::format(name, ctx);
59
+ }
60
+ };
@@ -29,4 +29,6 @@ constexpr std::chrono::milliseconds query_timeout{ 75'000 };
29
29
  constexpr std::chrono::milliseconds analytics_timeout{ 75'000 };
30
30
  constexpr std::chrono::milliseconds search_timeout{ 75'000 };
31
31
  constexpr std::chrono::milliseconds management_timeout{ 75'000 };
32
- } // namespace couchbase::timeout_defaults
32
+
33
+ constexpr std::chrono::milliseconds dns_srv_timeout{ 500 };
34
+ } // namespace couchbase::timeout_defaults
@@ -0,0 +1,231 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 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
+ #pragma once
19
+
20
+ #include <string>
21
+
22
+ #include <tao/json/external/pegtl.hpp>
23
+ #include <tao/json/external/pegtl/contrib/uri.hpp>
24
+
25
+ namespace couchbase::utils
26
+ {
27
+
28
+ struct connection_string {
29
+ enum class bootstrap_mode {
30
+ unspecified,
31
+ gcccp,
32
+ http,
33
+ };
34
+
35
+ enum class address_type {
36
+ ipv4,
37
+ ipv6,
38
+ dns,
39
+ };
40
+
41
+ struct node {
42
+ std::string address;
43
+ std::uint16_t port;
44
+ address_type type;
45
+ bootstrap_mode mode{ bootstrap_mode::unspecified };
46
+ };
47
+
48
+ std::string scheme{};
49
+ bool tls{ false };
50
+ std::map<std::string, std::string> params{};
51
+ std::vector<node> bootstrap_nodes{};
52
+
53
+ std::optional<std::string> default_bucket_name{};
54
+ bootstrap_mode default_mode{ bootstrap_mode::unspecified };
55
+ std::uint16_t default_port{ 0 };
56
+
57
+ std::optional<std::string> error{};
58
+ };
59
+
60
+ namespace priv
61
+ {
62
+ using namespace tao::json::pegtl;
63
+
64
+ struct bucket_name : seq<uri::segment_nz> {
65
+ };
66
+ using param_key = star<sor<abnf::ALPHA, abnf::DIGIT, one<'_'>>>;
67
+ using param_value = star<sor<minus<uri::pchar, one<'=', '&', '?'>>, one<'/'>>>;
68
+ struct param : seq<param_key, one<'='>, param_value> {
69
+ };
70
+
71
+ using sub_delims = minus<uri::sub_delims, one<',', '='>>; // host and mode separators
72
+ struct reg_name : star<sor<uri::unreserved, uri::pct_encoded, sub_delims>> {
73
+ };
74
+ struct host : sor<uri::IP_literal, uri::IPv4address, reg_name> {
75
+ };
76
+
77
+ struct mode : sor<istring<'c', 'c', 'c', 'p'>, istring<'g', 'c', 'c', 'c', 'p'>, istring<'h', 't', 't', 'p'>, istring<'m', 'c', 'd'>> {
78
+ };
79
+ using node = seq<host, opt<uri::colon, uri::port>, opt<one<'='>, mode>>;
80
+
81
+ using opt_bucket_name = opt_must<one<'/'>, bucket_name>;
82
+ using opt_params = opt_must<one<'?'>, list_must<param, one<'&'>>>;
83
+ using opt_nodes = seq<list_must<node, one<',', ';'>>, opt_bucket_name>;
84
+
85
+ using grammar = must<seq<uri::scheme, one<':'>, uri::dslash, opt_nodes, opt_params, eof>>;
86
+
87
+ template<typename Rule>
88
+ struct action {
89
+ };
90
+
91
+ template<>
92
+ struct action<uri::scheme> {
93
+ template<typename ActionInput>
94
+ static void apply(const ActionInput& in, connection_string& cs, connection_string::node& /* cur_node */)
95
+ {
96
+ cs.scheme = in.string();
97
+ if (cs.scheme == "couchbase") {
98
+ cs.default_port = 11210;
99
+ cs.default_mode = connection_string::bootstrap_mode::gcccp;
100
+ cs.tls = false;
101
+ } else if (cs.scheme == "couchbases") {
102
+ cs.default_port = 11207;
103
+ cs.default_mode = connection_string::bootstrap_mode::gcccp;
104
+ cs.tls = true;
105
+ } else if (cs.scheme == "http") {
106
+ cs.default_port = 8091;
107
+ cs.default_mode = connection_string::bootstrap_mode::http;
108
+ cs.tls = false;
109
+ } else if (cs.scheme == "https") {
110
+ cs.default_port = 18091;
111
+ cs.default_mode = connection_string::bootstrap_mode::http;
112
+ cs.tls = true;
113
+ }
114
+ }
115
+ };
116
+
117
+ template<>
118
+ struct action<param> {
119
+ template<typename ActionInput>
120
+ static void apply(const ActionInput& in, connection_string& cs, connection_string::node& /* cur_node */)
121
+ {
122
+ const auto& pair = in.string();
123
+ auto eq = pair.find('=');
124
+ std::string key = pair.substr(0, eq);
125
+ cs.params[key] = (eq == std::string::npos) ? "" : pair.substr(eq + 1);
126
+ }
127
+ };
128
+
129
+ template<>
130
+ struct action<reg_name> {
131
+ template<typename ActionInput>
132
+ static void apply(const ActionInput& in, connection_string& /* cs */, connection_string::node& cur_node)
133
+ {
134
+ cur_node.type = connection_string::address_type::dns;
135
+ cur_node.address = in.string_view();
136
+ }
137
+ };
138
+
139
+ template<>
140
+ struct action<uri::IPv4address> {
141
+ template<typename ActionInput>
142
+ static void apply(const ActionInput& in, connection_string& /* cs */, connection_string::node& cur_node)
143
+ {
144
+ cur_node.type = connection_string::address_type::ipv4;
145
+ cur_node.address = in.string_view();
146
+ }
147
+ };
148
+
149
+ template<>
150
+ struct action<uri::IPv6address> {
151
+ template<typename ActionInput>
152
+ static void apply(const ActionInput& in, connection_string& /* cs */, connection_string::node& cur_node)
153
+ {
154
+ cur_node.type = connection_string::address_type::ipv6;
155
+ cur_node.address = in.string_view();
156
+ }
157
+ };
158
+
159
+ template<>
160
+ struct action<node> {
161
+ template<typename ActionInput>
162
+ static void apply(const ActionInput& /* in */, connection_string& cs, connection_string::node& cur_node)
163
+ {
164
+ cs.bootstrap_nodes.push_back(cur_node);
165
+ cur_node = {};
166
+ }
167
+ };
168
+
169
+ template<>
170
+ struct action<uri::port> {
171
+ template<typename ActionInput>
172
+ static void apply(const ActionInput& in, connection_string& /* cs */, connection_string::node& cur_node)
173
+ {
174
+ cur_node.port = static_cast<std::uint16_t>(std::stoul(in.string()));
175
+ }
176
+ };
177
+
178
+ template<>
179
+ struct action<mode> {
180
+ template<typename ActionInput>
181
+ static void apply(const ActionInput& in, connection_string& /* cs */, connection_string::node& cur_node)
182
+ {
183
+ std::string mode = in.string();
184
+ std::transform(mode.begin(), mode.end(), mode.begin(), [](unsigned char c) { return std::tolower(c); });
185
+ if (mode == "mcd" || mode == "gcccp" || mode == "cccp") {
186
+ cur_node.mode = connection_string::bootstrap_mode::gcccp;
187
+ } else if (mode == "http") {
188
+ cur_node.mode = connection_string::bootstrap_mode::http;
189
+ }
190
+ }
191
+ };
192
+
193
+ template<>
194
+ struct action<bucket_name> {
195
+ template<typename ActionInput>
196
+ static void apply(const ActionInput& in, connection_string& cs, connection_string::node& /* cur_node */)
197
+ {
198
+ cs.default_bucket_name = in.string();
199
+ }
200
+ };
201
+ } // namespace priv
202
+
203
+ static connection_string
204
+ parse_connection_string(const std::string& input)
205
+ {
206
+ connection_string res;
207
+
208
+ if (input.empty()) {
209
+ res.error = "failed to parse connection string: empty input";
210
+ return res;
211
+ }
212
+
213
+ auto in = tao::json::pegtl::memory_input(input, __FUNCTION__);
214
+ try {
215
+ connection_string::node node{};
216
+ tao::json::pegtl::parse<priv::grammar, priv::action>(in, res, node);
217
+ } catch (tao::json::pegtl::parse_error& e) {
218
+ for (const auto& position : e.positions) {
219
+ if (position.source == __FUNCTION__) {
220
+ res.error = fmt::format(
221
+ "failed to parse connection string (column: {}, trailer: \"{}\")", position.byte_in_line, input.substr(position.byte));
222
+ break;
223
+ }
224
+ }
225
+ if (!res.error) {
226
+ res.error = e.what();
227
+ }
228
+ }
229
+ return res;
230
+ }
231
+ } // namespace couchbase::utils