home shape

ArangoDB | Introduction to Fuerte: ArangoDB C++ Driver

In this post, we will introduce you to our new ArangoDB C++ diver fuerte. fuerte allows you to communicate via HTTP and VST with ArangoDB instances. You will learn how to create collections, insert documents, retrieve documents, write AQL Queries and how to use the asynchronous API of the driver.

Requirements (Running the sample)

Please download and inspect the sample described in this post. The sample consists of a C++ – Example Source Code – File and a CMakeLists.txt. You need to install the fuerte diver, which can be found on github, into your system before compiling the sample. Please follow the instructions provided in the drivers README.md.

Into the code

So far we have implemented the drivers basic functionally. It is still undecided what the final interface will look like. So you are in the unlucky position that you have to write more code than in java or javascript. On the other hand, you are lucky to have the opportunity to make some great suggestions for the final interface. Please use Github issues for suggestions and feedback.

We skip some basic stuff like include and using statements and go straight to the first few lines that are related to fuerte:

c++
namespace f = ::arangodb::fuerte;
f::ConnectionBuilder cbuilder;
cbuilder.host("vst://127.0.0.1:8529"s);
cbuilder.authenticationType(f::AuthenticationType::Basic);
cbuilder.user("root"s);
cbuilder.password(""s);
auto eventLoopService = std::unique_ptr(new f::EventLoopService(1));
auto  connection = cbuilder.connect(*eventLoopService);

Those lines create a connection to the database with the endpoint `vst://127.0.0.1:8529`. First, a ConnectionBuilder is created. The builder is configured to use basic authentication with ArangoDB’s default username and password (password field is empty unless you changed that). Finally, the connection is created by the call to `connect(…)`. We pass an `EventLoopService` that uses one thread. Because the service uses select internally one thread will suffice for most applications.

c++
auto request = f::createRequest(f::RestVerb::Get, "/_api/version"s);
auto result = connection->sendRequest(std::move(request));
auto slice = result->slices().front();
auto version = slice.get("version").copyString();
auto server = slice.get("server").copyString();
assert(server ==  "arango");
std::cout << "you are connected to: "s << server << " " << version << std::endl;

The next block creates a request that is sent synchronous (blocking) to the server. The execution continues, when the functions receive the answer from the server. Then we can read version information from the result. The result provides a method `slices()` that is giving access to response's body. In most cases, the payload will be a single velocypack object. Similar to a json-object when communicating over http/json.

VPackBuilder builder;
builder.openObject();
builder.add("name" , VPackValue("blogpost"));
builder.close();
auto request = f::createRequest(f::RestVerb::Post, "/_api/collection"s);
request->addVPack(builder.slice());
auto result = connection->sendRequest(std::move(request));

The next snippet allows us to create a collection. The only differences are that we use another RestVerb and that we add a payload specifying the name of the collection to create. In order to generate the payload, we need velocypack builder. Eventually, we add the builder's slice to the request before sending it over the wire.

In case you know our HTTP API docs you can probably guess what the creation and retrieval of a document will look like. In case you are unsure you should really take a look. You will immediately realize how the documentation relates to the use of this driver.

c++
const std::string h="hello";
VPackBuilder builder;
builder.openObject();
builder.add(h , VPackValue("world"));
builder.close();
auto request = f::createRequest(f::RestVerb::Post, "/_api/document/blogpost"s);
request->addVPack(builder.slice());
auto result = connection->sendRequest(std::move(request));
std::stirng key=result->slices().front().get("_key").copyString();

request = f::createRequest(f::RestVerb::Get, "/_api/document/blogpost/"s + key);
result = connection->sendRequest(std::move(request));
std::cout << result->slices().front().get("hello").copyString();

As already mentioned the above code creates a document in the `blogpost` collection containing the `hello` attribute that maps to the string `world`. We save the key that can be found in the result of the of the first request (insert). We then use that key to receive the document from ArangoDB.

Next, we create a simple AQL query. Please refer to our documentation to get an overview of what you can add to the builder to tweak the query to your needs.

c++
auto request = f::createRequest(f::RestVerb::Post, "/_api/cursor"s);
VPackBuilder builder;
builder.openObject();
builder.add("query", VPackValue("LET x = SLEEP(10) RETURN 1"));
builder.close();
request->addVPack(builder.slice());
auto result = connection->sendRequest(std::move(request));

As promised the code creates an AQL Query using the `/_api/cursor` route. It keeps the server busy for 10 seconds and then returns a number. Imagine you would like to send multiple long-running queries. If you would try to execute 100 queries in a loop where each query takes 10 seconds you would need to wait a bit longer than 15 minutes for the result. As this is too long we provide an asynchronous API. This API allows you to send multiple queries to the server without waiting for a result. Reducing the required time to less than 30 seconds.

