- 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
94
95#include <tao/json.hpp>
96#include <tao/json/contrib/traits.hpp>
97
98int
99main(int argc, const char* argv[])
100{
101 if (argc != 4) {
102 fmt::print("USAGE: ./start_using couchbase://127.0.0.1 Administrator password\n");
103 return 1;
104 }
105
106 std::string connection_string{ argv[1] };
107 std::string username{ argv[2] };
108 std::string password{ argv[3] };
109 std::string bucket_name{ "travel-sample" };
110
111 auto options = couchbase::cluster_options(username, password);
112
113
114 options.apply_profile("wan_development");
115
116
118 if (connect_err) {
119 fmt::print("unable to connect to the cluster: {}\n", connect_err);
120 return 1;
121 }
122
123
124 auto bucket = cluster.bucket(bucket_name);
125
126
127 auto scope = bucket.scope("tenant_agent_00");
128 auto collection = scope.collection("users");
129
130 {
131 std::string doc_id = "my-document";
132 auto [err, upsert_result] =
133 collection.upsert(doc_id, tao::json::value{ { "name", "mike" } }).get();
134 if (err.ec()) {
135 fmt::print("unable to upsert the document \"{}\": {}\n", doc_id, err);
136 return 1;
137 }
138 fmt::print("saved document \"{}\", cas={}, token={}\n",
139 doc_id,
140 upsert_result.cas(),
141 upsert_result.mutation_token().value());
142 }
143
144 {
145 std::string doc_id = "my-document";
146 auto [err, get_result] = collection.get(doc_id).get();
147 if (err.ec()) {
148 fmt::print("unable to get the document \"{}\": {}\n", doc_id, err);
149 return 1;
150 }
151 auto name = get_result.content_as<tao::json::value>()["name"].get_string();
152 fmt::print("retrieved document \"{}\", name=\"{}\"\n", doc_id, name);
153 }
154
155 {
156 auto inventory_scope = bucket.scope("inventory");
157
158
159 std::string
query{ R
"(
160 SELECT META(h).id, h AS doc,
161 AVG(r.ratings.Overall) AS avg_rating
162 FROM hotel h
163 UNNEST h.reviews r
164 WHERE h.country IN $1 AND h.description LIKE "%cheap%"
165 GROUP BY META(h).id, h
166 ORDER BY avg_rating DESC
167 LIMIT 5;
168 )" };
169 auto query_options = couchbase::query_options{}.positional_parameters(
170 std::vector{ "United States", "United Kingdom" });
171 auto [
error, query_result] = inventory_scope.query(query, query_options).get();
172 if (error) {
173 fmt::print(
"unable to perform query: {}\n",
error.ctx().to_json());
174 return 1;
175 }
176 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
177 for (auto& row : query_result.rows_as()) {
178 fmt::println("{:<15} {:<15} {:>10.2f} {:<30}",
179 row["id"].as<std::string>(),
180 row["doc"]["country"].as<std::string>(),
181 row["avg_rating"].as<double>(),
182 row["doc"]["title"].as<std::string>());
183 }
184
185
186 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
187 for (const auto& row : query_result.rows_as<couchbase::codec::tao_json_serializer, hotel>()) {
188 fmt::println(
189 "{:<15} {:<15} {:>10.2f} {:<30}", row.id, row.country, row.average_rating, row.name);
190 }
191 }
192
193
194 cluster.close().get();
195 return 0;
196}
197
198
199
200
201
202
203
204
205
206
207
208
209
210
63
64struct hotel {
65 std::string id{};
66 std::string name{};
67 std::string country{};
68 double average_rating{};
69};
70
71template<>
72struct tao::json::traits<hotel> {
73 template<template<typename...> class Traits>
74 static auto as(const tao::json::basic_value<Traits>& v) -> hotel
75 {
76 hotel result;
77 auto object = v.get_object();
78 result.id = object["id"].template as<std::string>();
79 result.average_rating = object["avg_rating"].template as<double>();
80 result.name = object["doc"]["title"].template as<std::string>();
81 result.country = object["doc"]["country"].template as<std::string>();
82 return result;
83 }
84};
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)