1 /// Provides a wrapper around a `Path`. 2 module memgraph.path; 3 4 import memgraph.mgclient, memgraph.detail, memgraph.map, memgraph.value, memgraph.node, memgraph.unboundrelationship; 5 import memgraph.enums; 6 7 /// Represents a sequence of alternating nodes and relationships 8 /// corresponding to a walk in a labeled property graph. 9 /// 10 /// A path of length L consists of L + 1 nodes indexed from 0 to L, and L 11 /// unbound relationships, indexed from 0 to L - 1. Each relationship has a 12 /// direction. A relationship is said to be reversed if it was traversed in the 13 /// direction opposite of the direction of the underlying relationship in the 14 /// data graph. 15 struct Path { 16 /// Create a copy of `other` path. 17 this(inout ref Path other) { 18 this(mg_path_copy(other.ptr)); 19 } 20 21 /// Create a path from a Value. 22 this(inout ref Value value) { 23 assert(value.type == Type.Path); 24 this(mg_path_copy(mg_value_path(value.ptr))); 25 } 26 27 /// Return a printable string representation of this path. 28 const (string) toString() const { 29 return "TODO"; 30 } 31 32 /// Compares this path with `other`. 33 /// Return: true if same, false otherwise. 34 bool opEquals(const ref Path other) const { 35 return Detail.arePathsEqual(ptr_, other.ptr); 36 } 37 38 /// Returns the path length. 39 /// Length of the path is number of edges. 40 const (long) length() const { 41 assert(ptr_ != null); 42 return mg_path_length(ptr_); 43 } 44 45 /// Returns the vertex at the given `index`. 46 /// `index` should be less than or equal to length of the path. 47 const (Node) getNodeAt(uint index) const { 48 assert(ptr_ != null); 49 const auto vertex_ptr = mg_path_node_at(ptr_, index); 50 assert(vertex_ptr != null); 51 return Node(vertex_ptr); 52 } 53 54 /// Returns the edge at the given `index`. 55 /// `index` should be less than length of the path. 56 const (UnboundRelationship) getRelationshipAt(uint index) const { 57 assert(ptr_ != null); 58 auto edge_ptr = mg_path_relationship_at(ptr_, index); 59 assert(edge_ptr != null); 60 return UnboundRelationship(edge_ptr); 61 } 62 63 /// Returns the orientation of the edge at the given `index`. 64 /// `index` should be less than length of the path. 65 /// Return: True if the edge is reversed, false otherwise. 66 bool isReversedRelationshipAt(uint index) const { 67 assert(ptr_ != null); 68 auto is_reversed = mg_path_relationship_reversed_at(ptr_, index); 69 assert(is_reversed != -1); 70 return is_reversed == 1; 71 } 72 73 this(this) { 74 if (ptr_) 75 ptr_ = mg_path_copy(ptr_); 76 } 77 78 ~this() { 79 if (ptr_) 80 mg_path_destroy(ptr_); 81 } 82 83 package: 84 /// Create a Path using the given `mg_path`. 85 this(mg_path *ptr) { 86 assert(ptr != null); 87 ptr_ = ptr; 88 } 89 90 /// Create a Path from a copy of the given `mg_path`. 91 this(const mg_path *ptr) { 92 assert(ptr != null); 93 this(mg_path_copy(ptr)); 94 } 95 96 const (mg_path *) ptr() const { return ptr_; } 97 98 private: 99 mg_path *ptr_; 100 } 101 102 unittest { 103 import testutils : connectContainer, createTestData, deleteTestData; 104 import memgraph : Client, Type, Value, Node, Relationship, List; 105 import std.conv : to; 106 107 auto client = connectContainer(); 108 assert(client); 109 110 deleteTestData(client); 111 112 createTestData(client); 113 114 // TODO: fix unit test, ie. use unbound relationship 115 auto res = client.execute( 116 "MATCH p = ()-[*]-() RETURN p"); 117 assert(res, client.error); 118 foreach (c; res) { 119 assert(c[0].type == Type.Path); 120 auto p = to!Path(c[0]); 121 122 auto p2 = p; 123 auto p3 = c[0]; 124 assert(p2 == p3); 125 auto p4 = Path(p); 126 assert(p4 == p); 127 128 auto p5 = Value(p); 129 130 assert(p3 == p5); 131 132 foreach (i; 0..p.length) { 133 assert(p2.isReversedRelationshipAt(to!uint(i)) == 134 p4.isReversedRelationshipAt(to!uint(i))); 135 } 136 137 foreach (i; 0..p.length) { 138 auto n = p.getNodeAt(to!uint(i)); 139 auto r = p.getRelationshipAt(to!uint(i)); 140 141 auto n2 = n; 142 const n3 = Value(n); 143 assert(n2 == n3); 144 145 const r2 = r; 146 auto r3 = Value(r); 147 assert(r2 == r3); 148 const r4 = UnboundRelationship(r); 149 assert(r4 == r); 150 const r5 = UnboundRelationship(r3); 151 assert(r5 == r); 152 153 assert(to!string(r) == "IS_MANAGER"); 154 155 assert(r5.id == r.id); 156 assert(r5.type == r.type); 157 assert(r5.properties == r.properties); 158 159 auto r6 = Value(r); 160 assert(r3 == r6); 161 assert(to!string(r6) == to!string(r)); 162 163 } 164 assert(p.ptr != null); 165 166 auto v = Value(p); 167 assert(v == p); 168 assert(to!string(v) == to!string(p)); 169 170 const p6 = p; 171 const p7 = Path(p6); 172 assert(p7 == p); 173 } 174 }