Getting Started with Bigtable

This example is a very simple "hello world" application, written in C++, that illustrates how to:

  • Connect to a Cloud Bigtable instance.
  • Create a new table.
  • Write data to the table.
  • Read the data back.
  • Delete the table.

Running the example

This example uses the Cloud Bigtable C++ Client Library to communicate with Cloud Bigtable.

To run the example program, follow the instructions for the example on GitHub.

Including the Necessary Headers

The example uses the following headers:

#include "google/cloud/bigtable/admin/bigtable_table_admin_client.h"
#include "google/cloud/bigtable/resource_names.h"
#include "google/cloud/bigtable/table.h"

Introduce aliases to make the example more readable

  namespace cbt = ::google::cloud::bigtable;
  namespace cbta = ::google::cloud::bigtable_admin;
  using ::google::cloud::StatusOr;

Connecting to Cloud Bigtable to manage tables

To manage tables, connect to Cloud Bigtable using bigtable_admin::MakeBigtableTableAdminConnection():

  // Connect to the Cloud Bigtable Admin API.
  cbta::BigtableTableAdminClient table_admin(
      cbta::MakeBigtableTableAdminConnection());

  // Create an object to access the Cloud Bigtable Data API.
  cbt::Table table(cbt::MakeDataConnection(),
                   cbt::TableResource(project_id, instance_id, table_id));
See Also

bigtable_admin::BigtableTableAdminClient for more information on how to perform administrative operations on tables.

Creating a table

Create a table with BigtableTableAdminClient::CreateTable():

  // Define the desired schema for the Table.
  google::bigtable::admin::v2::Table t;
  auto& families = *t.mutable_column_families();
  families["family"].mutable_gc_rule()->set_max_num_versions(1);

  // Create a table.
  std::string instance_name = cbt::InstanceName(project_id, instance_id);
  StatusOr<google::bigtable::admin::v2::Table> schema =
      table_admin.CreateTable(instance_name, table_id, std::move(t));
See Also

bigtable::BigtableTableAdminClient for additional operations to list, read, modify, and delete tables.

See Also

https://cloud.google.com/bigtable/docs/overview for an overview of the Cloud Bigtable storage model, including an introduction to important Cloud Bigtable concepts, such as row keys, column families, columns, and cells.

See Also

https://cloud.google.com/bigtable/docs/schema-design for suggestions on how to design your table schema.

Connecting to Cloud Bigtable to read and write data

To read and write data, connect to Cloud Bigtable using bigtable::MakeDataConnection():

  // Create an object to access the Cloud Bigtable Data API.
  cbt::Table table(cbt::MakeDataConnection(),
                   cbt::TableResource(project_id, instance_id, table_id));
See Also

bigtable::Table for the functions to read and write data to Cloud Bigtable.

Writing Rows to a table

To write a single row use Table::Apply():

  std::vector<std::string> greetings{"Hello World!", "Hello Cloud Bigtable!",
                                     "Hello C++!"};
  int i = 0;
  for (auto const& greeting : greetings) {
    // Each row has a unique row key.
    //
    // Note: This example uses sequential numeric IDs for simplicity, but
    // this can result in poor performance in a production application.
    // Since rows are stored in sorted order by key, sequential keys can
    // result in poor distribution of operations across nodes.
    //
    // For more information about how to design a Bigtable schema for the
    // best performance, see the documentation:
    //
    //     https://cloud.google.com/bigtable/docs/schema-design
    std::string row_key = "key-" + std::to_string(i);
    google::cloud::Status status = table.Apply(cbt::SingleRowMutation(
        std::move(row_key), cbt::SetCell("family", "c0", greeting)));

    if (!status.ok()) throw std::runtime_error(status.message());
    ++i;
  }
See Also

Table::BulkApply() to modify multiple rows in a single request.

See Also

DeleteFromFamily(), DeleteFromRow(), DeleteFromColumn() to delete some (or all) the cells in a row.

See Also

One of the overloads for SetCell() allows setting an explicit timestamp, which is an idempotent mutation and thus can be automatically retried on transient errors.

Creating filter for the column families

Use a filter to select only a subset of the cells in a row, in this case, we will select only the rows in the family column family and the c0 column:

  cbt::Filter filter = cbt::Filter::ColumnRangeClosed("family", "c0", "c0");
See Also

bigtable::Filter to filter the column families, columns, and even the timestamps returned by ReadRow().

Reading data back: get a single row by its key

Get a row directly using its key with Table::ReadRow():

  StatusOr<std::pair<bool, cbt::Row>> result = table.ReadRow("key-0", filter);
  if (!result) throw std::move(result).status();
  if (!result->first) {
    std::cout << "Cannot find row 'key-0' in the table: " << table.table_name()
              << "\n";
    return;
  }
  cbt::Cell const& cell = result->second.cells().front();
  std::cout << cell.family_name() << ":" << cell.column_qualifier() << "    @ "
            << cell.timestamp().count() << "us\n"
            << '"' << cell.value() << '"' << "\n";
See Also

bigtable::Filter to filter the column families, columns, and even the timestamps returned by ReadRow().

See Also

Table::ReadRows() to iterate over multiple rows.

Reading data back: scanning all table rows

Use Table::ReadRows() to scan all of the rows in a table.

  for (auto& row : table.ReadRows(cbt::RowRange::InfiniteRange(),
                                  cbt::Filter::PassAllFilter())) {
    if (!row) throw std::move(row).status();
    std::cout << row->row_key() << ":\n";
    for (cbt::Cell const& c : row->cells()) {
      std::cout << "\t" << c.family_name() << ":" << c.column_qualifier()
                << "    @ " << c.timestamp().count() << "us\n"
                << "\t\"" << c.value() << '"' << "\n";
    }
  }
See Also

bigtable::RowRange to read only a portion of the table.

See Also

bigtable::RowSet to form arbitrary groups of ranges and specific row keys in a Table::ReadRows() request.

See Also

bigtable::Row for details of the contents in a row.

Deleting a table

Delete a table with BigtableTableAdminClient::DeleteTable().

  google::cloud::Status status = table_admin.DeleteTable(table.table_name());
  if (!status.ok()) throw std::runtime_error(status.message());

Putting it all together

Here is the full example


#include "google/cloud/bigtable/admin/bigtable_table_admin_client.h"
#include "google/cloud/bigtable/resource_names.h"
#include "google/cloud/bigtable/table.h"
#include "google/cloud/bigtable/examples/bigtable_examples_common.h"
#include "google/cloud/bigtable/testing/random_names.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/internal/random.h"
#include "google/cloud/log.h"
#include <iostream>

namespace {

using ::google::cloud::bigtable::examples::Usage;

void BigtableHelloWorld(std::vector<std::string> const& argv) {
  if (argv.size() != 3) {
    throw Usage{"hello-world <project-id> <instance-id> <table-id>"};
  }
  std::string const& project_id = argv[0];
  std::string const& instance_id = argv[1];
  std::string const& table_id = argv[2];

  // Create a namespace alias to make the code easier to read.
  namespace cbt = ::google::cloud::bigtable;
  namespace cbta = ::google::cloud::bigtable_admin;
  using ::google::cloud::StatusOr;

  // Connect to the Cloud Bigtable Admin API.
  cbta::BigtableTableAdminClient table_admin(
      cbta::MakeBigtableTableAdminConnection());

  // Create an object to access the Cloud Bigtable Data API.
  cbt::Table table(cbt::MakeDataConnection(),
                   cbt::TableResource(project_id, instance_id, table_id));

  // Define the desired schema for the Table.
  google::bigtable::admin::v2::Table t;
  auto& families = *t.mutable_column_families();
  families["family"].mutable_gc_rule()->set_max_num_versions(1);

  // Create a table.
  std::string instance_name = cbt::InstanceName(project_id, instance_id);
  StatusOr<google::bigtable::admin::v2::Table> schema =
      table_admin.CreateTable(instance_name, table_id, std::move(t));

  // Modify (and create if necessary) a row.
  std::vector<std::string> greetings{"Hello World!", "Hello Cloud Bigtable!",
                                     "Hello C++!"};
  int i = 0;
  for (auto const& greeting : greetings) {
    // Each row has a unique row key.
    //
    // Note: This example uses sequential numeric IDs for simplicity, but
    // this can result in poor performance in a production application.
    // Since rows are stored in sorted order by key, sequential keys can
    // result in poor distribution of operations across nodes.
    //
    // For more information about how to design a Bigtable schema for the
    // best performance, see the documentation:
    //
    //     https://cloud.google.com/bigtable/docs/schema-design
    std::string row_key = "key-" + std::to_string(i);
    google::cloud::Status status = table.Apply(cbt::SingleRowMutation(
        std::move(row_key), cbt::SetCell("family", "c0", greeting)));

    if (!status.ok()) throw std::runtime_error(status.message());
    ++i;
  }

  cbt::Filter filter = cbt::Filter::ColumnRangeClosed("family", "c0", "c0");

  // Read a single row.
  StatusOr<std::pair<bool, cbt::Row>> result = table.ReadRow("key-0", filter);
  if (!result) throw std::move(result).status();
  if (!result->first) {
    std::cout << "Cannot find row 'key-0' in the table: " << table.table_name()
              << "\n";
    return;
  }
  cbt::Cell const& cell = result->second.cells().front();
  std::cout << cell.family_name() << ":" << cell.column_qualifier() << "    @ "
            << cell.timestamp().count() << "us\n"
            << '"' << cell.value() << '"' << "\n";

  // Read all rows.
  for (auto& row : table.ReadRows(cbt::RowRange::InfiniteRange(),
                                  cbt::Filter::PassAllFilter())) {
    if (!row) throw std::move(row).status();
    std::cout << row->row_key() << ":\n";
    for (cbt::Cell const& c : row->cells()) {
      std::cout << "\t" << c.family_name() << ":" << c.column_qualifier()
                << "    @ " << c.timestamp().count() << "us\n"
                << "\t\"" << c.value() << '"' << "\n";
    }
  }

  // Delete the table
  google::cloud::Status status = table_admin.DeleteTable(table.table_name());
  if (!status.ok()) throw std::runtime_error(status.message());
}

void RunAll(std::vector<std::string> const& argv) {
  namespace examples = ::google::cloud::bigtable::examples;
  namespace cbt = ::google::cloud::bigtable;

  if (!argv.empty()) throw Usage{"auto"};
  if (!examples::RunAdminIntegrationTests()) return;
  examples::CheckEnvironmentVariablesAreSet({
      "GOOGLE_CLOUD_PROJECT",
      "GOOGLE_CLOUD_CPP_BIGTABLE_TEST_INSTANCE_ID",
  });
  auto const project_id =
      google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value();
  auto const instance_id = google::cloud::internal::GetEnv(
                               "GOOGLE_CLOUD_CPP_BIGTABLE_TEST_INSTANCE_ID")
                               .value();

  auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
  auto table_id = cbt::testing::RandomTableId(generator);

  std::cout << "\nRunning the BigtableHelloWorld() example" << std::endl;
  BigtableHelloWorld({project_id, instance_id, table_id});
}

}  // namespace

int main(int argc, char* argv[]) try {
  google::cloud::bigtable::examples::Example example({
      {"auto", RunAll},
      {"hello-world", BigtableHelloWorld},
  });
  return example.Run(argc, argv);
} catch (std::exception const& ex) {
  std::cerr << ex.what() << "\n";
  ::google::cloud::LogSink::Instance().Flush();
  return 1;
}