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 }