1 /// Provides a node wrapper. 2 module memgraph.node; 3 4 import memgraph.mgclient, memgraph.detail, memgraph.map; 5 import memgraph.value, memgraph.enums; 6 7 import std.conv; 8 9 /// Represents a node from a labeled property graph. 10 /// 11 /// Consists of a unique identifier (withing the scope of its origin graph), a 12 /// list of labels and a map of properties. A node owns its labels and 13 /// properties. 14 /// 15 /// Maximum possible number of labels allowed by Bolt protocol is `uint.max`. 16 struct Node { 17 /// View of the node's labels. 18 struct Labels { 19 /// Returns the number of labels of node `node`. 20 size_t size() const { 21 assert(node_ != null); 22 return mg_node_label_count(node_); 23 } 24 25 /// Return node's label at the `index` position. 26 string opIndex(int index) const { 27 assert(node_ != null); 28 return Detail.convertString(mg_node_label_at(node_, index)); 29 } 30 31 /// Checks if the range is empty. 32 bool empty() const { 33 return idx_ >= size(); 34 } 35 36 /// Returns the next element in the range. 37 auto front() const { 38 assert(idx_ < size()); 39 return Detail.convertString(mg_node_label_at(node_, idx_)); 40 } 41 42 /// Move to the next element in the range. 43 void popFront() { 44 idx_++; 45 } 46 47 bool opEquals(const string[] labels) const { 48 if (labels.length != size()) 49 return false; 50 foreach (idx, label; labels) { 51 if (label != Detail.convertString(mg_node_label_at(node_, to!uint(idx)))) 52 return false; 53 } 54 return true; 55 } 56 57 private: 58 this(const mg_node *node) { 59 assert(node != null); 60 node_ = node; 61 } 62 const mg_node *node_; 63 uint idx_; 64 } 65 66 /// Return a printable string representation of this node. 67 const (string) toString() const { 68 import std.range : join; 69 return labels.join(":") ~ " " ~ to!string(properties()); 70 } 71 72 this(this) { 73 if (ptr_) 74 ptr_ = mg_node_copy(ptr_); 75 } 76 77 /// Create a copy of the given `node`. 78 this(inout ref Node other) { 79 this(mg_node_copy(other.ptr_)); 80 } 81 82 /// Create a node from a Value. 83 this(inout ref Value value) { 84 assert(value.type == Type.Node); 85 this(mg_node_copy(mg_value_node(value.ptr))); 86 } 87 88 /// Returns the ID of this node. 89 long id() const { 90 assert(ptr_ != null); 91 return mg_node_id(ptr_); 92 } 93 94 /// Returns the labels belonging to this node. 95 auto labels() inout { return Labels(ptr_); } 96 97 /// Returns the property map belonging to this node. 98 auto properties() inout { return Map(mg_node_properties(ptr_)); } 99 100 /// Comparison operator. 101 bool opEquals(const ref Node other) const { 102 return Detail.areNodesEqual(ptr_, other.ptr_); 103 } 104 105 ~this() { 106 if (ptr_) 107 mg_node_destroy(ptr_); 108 } 109 110 package: 111 /// Create a Node using the given `mg_node`. 112 this(mg_node *p) { 113 assert(p != null); 114 ptr_ = p; 115 } 116 117 /// Create a Node from a copy of the given `mg_node`. 118 this(const mg_node *p) { 119 assert(p != null); 120 this(mg_node_copy(p)); 121 } 122 123 const (mg_node *) ptr() const { return ptr_; } 124 125 private: 126 /// Pointer to `mg_node` instance. 127 mg_node *ptr_; 128 } 129 130 unittest { 131 import testutils : startContainer; 132 startContainer(); 133 } 134 135 unittest { 136 import testutils; 137 import memgraph; 138 import std.algorithm, std.conv, std.range; 139 140 auto client = connectContainer(); 141 assert(client); 142 143 createTestIndex(client); 144 145 deleteTestData(client); 146 147 createTestData(client); 148 149 auto result = client.execute("MATCH (n) RETURN n;"); 150 assert(result, client.error); 151 assert(!result.empty()); 152 auto value = result.front; 153 assert(result.count == 5); 154 155 assert(value[0].type() == Type.Node); 156 157 auto node = to!Node(value[0]); 158 159 auto labels = node.labels(); 160 161 assert(node.id() >= 0); 162 163 immutable auto expectedLabels = [ "Person", "Entrepreneur" ]; 164 165 assert(labels.size() == 2); 166 assert(labels[0] == expectedLabels[0]); 167 assert(labels[1] == expectedLabels[1]); 168 169 assert([] != labels); 170 assert([ "Nope", "x" ] != labels); 171 assert(expectedLabels == labels); 172 assert(expectedLabels.join(":") == labels.join(":")); 173 174 auto other = Node(node); 175 assert(other == node); 176 177 const auto props = node.properties(); 178 assert(props.length == 5); 179 assert(props["id"] == 0); 180 assert(props["age"] == 40); 181 assert(props["name"] == "John"); 182 assert(props["isStudent"] == false); 183 assert(props["score"] == 5.0); 184 185 assert(to!string(node) == labels.join(":") ~ " " ~ to!string(props)); 186 187 const otherProps = props; 188 assert(otherProps == props); 189 190 // this is a package internal method 191 assert(node.ptr != null); 192 193 const v = Value(node); 194 assert(v.type == Type.Node); 195 assert(v == node); 196 assert(node == v); 197 198 const v2 = Value(node); 199 assert(v == v2); 200 }