Couchbase C++ SDK 1.0.6 (rev. b5b5145)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
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 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
74#include <couchbase/cluster.hxx>
76#include <couchbase/fmt/cas.hxx>
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] }; // "couchbase://127.0.0.1"
91 std::string username{ argv[2] }; // "Administrator"
92 std::string password{ argv[3] }; // "password"
93 std::string bucket_name{ "travel-sample" };
94
95 auto options = couchbase::cluster_options(username, password);
96 // customize through the 'options'.
97 // For example, optimize timeouts for WAN
98 options.apply_profile("wan_development");
99
100 // [1] connect to cluster using the given connection string and the options
101 auto [connect_err, cluster] = couchbase::cluster::connect(connection_string, options).get();
102 if (connect_err) {
103 fmt::print("unable to connect to the cluster: {}\n", connect_err);
104 return 1;
105 }
106
107 // get a bucket reference
108 auto bucket = cluster.bucket(bucket_name);
109
110 // get a user-defined collection reference
111 auto scope = bucket.scope("tenant_agent_00");
112 auto collection = scope.collection("users");
113
114 { // [2] upsert document
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 { // [3] get document
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 { // [4] N1QL query
140 auto inventory_scope = bucket.scope("inventory");
141 // Select first 5 hotels from US or UK, that describe themselves as cheap
142 // and order them by overall rating.
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 // [5] iterate over results using custom type
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 // [7] close cluster connection
178 cluster.close().get();
179 return 0;
180}
181
182/*
183
184$ ./start_using couchbase://127.0.0.1 Administrator password
185saved document "my-document", cas=17ed9f687ee90000, token=travel-sample:110:101634532779186:101
186retrieved document "my-document", name="mike"
187ID Country Rating Hotel
188hotel_26169 United States 4.75 San Francisco/Twin Peaks-Lake Merced
189hotel_26499 United States 4.60 Santa Monica
190hotel_3616 United Kingdom 4.57 Birmingham (England)
191hotel_7387 United States 4.50 Death Valley National Park
192hotel_25588 United States 4.44 San Francisco/Civic Center-Tenderloin
193
194 */
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 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
34#include <couchbase/cluster.hxx>
36#include <couchbase/fmt/cas.hxx>
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 // customize through the 'options'.
56 // For example, optimize timeouts for WAN
57 options.apply_profile("wan_development");
58
59 // [1] connect to cluster using the given connection string and the options
60 auto [connect_err, cluster] = couchbase::cluster::connect(connection_string, options).get();
61 if (connect_err) {
62 fmt::print("unable to connect to the cluster: {}\n", connect_err);
63 return 1;
64 }
65
66 // [2] persist three documents to the default collection of bucket "default"
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 { // [3] blocking transaction
83 auto [tx_err, tx_res] = cluster.transactions()->run(
84 // [3.1] closure argument to run() method encapsulates logic, that has to be run in
85 // transaction
86 [=](std::shared_ptr<couchbase::transactions::attempt_context> ctx) -> couchbase::error {
87 // [3.2] get document
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 // [3.3] don't continue the transaction logic
92 return {};
93 }
94 // [3.4] replace document's content
95 ctx->replace(doc, tao::json::value{ { "some", "other content" } });
96 return {};
97 });
98 // [3.5] check the overall status of the transaction
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 { // [4] asynchronous transaction
113 // [4.1] create promise to retrieve result from the transaction
114 auto barrier = std::make_shared<std::promise<std::error_code>>();
115 auto f = barrier->get_future();
116 cluster.transactions()->run(
117 // [4.2] closure argument to run() method encapsulates logic, that has to be run in
118 // transaction
119 [=](std::shared_ptr<couchbase::transactions::async_attempt_context> ctx) -> couchbase::error {
120 // [4.3] get document
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 // [4.4] replace document's content
128 ctx->replace(doc,
129 tao::json::value{ { "some", "other async content" } },
130 [=](auto err_ctx_2, auto /*res*/) {
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 /*res*/) {
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 /*res*/) {
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 // [4.5], second closure represents transaction completion logic
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 // [5], close cluster connection
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)