/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 2023-Present Couchbase, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "test_helper_integration.hxx" #include "core/operations/management/query_index_build.hxx" #include "core/operations/management/query_index_create.hxx" #include "core/operations/management/query_index_get_all.hxx" #include #include #include #include #include #include #include #include #include namespace start_using { //! [start-using] #include #include #include #include int main(int argc, const char* argv[]) { if (argc != 4) { fmt::print("USAGE: ./start_using couchbase://127.0.0.1 Administrator password\n"); return 1; } std::string connection_string{ argv[1] }; // "couchbase://127.0.0.1" std::string username{ argv[2] }; // "Administrator" std::string password{ argv[3] }; // "password" std::string bucket_name{ "travel-sample" }; // run IO context on separate thread asio::io_context io; auto guard = asio::make_work_guard(io); std::thread io_thread([&io]() { io.run(); }); auto options = couchbase::cluster_options(username, password); // customize through the 'options'. // For example, optimize timeouts for WAN options.apply_profile("wan_development"); // [1] connect to cluster using the given connection string and the options auto [cluster, ec] = couchbase::cluster::connect(io, connection_string, options).get(); if (ec) { fmt::print("unable to connect to the cluster: {}\n", ec.message()); return 1; } // get a bucket reference auto bucket = cluster.bucket(bucket_name); // get a user-defined collection reference auto scope = bucket.scope("tenant_agent_00"); auto collection = scope.collection("users"); { // [2] upsert document auto [ctx, upsert_result] = collection.upsert("my-document", tao::json::value{ { "name", "mike" } }).get(); if (ctx.ec()) { fmt::print("unable to upsert the document \"{}\": {}\n", ctx.id(), ctx.ec().message()); return 1; } fmt::print("saved document \"{}\", cas={}, token={}\n", ctx.id(), upsert_result.cas(), upsert_result.mutation_token().value()); } { // [3] get document auto [ctx, get_result] = collection.get("my-document").get(); if (ctx.ec()) { fmt::print("unable to get the document \"{}\": {}\n", ctx.id(), ctx.ec().message()); return 1; } auto name = get_result.content_as()["name"].get_string(); fmt::print("retrieved document \"{}\", name=\"{}\"\n", ctx.id(), name); } { // [4] N1QL query auto inventory_scope = bucket.scope("inventory"); auto [ctx, query_result] = inventory_scope.query("SELECT * FROM airline WHERE id = 10").get(); if (ctx.ec()) { fmt::print("unable to perform query: {}, ({}, {})\n", ctx.ec().message(), ctx.first_error_code(), ctx.first_error_message()); return 1; } for (const auto& row : query_result.rows_as_json()) { fmt::print("row: {}\n", tao::json::to_string(row)); } } // [5] close cluster connection cluster.close(); guard.reset(); io_thread.join(); return 0; } /* $ ./start_using couchbase://127.0.0.1 Administrator password saved document "my-document", cas=17486a1722b20000 retrieved document "my-document", name="mike" row: {"airline":{"callsign":"MILE-AIR","country":"United States","iata":"Q5","icao":"MLA","id":10,"name":"40-Mile Air","type":"airline"}} */ //! [start-using] } // namespace start_using TEST_CASE("example: start using", "[integration]") { test::utils::integration_test_guard integration; if (integration.cluster_version().is_capella()) { SKIP("Capella does not allow to use REST API to load sample buckets"); } if (!integration.cluster_version().supports_collections()) { SKIP("cluster does not support collections"); } if (integration.cluster_version().is_mock()) { SKIP("GOCAVES cannot load travel-sample bucket. See https://github.com/couchbaselabs/gocaves/issues/101"); } { couchbase::core::operations::management::query_index_create_request req{}; req.index_name = "def_inventory_airline_primary"; req.bucket_name = "travel-sample"; req.scope_name = "inventory"; req.collection_name = "airline"; req.is_primary = true; req.ignore_if_exists = true; auto resp = test::utils::execute(integration.cluster, req); REQUIRE_FALSE(resp.ctx.ec); } { couchbase::core::operations::management::query_index_build_request req{}; req.index_names = { "def_inventory_airline_primary" }; req.bucket_name = "travel-sample"; req.scope_name = "inventory"; req.collection_name = "airline"; auto resp = test::utils::execute(integration.cluster, req); REQUIRE_FALSE(resp.ctx.ec); } CHECK(test::utils::wait_until( [&integration]() { couchbase::core::operations::management::query_index_get_all_request req{}; req.bucket_name = "travel-sample"; req.scope_name = "inventory"; auto resp = test::utils::execute(integration.cluster, req); if (resp.ctx.ec) { return false; } return std::any_of(resp.indexes.begin(), resp.indexes.end(), [](const auto& index) { return index.collection_name == "airline" && index.is_primary && index.state == "online"; }); }, std::chrono::minutes{ 5 })); const auto env = test::utils::test_context::load_from_environment(); const char* argv[] = { "start_using", // name of the "executable" env.connection_string.c_str(), env.username.c_str(), env.password.c_str(), }; REQUIRE(start_using::main(4, argv) == 0); } namespace example_search { //! [example-search] #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { if (argc != 4) { fmt::print("USAGE: ./example_search couchbase://127.0.0.1 Administrator password\n"); return 1; } std::string connection_string{ argv[1] }; // "couchbase://127.0.0.1" std::string username{ argv[2] }; // "Administrator" std::string password{ argv[3] }; // "password" std::string bucket_name{ "travel-sample" }; // run IO context on separate thread asio::io_context io; auto guard = asio::make_work_guard(io); std::thread io_thread([&io]() { io.run(); }); auto options = couchbase::cluster_options(username, password); // customize through the 'options'. // For example, optimize timeouts for WAN options.apply_profile("wan_development"); auto [cluster, ec] = couchbase::cluster::connect(io, connection_string, options).get(); if (ec) { fmt::print("unable to connect to the cluster: {}\n", ec.message()); return 1; } { fmt::print("--- simple query\n"); auto [ctx, result] = cluster.search_query("travel-sample-index", couchbase::query_string_query("nice bar")).get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); } } { fmt::print("--- simple query with fields\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("nice bar"), couchbase::search_options{}.fields({ "description" })) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { auto fields = row.fields_as(); fmt::print("id: {}, score: {}, description: {}\n", row.id(), row.score(), fields["description"].as()); } } { fmt::print("--- simple query with limit\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("nice bar"), couchbase::search_options{}.skip(3).limit(4)) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); } } { fmt::print("--- simple query with highlight\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("nice bar"), couchbase::search_options{}.highlight(couchbase::highlight_style::html, { "description", "title" })) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); for (const auto& [field, fragments] : row.fragments()) { fmt::print("- {}:\n", field); for (const auto& fragment : fragments) { fmt::print("-- {}\n", fragment); } } } } { fmt::print("--- simple query with collections\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("west"), couchbase::search_options{}.collections({ "airline" })) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); } } { fmt::print("--- query with consistency requirements\n"); auto bucket = cluster.bucket(bucket_name); auto collection = bucket.scope("inventory").collection("hotel"); couchbase::mutation_state state; { auto [ctx, upsert_result] = collection .upsert( "prancing-pony", tao::json::value{ { "title", "The Prancing Pony" }, { "type", "hotel" }, { "description", "The inn was located just where the East Road bent round the foot of Bree-hill, within the dike that stretched " "around the town. The building was three stories tall with many windows. Its front faced the Road and it had two " "wings that ran back towards the elevated ground of the hill, such that in the rear the second floor was at ground " "level. " } }) .get(); if (ctx.ec()) { fmt::print("unable to upsert the document \"{}\": {}\n", ctx.id(), ctx.ec().message()); return 1; } fmt::print("saved document \"{}\", cas={}, token={}\n", ctx.id(), upsert_result.cas(), upsert_result.mutation_token().value_or(couchbase::mutation_token{})); state.add(upsert_result); } auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("bree"), couchbase::search_options{}.consistent_with(state)) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); } } { fmt::print("--- complex query\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::boolean_query() .must(couchbase::match_query("honeymoon").field("reviews.content"), couchbase::numeric_range_query().field("reviews.ratings.Overall").min(4)) .must_not(couchbase::match_query("San Francisco").field("city")), couchbase::search_options{}.collections({ "hotel" }).highlight()) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& row : result.rows()) { fmt::print("id: {}, score: {}\n", row.id(), row.score()); } } { fmt::print("--- simple query with facets\n"); auto [ctx, result] = cluster .search_query("travel-sample-index", couchbase::query_string_query("honeymoon"), couchbase::search_options{}.collections({ "hotel" }).facet("by_country", couchbase::term_facet("country", 3))) .get(); if (ctx.ec()) { fmt::print("unable to perform search query: {}, ({}, {})\n", ctx.ec().message(), ctx.status(), ctx.error()); return 1; } fmt::print("{} hits, total: {}\n", result.rows().size(), result.meta_data().metrics().total_rows()); for (const auto& [name, facet] : result.facets()) { fmt::print("{} facet: total={}, missing={}\n", name, facet->total(), facet->missing()); if (name == "by_country") { auto term_facet = std::static_pointer_cast(facet); for (const auto& group : term_facet->terms()) { fmt::print("* {}: {}\n", group.name(), group.count()); } } } } // close cluster connection cluster.close(); guard.reset(); io_thread.join(); return 0; } /* $ ./example_search couchbase://127.0.0.1 Administrator password saved document "my-document", cas=17486a1722b20000 retrieved document "my-document", name="mike" row: {"airline":{"callsign":"MILE-AIR","country":"United States","iata":"Q5","icao":"MLA","id":10,"name":"40-Mile Air","type":"airline"}} */ //! [example-search] } // namespace example_search TEST_CASE("example: search", "[integration]") { test::utils::integration_test_guard integration; if (integration.cluster_version().is_capella()) { SKIP("Capella does not allow to use REST API to load sample buckets"); } if (!integration.cluster_version().supports_collections()) { SKIP("cluster does not support collections"); } const auto env = test::utils::test_context::load_from_environment(); const char* argv[] = { "example_search", // name of the "executable" env.connection_string.c_str(), env.username.c_str(), env.password.c_str(), }; REQUIRE(example_search::main(4, argv) == 0); } namespace example_buckets { //! [example-buckets] #include int main(int argc, const char* argv[]) { if (argc != 4) { fmt::print("USAGE: ./example_buckets couchbase://127.0.0.1 Administrator password\n"); return 1; } std::string connection_string{ argv[1] }; // "couchbase://127.0.0.1" std::string username{ argv[2] }; // "Administrator" std::string password{ argv[3] }; // "password" std::string bucket_name{ "travel-sample" }; // run IO context on separate thread asio::io_context io; auto guard = asio::make_work_guard(io); std::thread io_thread([&io]() { io.run(); }); auto options = couchbase::cluster_options(username, password); // customize through the 'options'. // For example, optimize timeouts for WAN options.apply_profile("wan_development"); auto [cluster, ec] = couchbase::cluster::connect(io, connection_string, options).get(); if (ec) { fmt::print("unable to connect to the cluster: {}\n", ec.message()); return 1; } auto manager = cluster.buckets(); couchbase::management::cluster::bucket_settings bucket_settings{}; std::string test_bucket_name = "cxx_test_integration_examples_bucket"; bucket_settings.name = test_bucket_name; bucket_settings.ram_quota_mb = 150; bucket_settings.bucket_type = couchbase::management::cluster::bucket_type::couchbase; bucket_settings.eviction_policy = couchbase::management::cluster::bucket_eviction_policy::value_only; bucket_settings.flush_enabled = true; bucket_settings.replica_indexes = true; bucket_settings.conflict_resolution_type = couchbase::management::cluster::bucket_conflict_resolution::sequence_number; { fmt::print("--- create bucket\n"); auto ctx = manager.create_bucket(bucket_settings).get(); if (ctx.ec()) { if (ctx.ec() == couchbase::errc::common::invalid_argument) { fmt::print("bucket already exists\n"); } else { fmt::print("unable to create the bucket: {}\n", ctx.ec().message()); return 1; } } else { fmt::print("--- bucket has been successfully created\n"); } } { fmt::print("--- get bucket\n"); auto [ctx, bucket] = manager.get_bucket(bucket_name).get(); if (ctx.ec()) { fmt::print("unable to get the bucket: {}\n", ctx.ec().message()); return 1; } fmt::print("name of fetched bucket: {}\n", bucket.name); } { fmt::print("--- get all buckets\n"); auto [ctx, buckets] = manager.get_all_buckets().get(); if (ctx.ec()) { fmt::print("unable to get all buckets: {}\n", ctx.ec().message()); return 1; } for (const auto& fetched_bucket : buckets) { fmt::print("Bucket name: {}\n", fetched_bucket.name); } } { fmt::print("--- update bucket\n"); bucket_settings.ram_quota_mb = 150; auto ctx = manager.update_bucket(bucket_settings).get(); if (ctx.ec()) { fmt::print("unable to update the bucket: {}\n", ctx.ec().message()); return 1; } fmt::print("bucket has been updated\n"); } { fmt::print("--- drop bucket\n"); auto ctx = manager.drop_bucket(test_bucket_name).get(); if (ctx.ec()) { fmt::print("unable to drop the bucket: {}\n", ctx.ec().message()); return 1; } fmt::print("bucket has been dropped\n"); } // close cluster connection cluster.close(); guard.reset(); io_thread.join(); return 0; } //! [example-buckets] } // namespace example_buckets TEST_CASE("example: bucket management", "[integration]") { test::utils::integration_test_guard integration; if (integration.cluster_version().is_capella()) { SKIP("Capella does not allow to use REST API to load sample buckets"); } const auto env = test::utils::test_context::load_from_environment(); const char* argv[] = { "example_buckets", // name of the "executable" env.connection_string.c_str(), env.username.c_str(), env.password.c_str(), }; REQUIRE(example_buckets::main(4, argv) == 0); }