- Note
- You may read about related Couchbase software at https://docs.couchbase.com/
Start Using
The following example shows the most basic usage of the library. It performs the following operations:
- [1] connect to local cluster (location and credentials given in program arguments),
- [2] persists document to the collection using upsert(),
- [3] retrieves document back with get(), extracts content as generic JSON value, and prints one of the fields,
- [4] performs N1QL query with query() and prints the result.
- [5] output results obtained in [4] using custom type defined in [6]
- [7] closes the cluster and deallocate resources
78
79#include <tao/json.hpp>
80#include <tao/json/contrib/traits.hpp>
81
82int
83main(int argc, const char* argv[])
84{
85 if (argc != 4) {
86 fmt::print("USAGE: ./start_using couchbase://127.0.0.1 Administrator password\n");
87 return 1;
88 }
89
90 std::string connection_string{ argv[1] };
91 std::string username{ argv[2] };
92 std::string password{ argv[3] };
93 std::string bucket_name{ "travel-sample" };
94
95 auto options = couchbase::cluster_options(username, password);
96
97
98 options.apply_profile("wan_development");
99
100
102 if (connect_err) {
103 fmt::print("unable to connect to the cluster: {}\n", connect_err);
104 return 1;
105 }
106
107
108 auto bucket = cluster.bucket(bucket_name);
109
110
111 auto scope = bucket.scope("tenant_agent_00");
112 auto collection = scope.collection("users");
113
114 {
115 std::string doc_id = "my-document";
116 auto [err, upsert_result] =
117 collection.upsert(doc_id, tao::json::value{ { "name", "mike" } }).get();
118 if (err.ec()) {
119 fmt::print("unable to upsert the document \"{}\": {}\n", doc_id, err);
120 return 1;
121 }
122 fmt::print("saved document \"{}\", cas={}, token={}\n",
123 doc_id,
124 upsert_result.cas(),
125 upsert_result.mutation_token().value());
126 }
127
128 {
129 std::string doc_id = "my-document";
130 auto [err, get_result] = collection.get(doc_id).get();
131 if (err.ec()) {
132 fmt::print("unable to get the document \"{}\": {}\n", doc_id, err);
133 return 1;
134 }
135 auto name = get_result.content_as<tao::json::value>()["name"].get_string();
136 fmt::print("retrieved document \"{}\", name=\"{}\"\n", doc_id, name);
137 }
138
139 {
140 auto inventory_scope = bucket.scope("inventory");
141
142
143 std::string
query{ R
"(
144 SELECT META(h).id, h AS doc,
145 AVG(r.ratings.Overall) AS avg_rating
146 FROM hotel h
147 UNNEST h.reviews r
148 WHERE h.country IN $1 AND h.description LIKE "%cheap%"
149 GROUP BY META(h).id, h
150 ORDER BY avg_rating DESC
151 LIMIT 5;
152 )" };
153 auto query_options = couchbase::query_options{}.positional_parameters(
154 std::vector{ "United States", "United Kingdom" });
155 auto [
error, query_result] = inventory_scope.query(query, query_options).get();
156 if (error) {
157 fmt::print(
"unable to perform query: {}\n",
error.ctx().to_json());
158 return 1;
159 }
160 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
161 for (auto& row : query_result.rows_as()) {
162 fmt::println("{:<15} {:<15} {:>10.2f} {:<30}",
163 row["id"].as<std::string>(),
164 row["doc"]["country"].as<std::string>(),
165 row["avg_rating"].as<double>(),
166 row["doc"]["title"].as<std::string>());
167 }
168
169
170 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
171 for (const auto& row : query_result.rows_as<couchbase::codec::tao_json_serializer, hotel>()) {
172 fmt::println(
173 "{:<15} {:<15} {:>10.2f} {:<30}", row.id, row.country, row.average_rating, row.name);
174 }
175 }
176
177
178 cluster.close().get();
179 return 0;
180}
181
182
183
184
185
186
187
188
189
190
191
192
193
194
47
48struct hotel {
49 std::string id{};
50 std::string name{};
51 std::string country{};
52 double average_rating{};
53};
54
55template<>
56struct tao::json::traits<hotel> {
57 template<template<typename...> class Traits>
58 static auto as(const tao::json::basic_value<Traits>& v) -> hotel
59 {
60 hotel result;
61 auto object = v.get_object();
62 result.id = object["id"].template as<std::string>();
63 result.average_rating = object["avg_rating"].template as<double>();
64 result.name = object["doc"]["title"].template as<std::string>();
65 result.country = object["doc"]["country"].template as<std::string>();
66 return result;
67 }
68};
Using Transactions
Next example shows transactions API. Read more details in transactions
- [1] connect to cluster using the given connection string and the options
- [2] persist three documents to the default collection of bucket "default"
- [3] blocking transaction
- [3.1] closure argument to run method encapsulates logic, that has to be run in transaction
- [3.2] get document
- [3.4] replace document's content
- [3.5] check the overall status of the transaction
- [4] asynchronous transaction
- [4.1] create promise to retrieve result from the transaction
- [4.2] closure argument to run method encapsulates logic, that has to be run in transaction
- [4.3] get document
- [4.4] replace document's content
- [4.5] second closure argument to run represents transaction completion logic
- [5] close cluster connection
37
38#include <tao/json.hpp>
39
40auto
41main(int argc, const char* argv[]) -> int
42{
43 if (argc != 4) {
44 fmt::print("USAGE: ./blocking-txn couchbase://127.0.0.1 Administrator password\n");
45 return 1;
46 }
47
48 int retval = 0;
49
50 const std::string connection_string{ argv[1] };
51 const std::string username{ argv[2] };
52 const std::string password{ argv[3] };
53
54 auto options = couchbase::cluster_options(username, password);
55
56
57 options.apply_profile("wan_development");
58
59
61 if (connect_err) {
62 fmt::print("unable to connect to the cluster: {}\n", connect_err);
63 return 1;
64 }
65
66
67 auto collection = cluster.bucket("default").default_collection();
68 constexpr auto id_1 = "my-doc_1";
69 constexpr auto id_2 = "my_doc_2";
70 constexpr auto id_3 = "my_doc_3";
71 const tao::json::value content = { { "some", "content" } };
72
73 for (const auto& id : { id_1, id_2, id_3 }) {
74 if (auto [err, res] = collection.upsert(id, content).get(); err.ec()) {
75 fmt::print(
76 stderr, "upsert \"{}\" failed before starting transaction: {}\n", id, err.ec().message());
77 return 1;
78 }
79 }
80
81 {
83 auto [tx_err, tx_res] = cluster.transactions()->run(
84
85
86 [=](std::shared_ptr<couchbase::transactions::attempt_context> ctx) ->
couchbase::error {
87
88 auto [err_ctx, doc] = ctx->get(collection, id_1);
89 if (err_ctx.ec()) {
90 fmt::print(stderr, "failed to get document \"{}\": {}\n", id_1, err_ctx.ec().message());
91
92 return {};
93 }
94
95 ctx->replace(doc, tao::json::value{ { "some", "other content" } });
96 return {};
97 });
98
99 if (tx_err.ec()) {
100 fmt::print(stderr,
101 "error in transaction {}, cause: {}\n",
102 tx_err.ec().message(),
103 tx_err.cause().has_value() ? tx_err.cause().value().ec().message() : "");
104 retval = 1;
105 } else {
106 fmt::print("transaction {} completed successfully\n", tx_res.transaction_id);
107 }
109 }
110
111 {
113
114 auto barrier = std::make_shared<std::promise<std::error_code>>();
115 auto f = barrier->get_future();
116 cluster.transactions()->run(
117
118
119 [=](std::shared_ptr<couchbase::transactions::async_attempt_context> ctx) ->
couchbase::error {
120
121 ctx->get(collection, id_1, [=](auto err_ctx_1, auto doc) {
122 if (err_ctx_1.ec()) {
123 fmt::print(
124 stderr, "failed to get document \"{}\": {}\n", id_1, err_ctx_1.ec().message());
125 return;
126 }
127
128 ctx->replace(doc,
129 tao::json::value{ { "some", "other async content" } },
130 [=](auto err_ctx_2, auto ) {
131 if (err_ctx_2.ec()) {
132 fmt::print(stderr,
133 "error replacing content in doc {}: {}\n",
134 id_1,
135 err_ctx_2.ec().message());
136 } else {
137 fmt::print("successfully replaced: {}\n", id_1);
138 }
139 });
140 });
141 ctx->get(collection, id_2, [=](auto err_ctx_1, auto doc) {
142 if (err_ctx_1.ec()) {
143 fmt::print("error getting doc {}: {}", id_2, err_ctx_1.ec().message());
144 return;
145 }
146 ctx->replace(doc,
147 tao::json::value{ { "some", "other async content" } },
148 [=](auto err_ctx_2, auto ) {
149 if (err_ctx_2.ec()) {
150 fmt::print(stderr,
151 "error replacing content in doc {}: {}\n",
152 id_2,
153 err_ctx_2.ec().message());
154 } else {
155 fmt::print("successfully replaced: {}\n", id_2);
156 }
157 });
158 });
159 ctx->get(collection, id_3, [=](auto err_ctx_1, auto doc) {
160 if (err_ctx_1.ec()) {
161 fmt::print(stderr, "error getting doc {}: {}\n", id_3, err_ctx_1.ec().message());
162 return;
163 }
164 ctx->replace(doc,
165 tao::json::value{ { "some", "other async content" } },
166 [=](auto err_ctx_2, auto ) {
167 if (err_ctx_2.ec()) {
168 fmt::print(stderr,
169 "error replacing content in doc {}: {}\n",
170 id_3,
171 err_ctx_2.ec().message());
172 } else {
173 fmt::print("successfully replaced: {}\n", id_3);
174 }
175 });
176 });
177 return {};
178 },
179
180 [barrier](auto tx_err, auto tx_res) {
181 if (tx_err.ec()) {
182 fmt::print(stderr,
183 "error in async transaction {}, {}\n",
184 tx_res.transaction_id,
185 tx_err.ec().message());
186 }
187 barrier->set_value(tx_err.ec());
188 });
189 if (auto async_err = f.get()) {
190 fmt::print(stderr, "received async error from future: message - {}\n", async_err.message());
191 retval = 1;
192 }
194 }
195
196
197 cluster.close().get();
198 return retval;
199}
200
See also simple class that implements an operation in fictional game backend: game_server.cxx (and its asynchronous version in async_game_server.cxx)