couchbase 3.0.0.alpha.3 → 3.0.0.alpha.4

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