1 /// Provides single result row or query execution summary. 2 module memgraph.result; 3 4 import std.string, std.conv; 5 6 import memgraph.mgclient, memgraph.value, memgraph.map, memgraph.detail; 7 8 /// An object encapsulating a single result row or query execution summary. It's 9 /// lifetime is limited by lifetime of parent `mg_session`. Also, invoking 10 /// `mg_session_pull` ends the lifetime of previously returned `mg_result`. 11 /// Implements an `InputRange`. 12 struct Result { 13 /// Returns names of columns output by the current query execution. 14 auto columns() inout { 15 assert(ptr_ != null); 16 assert(*ptr_ != null); 17 const (mg_list) *list = mg_result_columns(*ptr_); 18 const size_t list_length = mg_list_size(list); 19 string[] cols; 20 cols.length = list_length; 21 for (uint i = 0; i < list_length; ++i) 22 cols[i] = Detail.convertString(mg_value_string(mg_list_at(list, i))); 23 return cols; 24 } 25 26 /// Returns query execution summary as a key/value `Map`. 27 auto summary() inout { 28 assert(ptr_ != null); 29 assert(*ptr_ != null); 30 return Map(mg_result_summary(*ptr_)); 31 } 32 33 /// Check if the `Result` is empty. 34 /// Return: true if empty, false if there are more rows to be fetched. 35 /// Note: part of `InputRange` interface 36 @property bool empty() { 37 if (values_.length == 0) { 38 assert(ptr_ != null); 39 immutable status = mg_session_fetch(session_, ptr_); 40 if (status != 1) 41 return true; 42 const (mg_list) *list = mg_result_row(*ptr_); 43 const size_t list_length = mg_list_size(list); 44 Value[] data; 45 data.length = list_length; 46 for (uint i = 0; i < list_length; ++i) 47 data[i] = Value(mg_list_at(list, i)); 48 values_ ~= data; 49 } 50 return values_.length == 0; 51 } 52 /// Returns the front element of the range. 53 /// Note: part of `InputRange` interface 54 auto front() { 55 assert(values_.length > 0); 56 return values_[0]; 57 } 58 /// Pops the first element from the range, shortening the range by one element. 59 /// Note: part of `InputRange` interface 60 @property void popFront() { 61 values_ = values_[1..$]; 62 } 63 64 ~this() { 65 import core.stdc.stdlib : free; 66 refs_--; 67 if (ptr_ && refs_ == 0) 68 free(ptr_); 69 } 70 71 this(this) { 72 refs_++; 73 } 74 75 auto opCast(T : bool)() const { 76 return ptr_ != null; 77 } 78 79 package: 80 /// Initial construction of a `Result` from the given `mg_session` pointer. 81 /// Ranges in D first perform a copy of the range object on which they will 82 /// operate. This means that the original `Result` instance could not be 83 /// used to e.g. query the summary since it does not have the last `mg_result`. 84 /// Allocate a reference counted `mg_result` pointer to be shared with all 85 /// future range copies. 86 this(mg_session *session) { 87 assert(session != null); 88 import core.stdc.stdlib : malloc; 89 session_ = session; 90 ptr_ = cast(mg_result **)malloc((mg_result *).sizeof); 91 } 92 93 private: 94 /// Pointer to private `mg_session` instance. 95 mg_session *session_; 96 /// Pointer to private `mg_result` instance. 97 mg_result **ptr_; 98 /// Temporary value store. 99 Value[][] values_; 100 /// Reference count. 101 uint refs_ = 1; 102 } 103 104 unittest { 105 import std.range.primitives : isInputRange; 106 assert(isInputRange!Result); 107 }