AnnaDB 1.0
Loading...
Searching...
No Matches
connection.hpp
1//
2// Created by felix on 28.01.23.
3//
4
5#ifndef ANNADB_DRIVER_CONNECTION_HPP
6#define ANNADB_DRIVER_CONNECTION_HPP
7
8#include <map>
9#include <regex>
10#include <valarray>
11#include <zmq.hpp>
12#include "TySON.hpp"
13#include "query.hpp"
14
15
16namespace annadb
17{
18 const std::regex pattern ("(,)\\b[\\w-]{2,}+\\b\\|");
19 struct KeyVal
20 {
21 KeyVal(std::string data)
22 {
23 auto separation = data.find_first_of(':');
24 auto start = data[0] == ',' ? 1 : 0;
25 auto end = data[data.size() - 1] == ',' ? 1 : 0;
26 link = data.substr(start, separation - start);
27 value = data.substr(separation + 1, data.size() - 1 - separation - end);
28 }
29
30 std::string link;
31 std::string value;
32 };
33
34 enum class MetaType: unsigned char
35 {
36 none = 'n',
37 insert_meta = 'i',
38 get_meta = 'g',
39 find_meta = 'f',
40 update_meta = 'u'
41 };
42
43 inline std::ostream& operator<< (std::ostream& os, MetaType metaType) noexcept
44 {
45 switch (metaType)
46 {
47 case MetaType::insert_meta:
48 return os << "insert_meta";
49 case MetaType::get_meta:
50 return os << "get_meta";
51 case MetaType::find_meta:
52 return os << "find_meta";
53 case MetaType::update_meta:
54 return os << "update_meta";
55 case MetaType::none:
56 return os << "none";
57 }
58
59 return os << "";
60 }
61
62 const auto metaTypes = std::map<std::string, MetaType>{std::make_pair(":insert_meta", MetaType::insert_meta),
63 std::make_pair(":get_meta", MetaType::get_meta),
64 std::make_pair(":find_meta", MetaType::find_meta),
65 std::make_pair(":update_meta", MetaType::update_meta),};
66
67 class Data
68 {
69 std::string data_;
70
77 std::vector<KeyVal> split_data(std::string_view str_data) noexcept
78 {
79 auto new_data = std::regex_replace(str_data.data(), pattern, "^$&");
80 auto data = utils::split(new_data, '^');
81
82 std::vector<KeyVal> parts {};
83 parts.reserve(data.size());
84 std::transform(data.begin(), data.end(), std::back_inserter(parts), [](auto &val){return KeyVal(val);});
85
86 return parts;
87 }
88
89 public:
90
95 explicit Data(std::string_view data) noexcept : data_(data) {}
96 ~Data() = default;
97
104 template<tyson::TySonType T>
105 requires (T == tyson::TySonType::Objects || T == tyson::TySonType::IDs)
106 std::optional<tyson::TySonCollectionObject> get() noexcept
107 {
108 if (data_.starts_with(std::string("s|data|:objects")) && T == tyson::TySonType::Objects)
109 {
110 auto start_val = data_.find_first_of('{') + 1;
111 auto end_val = data_.find_last_of('}');
112
113 auto tyson_str_data = split_data(data_.substr(start_val, end_val - start_val));
114 tyson::TySonCollectionObject object {tyson_str_data.size(), true};
115
116 for (auto &key_val: tyson_str_data)
117 {
118 object.add(key_val.link, key_val.value);
119 }
120
121 tyson_str_data.clear();
122 tyson_str_data.shrink_to_fit();
123
124 return object;
125 }
126 else if (data_.starts_with(std::string("s|data|:ids")) && T == tyson::TySonType::IDs)
127 {
128 auto start_val = data_.find_first_of('[');
129 auto end_val = data_.find_last_of(']');
130
131 auto tyson_str_data = utils::split(data_.substr(start_val + 1, end_val - start_val - 1), ',');
132 tyson::TySonCollectionObject object {tyson_str_data.size()};
133 for (auto &link_data: tyson_str_data)
134 {
135 object.add(link_data);
136 }
137
138 tyson_str_data.clear();
139 tyson_str_data.shrink_to_fit();
140
141 return object;
142 }
143 return {};
144 }
145 };
146
147 class Meta
148 {
149 tyson::TySonObject data_;
150 /*
151 * Always a TySON map object
152 * s|meta|:find_meta{s|count|:n|5|}
153 */
154 std::string meta_txt_;
155
156 std::string count_;
157 MetaType metaType = MetaType::none;
158
159
167 void parse_data() noexcept
168 {
169 auto pos_map_start = meta_txt_.find('{');
170 auto pos_map_end = meta_txt_.rfind('}') + 1;
171 auto map_str = "m" + meta_txt_.substr(pos_map_start, pos_map_end - pos_map_start);
172 data_ = tyson::TySonObject {map_str};
173 }
174
182 void parse_type() noexcept
183 {
184 auto pos_type_start = meta_txt_.find(':');
185 auto pos_type_end = meta_txt_.find('{');
186 auto meta_type_str = meta_txt_.substr(pos_type_start, pos_type_end - pos_type_start);
187 metaType = metaTypes.at(meta_type_str);
188 }
189
190 friend std::ostream & operator<<(std::ostream &os, const Meta& meta) noexcept
191 {
192 std::string count_val = "0";
193
194 auto count = meta.data_["count"];
195 if (count)
196 {
197 count_val = count.value().value<tyson::TySonType::String>();
198 }
199
200 std::string repr = "{s|count|:n|" + count_val + "|";
201 return os << "s|meta|:" << meta.metaType << repr << "}";
202 }
203
204 public:
205
211 explicit Meta(std::string_view meta_txt) noexcept
212 {
213 meta_txt_ = meta_txt;
214 parse_data();
215 parse_type();
216 }
217
218 ~Meta() = default;
219
225 {
226 return data_;
227 }
228
229 template<typename T>
230 requires std::is_integral_v<T>
231 std::optional<T> rows() noexcept
232 {
233 auto count = data_["count"];
234 if (count)
235 {
236 auto res = count.value().value<T>();
237 return res;
238 }
239 return {};
240 }
241
246 MetaType type() noexcept
247 {
248 return metaType;
249 }
250 };
251
253 {
254 std::string data_;
255 std::string meta_;
256 bool result_ = false;
257
264 void parse_response(std::string_view response) noexcept
265 {
266 /*
267 * The response format from annadb is
268 * `result:<ok|false>[response{s|data|<...>, s|meta|<...>}]`
269 */
270 auto pos_result_end = response.find('[');
271 auto pos_data_begin = response.find("s|data|:");
272
273 // the meta information is always at the end,
274 // so that it is faster to use rfind instead of regular find
275 auto pos_meta_begin = response.rfind("s|meta|:");
276
277 // there is no usage for the closing tags, so we exclude them
278 auto pos_response_end = response.rfind(",}]");
279
280 auto res = response.substr(0, pos_result_end);
281 if (res.find("ok") != std::string::npos)
282 {
283 result_ = true;
284 }
285
286 data_ = response.substr(pos_data_begin, pos_meta_begin - pos_data_begin);
287 meta_ = response.substr(pos_meta_begin, pos_response_end - pos_meta_begin);
288 }
289
290 friend std::ostream & operator<<(std::ostream &os, const Journal& journal) noexcept
291 {
292 std::string result = journal.ok() ? "ok" : "err";
293 auto meta = journal.meta();
294 auto data = "journal.data()"; // journal.data();
295
296 std::string repr = "result:" + result + "[response{";
297 return os << repr << data << meta << ",},];";
298 }
299
300 public:
301
307 explicit Journal(std::string_view response) noexcept
308 {
309 parse_response(response);
310 }
311
312 ~Journal() = default;
313
319 [[nodiscard]] bool ok() const noexcept
320 {
321 return result_;
322 }
323
328 [[nodiscard]] Meta meta() const noexcept
329 {
330 Meta meta{meta_};
331 return meta;
332 }
333
338 [[nodiscard]] Data data() const noexcept
339 {
340 Data data{data_};
341 return data;
342 }
343 };
344
345 class AnnaDB
346 {
347 std::string username_;
348 std::string password_;
349 std::string host_;
350 std::string port_;
351
352 zmq::context_t context {1};
353 zmq::socket_t requester {context, ZMQ_REQ};
354
355 bool zmq_send(std::string_view query) noexcept
356 {
357 zmq::message_t message(query.size());
358 std::memcpy(message.data(), query.data(), query.size());
359
360 auto response = requester.send(message, zmq::send_flags::none);
361
362 if (response)
363 {
364 return true;
365 }
366
367 return false;
368 }
369
370 std::optional<std::string> zmq_receive() noexcept
371 {
372 zmq::message_t message;
373 auto response = requester.recv(message, zmq::recv_flags::none);
374
375 if (response)
376 {
377 auto result = std::string(static_cast<char *>(message.data()), message.size());
378 return result;
379 }
380
381 return {};
382 }
383
384 public:
385
394 AnnaDB(std::string_view username,
395 std::string_view password,
396 std::string_view host,
397 u_short port
398 ) noexcept : username_(username),
399 password_(password),
400 host_(host),
401 port_(std::to_string(port))
402 {
403 };
404
405 ~AnnaDB() = default;
406
410 void connect() noexcept
411 {
412 requester.connect("tcp://" + host_ + ":" + port_);
413 }
414
418 void close() noexcept
419 {
420 requester.close();
421 }
422
429 [[nodiscard]] std::optional<Journal> send(std::string_view query) noexcept
430 {
431 auto result = zmq_send(query);
432
433 if (result)
434 {
435 auto response = zmq_receive();
436 if (response)
437 {
438 return Journal(*response);
439 }
440 }
441 return {};
442 }
443
450 [[nodiscard]] std::optional<Journal> send(annadb::Query::Query &query) noexcept
451 {
452 std::stringstream sstream;
453 sstream << query;
454
455 auto query_str = sstream.str();
456 auto result = zmq_send(query_str);
457
458 if (result)
459 {
460 auto response = zmq_receive();
461 if (response)
462 {
463 return Journal(*response);
464 }
465 }
466 return {};
467 }
468 };
469}
470
471#endif //ANNADB_DRIVER_CONNECTION_HPP
Definition: connection.hpp:346
std::optional< Journal > send(std::string_view query) noexcept
Definition: connection.hpp:429
void connect() noexcept
Definition: connection.hpp:410
std::optional< Journal > send(annadb::Query::Query &query) noexcept
Definition: connection.hpp:450
AnnaDB(std::string_view username, std::string_view password, std::string_view host, u_short port) noexcept
Definition: connection.hpp:394
void close() noexcept
Definition: connection.hpp:418
Definition: connection.hpp:68
std::optional< tyson::TySonCollectionObject > get() noexcept
Definition: connection.hpp:106
Data(std::string_view data) noexcept
Definition: connection.hpp:95
Definition: connection.hpp:253
Journal(std::string_view response) noexcept
Definition: connection.hpp:307
Data data() const noexcept
Definition: connection.hpp:338
bool ok() const noexcept
Definition: connection.hpp:319
Meta meta() const noexcept
Definition: connection.hpp:328
Definition: connection.hpp:148
MetaType type() noexcept
Definition: connection.hpp:246
tyson::TySonObject data() noexcept
Definition: connection.hpp:224
Meta(std::string_view meta_txt) noexcept
Definition: connection.hpp:211
Definition: query.hpp:775
Definition: TySON.hpp:702
void add(const std::string &object)
Definition: TySON.hpp:727
Definition: TySON.hpp:86
std::string value() const noexcept
Definition: TySON.hpp:588
Definition: connection.hpp:20