c++
f::WaitGroup wg;
std::atomic_uint sum(0);
f::RequestCallback cb = [&sum](f::Error error, std::unique_ptr req, std::unique_ptr res) {
  f::WaitGroupDone done(wg);
  if (error) {
      assert(false); // you will do better error handling :)
  } else {
      sum+=res->slices().front().get("result").at(0).getUInt();
  }
};

VPackBuilder builder;
size_t start = 1;
size_t end = 100;
for(size_t i = start; i <= end; i++){
  auto request = f::createRequest(f::RestVerb::Post, "/_api/cursor"s);
  builder.clear();
  builder.openObject();
  builder.add("query", VPackValue("LET x = SLEEP(10) RETURN "s + std::to_string(i)));
  builder.close();
  request->addVPack(builder.slice());
  wg.add();
  connection->sendRequest(std::move(request),cb);
}
wg.wait_for(c::seconds{30});
assert(sum == (end*(end+1)) / 2);
std::cout << "sum is: " << sum << std::endl;

The above code creates 100 queries. Each returning one element in the range 1..100 without duplicates. We then add all the numbers returned by the server expecting the same result as young Gauss did.

In order to achieve this we create a `WaitGroup` that will ensure that all queries have been processed once the call to `wait_for()` has returned. Next we create a variable `sum` that will hold the calculation result. We then create a callback that will extract the number from the result if everything works fine and will do error handling otherwise. We then enter the loop that we use for the 100 requests. In the loop we create a new request, tell the
`WaitGroup` that we want to wait for one more request (via a call to `add`) and send the request. Finally we wait for all the requests to return and print the result.

As the call to `sendRequest` returns a `MessageId` you do not necessarily need to use `WaitGroups`. You could keep track of those ids revealing more information about which query is still outstanding.

We hope this post gives you enough information to get started with ArangoDB using C++. We are happy to get some feedback, so we can improve the driver - get in touch with us via our contact form or join ArangoDB Community Slack.

Jan Steemann

Jan Steemann

After more than 30 years of playing around with 8 bit computers, assembler and scripting languages, Jan decided to move on to work in database engineering. Jan is now a senior C/C++ developer with the ArangoDB core team, being there from version 0.1. He is mostly working on performance optimization, storage engines and the querying functionality. He also wrote most of AQL (ArangoDB’s query language).

3 Comments

  1. Paul McClure on January 23, 2020 at 4:04 am

    Hello,
    My company is evaluating ArangoDB and I’m looking at the fuerte C++ driver.
    First, thank you for making this available. Most of our application code is in C, but we hope to integrate with this driver.
    I’m doing my fuerte work on ubuntu 18.04, with velocypack, boost 1.71.0 and arangodb 3.6.0-1.
    I’ve built and installed the driver and have been able to do some basic CRUD functions, but not all.
    I’m not finding much documentation, and the available doc does not always seem to be correct.
    I’ve looked thru what I can find, and have looked at the example and test code.
    I still cannot find a way to specify a database to use. I’m only able to work with the _system database.
    After connecting to arangodb, I can see the existing databases with this code;
    auto request = fu::createRequest(fu::RestVerb::Get, “/_api/database”);
    auto result = conn->sendRequest(std::move(request));
    auto slice = result->slices().front();
    std::cout << to_string(slice) <addVPack(builder.slice());
    result = conn->sendRequest(std::move(request));
    slice = result->slices().front();
    std::cout << "code: " << slice.get("code").getInt() <>> an exception is thrown when the line above executes
    request = fu::createRequest(fu::RestVerb::Get, “/_api/document/r1test/” + key);
    result = conn->sendRequest(std::move(request));
    std::cout <slices().front().get(“hello”).copyString();
    Any call to get on my result fails to return the value for the given attribute.

    Using this;
    std::cout << to_string(slice) << std::endl;
    I can see that the result has my data, but I have not been able to extract the data with get().

    If you can shed any light on these problems, that would be great.
    Also – can you give me a sense of the maturity of this driver? Do you know how much it is being used in the linux environment?
    Thanks very much,
    Paul

  2. Paul McClure on January 23, 2020 at 4:09 am

    My post above lost a bunch of lines, so it will not be readable…
    If possible, please email me and I will send the full post.
    Thanks,

  3. William Stroupe on August 19, 2020 at 11:40 pm

    Hello,
    I am considering ArangoDB as the database for a C++ Windows application. My impression is that fuerte is incomplete and not very well documented. Support seems to be lacking. That makes me nervous relying upon a non-official driver to interface my C++ applications with ArangoDB. What options are there for me to consider, since I am totally locked into C++ as the core language for my applications? If indeed fuerte is a risky path at present, what other path can I consider for interfacing C++ programs to ArangoDB? I really like the features ArangoDB offers – but as a C++ developer I feel sort of bewildered.

Leave a Comment





Get the latest tutorials, blog posts and news: