Couchbase C++ SDK 1.0.1 (rev. 58d46d7)
Loading...
Searching...
No Matches
Couchbase C++ SDK
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 couchbase::collection::upsert(),
  • [3] retrieves document back with couchbase::collection::get(), extracts content as generic JSON value, and prints one of the fields,
  • [4] performs N1QL query with couchbase::scope::query() and prints the result.
  • [5] output results obtained in [4] using custom type defined in [6]
  • [7] closes the cluster and deallocate resources
74#include <couchbase/cluster.hxx>
75#include <couchbase/fmt/cas.hxx>
77
78#include <tao/json.hpp>
79#include <tao/json/contrib/traits.hpp>
80
81int
82main(int argc, const char* argv[])
83{
84 if (argc != 4) {
85 fmt::print("USAGE: ./start_using couchbase://127.0.0.1 Administrator password\n");
86 return 1;
87 }
88
89 std::string connection_string{ argv[1] }; // "couchbase://127.0.0.1"
90 std::string username{ argv[2] }; // "Administrator"
91 std::string password{ argv[3] }; // "password"
92 std::string bucket_name{ "travel-sample" };
93
94 auto options = couchbase::cluster_options(username, password);
95 // customize through the 'options'.
96 // For example, optimize timeouts for WAN
97 options.apply_profile("wan_development");
98
99 // [1] connect to cluster using the given connection string and the options
100 auto [connect_err, cluster] = couchbase::cluster::connect(connection_string, options).get();
101 if (connect_err) {
102 fmt::print("unable to connect to the cluster: {}\n", connect_err);
103 return 1;
104 }
105
106 // get a bucket reference
107 auto bucket = cluster.bucket(bucket_name);
108
109 // get a user-defined collection reference
110 auto scope = bucket.scope("tenant_agent_00");
111 auto collection = scope.collection("users");
112
113 { // [2] upsert document
114 std::string doc_id = "my-document";
115 auto [err, upsert_result] =
116 collection.upsert(doc_id, tao::json::value{ { "name", "mike" } }).get();
117 if (err.ec()) {
118 fmt::print("unable to upsert the document \"{}\": {}\n", doc_id, err);
119 return 1;
120 }
121 fmt::print("saved document \"{}\", cas={}, token={}\n",
122 doc_id,
123 upsert_result.cas(),
124 upsert_result.mutation_token().value());
125 }
126
127 { // [3] get document
128 std::string doc_id = "my-document";
129 auto [err, get_result] = collection.get(doc_id).get();
130 if (err.ec()) {
131 fmt::print("unable to get the document \"{}\": {}\n", doc_id, err);
132 return 1;
133 }
134 auto name = get_result.content_as<tao::json::value>()["name"].get_string();
135 fmt::print("retrieved document \"{}\", name=\"{}\"\n", doc_id, name);
136 }
137
138 { // [4] N1QL query
139 auto inventory_scope = bucket.scope("inventory");
140 // Select first 5 hotels from US or UK, that describe themselves as cheap
141 // and order them by overall rating.
142 std::string query{ R"(
143 SELECT META(h).id, h AS doc,
144 AVG(r.ratings.Overall) AS avg_rating
145 FROM hotel h
146 UNNEST h.reviews r
147 WHERE h.country IN $1 AND h.description LIKE "%cheap%"
148 GROUP BY META(h).id, h
149 ORDER BY avg_rating DESC
150 LIMIT 5;
151 )" };
152 auto query_options = couchbase::query_options{}.positional_parameters(
153 std::vector{ "United States", "United Kingdom" });
154 auto [error, query_result] = inventory_scope.query(query, query_options).get();
155 if (error) {
156 fmt::print("unable to perform query: {}\n", error.ctx().to_json());
157 return 1;
158 }
159 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
160 for (auto& row : query_result.rows_as_json()) {
161 fmt::println("{:<15} {:<15} {:>10.2f} {:<30}",
162 row["id"].as<std::string>(),
163 row["doc"]["country"].as<std::string>(),
164 row["avg_rating"].as<double>(),
165 row["doc"]["title"].as<std::string>());
166 }
167
168 // [5] iterate over results using custom type
169 fmt::println("{:<15} {:<15} {:>10} {:<30}", "ID", "Country", "Rating", "Hotel");
170 for (const auto& row : query_result.rows_as<couchbase::codec::tao_json_serializer, hotel>()) {
171 fmt::println(
172 "{:<15} {:<15} {:>10.2f} {:<30}", row.id, row.country, row.average_rating, row.name);
173 }
174 }
175
176 // [7] close cluster connection
177 cluster.close().get();
178 return 0;
179}
180
181/*
182
183$ ./start_using couchbase://127.0.0.1 Administrator password
184saved document "my-document", cas=17ed9f687ee90000, token=travel-sample:110:101634532779186:101
185retrieved document "my-document", name="mike"
186ID Country Rating Hotel
187hotel_26169 United States 4.75 San Francisco/Twin Peaks-Lake Merced
188hotel_26499 United States 4.60 Santa Monica
189hotel_3616 United Kingdom 4.57 Birmingham (England)
190hotel_7387 United States 4.50 Death Valley National Park
191hotel_25588 United States 4.44 San Francisco/Civic Center-Tenderloin
192
193 */
47// [6] definition of the custom type and its decoder
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 couchbase::transactions::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 couchbase::transactions::transactions::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
  • [5] close cluster connection
