Tash (ট্যাঁশ) an Open Source C++ library for ArangoDB and AQL

I am working on Tash which is an Open Source C++ library for ArangoDB Database which includes APIs for HTTP based document access and a query builder for AQL (Arango Query Language). These are a few example usages.

1
2
3
4
5
6
7
8
std::string name = "Hijibijbij";
tash::shell shell("school");
shell << select("s").in("students")
/ filter((clause("s.name") == name) && (clause("s.fathers_name") == name))
/ sort().asc("s._key")
/ yield("s");
nlohmann::json result;
shell >> result;

The above generates an equivalent AQL and execute it over the arangodb connection

tashgoru

1
2
3
4
FOR s IN students
FILTER s.name == "Hijibijbij" AND s.fathers_name == "Hijibijbij"
SORT s._key ASC
RETURN s

Along with AQL generation it also performs normal HTTP API based document access.

1
2
3
4
5
6
7
8
9
10
11
tash::vertex students(school, "students");
if(students.exists() == boost::beast::http::status::not_found){
students.create();
}
nlohmann::json document = {
{"name", "Hijibijbij"},
{"fathers_name", "Hijibijbij"},
{"uncles_name", "Hijibijbij"}
};
boost::beast::http::status status = students.add(document);
nlohmann::json hijibijbij = students.by_key(document["_key"].get<std::string>());

Unlike Fuerte it does not require velocypack or node. Instead it uses nlohmann::json and Boost.Beast libraries

Building

prerequisites

  • C++ compiler
  • CMake
  • boost library
  • nlohmann::json [OPTIONAL] (tash ships with a single file version of nlohmann::json)

compiling

1
2
3
4
5
git clone https://gitlab.com/neel.basu/tash.git
mkdir build
cd build
cmake ..
make

Basic Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <tash/arango.h>
#include <boost/format.hpp>
#include <boost/beast/http/status.hpp>

int main(){
tash::shell school("school"); // shell("school", "localhost", 8529, "root", "root")
if(school.exists() == boost::beast::http::status::not_found){
school.create();
}
tash::vertex students(school, "students");
if(students.exists() == boost::beast::http::status::not_found){
students.create();
}
nlohmann::json document = {
{"name", "Hijibijbij"},
{"fathers_name", "Hijibijbij"},
{"uncles_name", "Hijibijbij"}
};
boost::beast::http::status status = students.add(document);
if(status == boost::beast::http::status::accepted){
std::cout << boost::format("document created with key %1%") % document["_key"] << std::endl;
}else{
std::cout << "Failed to create document with error " << status << std::endl;
}
nlohmann::json hijibijbij = students.by_key(document["_key"].get<std::string>());
std::cout << "retrieved document " << std::endl;
std::cout << hijibijbij << std::endl;
return 0;
}

AQL (Arango Query Language) Builder

Retrieve / Filter / Sort

1
2
3
4
5
6
7
tash::shell shell("school"); // shell("school", "localhost", 8529, "root", "root")
shell << select("s").in("students") // use select instead of FOR because for is a C++ keyword
/ filter((clause("s.name") == name) && (clause("s.fathers_name") == name)) // using std::string puts quotes around the value
/ sort().asc("s._key")
/ yield("s"); // use yield instead of RETURN because return is a C++ keyword
nlohmann::json result;
shell >> result;
1
2
3
4
FOR s IN students
FILTER s.name == "Hijibijbij" AND s.fathers_name == "Hijibijbij"
SORT s._key ASC
RETURN s
1
2
3
4
  select("s").in("students") 
/ filter((clause("s.name") == "s.fathers_name")) // using C string doesn't put quotes around the value
/ sort().asc("s._key")
/ yield("s");
1
2
3
4
FOR s IN students
FILTER s.name == s.fathers_name
SORT s._key ASC
RETURN s

insert

insert single row

1
2
3
4
insert(nlohmann::json{
{"name", "tash"},
{"fathers_name", "tash"}
}).in("students");
1
INSERT {"fathers_name":"tash","name":"tash"} INTO students

insert multiple rows

1
2
3
4
select("u").in({
{"name", "tash"},
{"fathers_name", "tash"}
}) / insert("u").in("students")
1
2
FOR u IN {"fathers_name":"tash","name":"tash"}
INSERT u INTO students

generate rows

  • in nlohmann::json string values are always quoted
  • tash::assign generates non-nested key value pairs (non-nested json)
  • C style strings are unquoted, std::string is quoted
1
2
3
4
5
6
  select("i").in(1, 10) 
/ insert(
assign("name", "CONCAT('tash', i)")
.assign("gender", "(i % 2 == 0 ? 'f' : 'm')")
.assign("fathers_name", std::string("tash"))
).in("users")
1
2
3
4
5
6
FOR i IN 1..10
INSERT {
name: CONCAT('test', i),
gender: (i % 2 == 0 ? 'f' : 'm'),
fathers_name: "tash"
} INTO users

update

1
2
3
4
5
update(nlohmann::json{
{"_key", 1234}
}).with({
{"uncles_name", "tash"}
}).in("students")
1
UPDATE {"_key":1234} WITH {"uncles_name":"tash"} IN students

let

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  (let("date") = "DATE_NOW()")
/ select("user").in("users")
/ filter(clause("user.isImportantUser") == "null")
/ (let("numberOfLogins") =
select("login").in("logins")
/ filter(clause("login.user") == "user._key")
/ collect().with("COUNT").in("numLogins")
/ yield("numLogins")
)
/ filter(clause("numberOfLogins") > 50)
/ update("user").with(
assign("isImportantUser", 1)
.assign("dateBecameImportant", "date")
.assign("uncles_name", std::string("tash"))
).in("users")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LET date = DATE_NOW()
FOR user IN users
FILTER user.isImportantUser == null
LET numberOfLogins = (
FOR login IN logins
FILTER login.user == user._key
COLLECT WITH COUNT INTO numLogins
RETURN numLogins
)
FILTER numberOfLogins > 50
UPDATE user WITH {
isImportantUser: 1,
dateBecameImportant: date,
uncles_name: "tash"
} IN users

replace

1
2
3
4
5
6
replace(nlohmann::json{
{"_key", 1234}
}).with({
{"name", "tash"},
{"uncles_name", "tash"}
}).in("students")
1
2
3
4
5
REPLACE {"_key":1234} 
WITH {
"name":"tash",
"uncles_name":"tash"
} IN students

remove

1
erase(assign("_key", "1")).in("students")
1
REMOVE {_key: 1} IN students

upsert

1
2
3
4
5
6
7
8
9
upsert(nlohmann::json{
{"name", "tokai"}
}).insert({
{"name", "tokai"},
{"fathers_name", "tokai"}
}).update({
{"name", "tokai"},
{"fathers_name", "tokai"}
}).in("students")
1
2
3
4
UPSERT {"name":"tokai"} 
INSERT {"fathers_name":"tokai","name":"tokai"}
UPDATE {"fathers_name":"tokai","name":"tokai"}
IN students

create graph

1
2
3
4
5
auto graph = ddl::graph_definition("corpus")
.add_edge_definition(ddl::edge_definition("StructuralE").add_from("Word").add_to("Word").add_to("Sentence"))
.add_edge_definition(ddl::edge_definition("WNeighbourhoodE").add_from("Word").add_to("Word"))
.add_edge_definition(ddl::edge_definition("InstanceE").add_from("Word").add_to("Vocabulary"));
query("_api/gharial", graph.json().dump());

create aql function

1
2
3
4
5
6
7
8
9
10
11
std::string function_str = R"(
function(p1, p2){
// function body
}
)";
auto fnc = nlohmann::json({
{"name", "NS::FNAME"},
{"code", function_str},
{"isDeterministic", true}
});
query("_api/aqlfunction", fnc.dump());

Tash (ট্যাঁশ) an Open Source C++ library for ArangoDB and AQL

http://neelex.com/2019/09/19/tash/

Author

Neel Basu (Sunanda Bose)

Posted on

2019-09-19

Updated on

2021-01-14

Licensed under

Comments