30#include <couchbase/cluster.hxx>
31#include <couchbase/fmt/cas.hxx>
32
33#include <tao/json.hpp>
34
35int
36main(int argc, const char* argv[])
37{
38 if (argc != 4) {
39 fmt::print("USAGE: ./blocking-txn couchbase://127.0.0.1 Administrator password\n");
40 return 1;
41 }
42
43 int retval = 0;
44
45 const std::string connection_string{ argv[1] };
46 const std::string username{ argv[2] };
47 const std::string password{ argv[3] };
48
49 auto options = couchbase::cluster_options(username, password);
50 // customize through the 'options'.
51 // For example, optimize timeouts for WAN
52 options.apply_profile("wan_development");
53
54 // [1] connect to cluster using the given connection string and the options
55 auto [connect_err, cluster] = couchbase::cluster::connect(connection_string, options).get();
56 if (connect_err) {
57 fmt::print("unable to connect to the cluster: {}\n", connect_err);
58 return 1;
59 }
60
61 // [2] persist three documents to the default collection of bucket "default"
62 auto collection = cluster.bucket("default").default_collection();
63 constexpr auto id_1 = "my-doc_1";
64 constexpr auto id_2 = "my_doc_2";
65 constexpr auto id_3 = "my_doc_3";
66 const tao::json::value content = { { "some", "content" } };
67
68 for (const auto& id : { id_1, id_2, id_3 }) {
69 if (auto [err, res] = collection.upsert(id, content).get(); err.ec()) {
70 fmt::print(
71 stderr, "upsert \"{}\" failed before starting transaction: {}\n", id, err.ec().message());
72 return 1;
73 }
74 }
75
76 { // [3] blocking transaction
78 auto [tx_err, tx_res] = cluster.transactions()->run(
79 // [3.1] closure argument to run() method encapsulates logic, that has to be run in
80 // transaction
81 [=](std::shared_ptr<couchbase::transactions::attempt_context> ctx) -> couchbase::error {
82 // [3.2] get document
83 auto [err_ctx, doc] = ctx->get(collection, id_1);
84 if (err_ctx.ec()) {
85 fmt::print(stderr, "failed to get document \"{}\": {}\n", id_1, err_ctx.ec().message());
86 // [3.3] don't continue the transaction logic
87 return {};
88 }
89 // [3.4] replace document's content
90 ctx->replace(doc, tao::json::value{ { "some", "other content" } });
91 return {};
92 });
93 // [3.5] check the overall status of the transaction
94 if (tx_err.ec()) {
95 fmt::print(stderr,
96 "error in transaction {}, cause: {}\n",
97 tx_err.ec().message(),
98 tx_err.cause().has_value() ? tx_err.cause().value().ec().message() : "");
99 retval = 1;
100 } else {
101 fmt::print("transaction {} completed successfully\n", tx_res.transaction_id);
102 }
104 }
105
106 { // [4] asynchronous transaction
108 // [4.1] create promise to retrieve result from the transaction
109 auto barrier = std::make_shared<std::promise<std::error_code>>();
110 auto f = barrier->get_future();
111 cluster.transactions()->run(
112 // [4.2] closure argument to run() method encapsulates logic, that has to be run in
113 // transaction
114 [=](std::shared_ptr<couchbase::transactions::async_attempt_context> ctx) -> couchbase::error {
115 // [4.3] get document
116 ctx->get(collection, id_1, [=](auto err_ctx_1, auto doc) {
117 if (err_ctx_1.ec()) {
118 fmt::print(
119 stderr, "failed to get document \"{}\": {}\n", id_1, err_ctx_1.ec().message());
120 return;
121 }
122 // [4.4] replace document's content
123 ctx->replace(doc,
124 tao::json::value{ { "some", "other async content" } },
125 [=](auto err_ctx_2, auto /*res*/) {
126 if (err_ctx_2.ec()) {
127 fmt::print(stderr,
128 "error replacing content in doc {}: {}\n",
129 id_1,
130 err_ctx_2.ec().message());
131 } else {
132 fmt::print("successfully replaced: {}\n", id_1);
133 }
134 });
135 });
136 ctx->get(collection, id_2, [=](auto err_ctx_1, auto doc) {
137 if (err_ctx_1.ec()) {
138 fmt::print("error getting doc {}: {}", id_2, err_ctx_1.ec().message());
139 return;
140 }
141 ctx->replace(doc,
142 tao::json::value{ { "some", "other async content" } },
143 [=](auto err_ctx_2, auto /*res*/) {
144 if (err_ctx_2.ec()) {
145 fmt::print(stderr,
146 "error replacing content in doc {}: {}\n",
147 id_2,
148 err_ctx_2.ec().message());
149 } else {
150 fmt::print("successfully replaced: {}\n", id_2);
151 }
152 });
153 });
154 ctx->get(collection, id_3, [=](auto err_ctx_1, auto doc) {
155 if (err_ctx_1.ec()) {
156 fmt::print(stderr, "error getting doc {}: {}\n", id_3, err_ctx_1.ec().message());
157 return;
158 }
159 ctx->replace(doc,
160 tao::json::value{ { "some", "other async content" } },
161 [=](auto err_ctx_2, auto /*res*/) {
162 if (err_ctx_2.ec()) {
163 fmt::print(stderr,
164 "error replacing content in doc {}: {}\n",
165 id_3,
166 err_ctx_2.ec().message());
167 } else {
168 fmt::print("successfully replaced: {}\n", id_3);
169 }
170 });
171 });
172 return {};
173 },
174 // [4.5], second closure represents transaction completion logic
175 [barrier](auto tx_err, auto tx_res) {
176 if (tx_err.ec()) {
177 fmt::print(stderr,
178 "error in async transaction {}, {}\n",
179 tx_res.transaction_id,
180 tx_err.ec().message());
181 }
182 barrier->set_value(tx_err.ec());
183 });
184 if (auto async_err = f.get()) {
185 fmt::print(stderr, "received async error from future: message - {}\n", async_err.message());
186 retval = 1;
187 }
189 }
190
191 // [5], close cluster connection
192 cluster.close().get();
193 return retval;
194}
195

See also simple class that implements an operation in fictional game backend: game_server.cxx (and its asynchronous version in async_game_server.cxx)