Add path pop to mgp API (#1249)
This commit is contained in:
parent
b719f0744f
commit
404cdf05d3
@ -477,6 +477,8 @@ inline void path_destroy(mgp_path *path) { mgp_path_destroy(path); }
|
||||
|
||||
inline void path_expand(mgp_path *path, mgp_edge *edge) { MgInvokeVoid(mgp_path_expand, path, edge); }
|
||||
|
||||
inline void path_pop(mgp_path *path) { MgInvokeVoid(mgp_path_pop, path); }
|
||||
|
||||
inline size_t path_size(mgp_path *path) { return MgInvoke<size_t>(mgp_path_size, path); }
|
||||
|
||||
inline mgp_vertex *path_vertex_at(mgp_path *path, size_t index) {
|
||||
|
@ -333,6 +333,13 @@ class Path:
|
||||
self._vertices.append(edge.end_id)
|
||||
self._edges.append((edge.start_id, edge.end_id, edge.id))
|
||||
|
||||
def pop(self):
|
||||
if not self._edges:
|
||||
raise IndexError("Path contains no relationships.")
|
||||
|
||||
self._vertices.pop()
|
||||
self._edges.pop()
|
||||
|
||||
def vertex_at(self, index: int) -> Vertex:
|
||||
return Vertex(self._vertices[index], self._graph)
|
||||
|
||||
|
@ -543,6 +543,10 @@ void mgp_path_destroy(struct mgp_path *path);
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for path extension.
|
||||
enum mgp_error mgp_path_expand(struct mgp_path *path, struct mgp_edge *edge);
|
||||
|
||||
/// Remove the last node and the last relationship from the path.
|
||||
/// Return mgp_error::MGP_ERROR_OUT_OF_RANGE if the path contains no relationships.
|
||||
enum mgp_error mgp_path_pop(struct mgp_path *path);
|
||||
|
||||
/// Get the number of edges in a mgp_path.
|
||||
/// Current implementation always returns without errors.
|
||||
enum mgp_error mgp_path_size(struct mgp_path *path, size_t *result);
|
||||
|
@ -886,6 +886,8 @@ class Path {
|
||||
|
||||
/// @brief Adds a relationship continuing from the last node on the path.
|
||||
void Expand(const Relationship &relationship);
|
||||
/// @brief Removes the last node and the last relationship from the path.
|
||||
void Pop();
|
||||
|
||||
/// @exception std::runtime_error Path contains element(s) with unknown value.
|
||||
bool operator==(const Path &other) const;
|
||||
@ -2995,6 +2997,8 @@ inline Relationship Path::GetRelationshipAt(size_t index) const {
|
||||
|
||||
inline void Path::Expand(const Relationship &relationship) { mgp::path_expand(ptr_, relationship.ptr_); }
|
||||
|
||||
inline void Path::Pop() { mgp::path_pop(ptr_); }
|
||||
|
||||
inline bool Path::operator==(const Path &other) const { return util::PathsEqual(ptr_, other.ptr_); }
|
||||
|
||||
inline bool Path::operator!=(const Path &other) const { return !(*this == other); }
|
||||
|
@ -983,6 +983,24 @@ class Path:
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
def pop(self):
|
||||
"""
|
||||
Remove the last node and the last relationship from the path.
|
||||
|
||||
Raises:
|
||||
InvalidContextError: If using an invalid `Path` instance
|
||||
OutOfRangeError: If the path contains no relationships.
|
||||
|
||||
Examples:
|
||||
```path.pop()```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
self._path.pop()
|
||||
# Invalidate our cached tuples
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
@property
|
||||
def vertices(self) -> typing.Tuple[Vertex, ...]:
|
||||
"""
|
||||
@ -1023,6 +1041,10 @@ class Path:
|
||||
self._edges = tuple(Edge(self._path.edge_at(i)) for i in range(num_edges))
|
||||
return self._edges
|
||||
|
||||
@property
|
||||
def length(self) -> int:
|
||||
return self._path.size()
|
||||
|
||||
|
||||
class Record:
|
||||
"""Represents a record of resulting field values."""
|
||||
|
@ -929,6 +929,25 @@ class Path:
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
def pop(self):
|
||||
"""
|
||||
Remove the last node and the last relationship from the path.
|
||||
|
||||
Raises:
|
||||
InvalidContextError: If using an invalid `Path` instance
|
||||
OutOfRangeError: If the path contains no relationships.
|
||||
|
||||
Examples:
|
||||
```path.pop()```
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise InvalidContextError()
|
||||
self._path.pop()
|
||||
|
||||
# Invalidate cached tuples
|
||||
self._vertices = None
|
||||
self._edges = None
|
||||
|
||||
@property
|
||||
def vertices(self) -> typing.Tuple[Vertex, ...]:
|
||||
"""
|
||||
|
@ -1180,6 +1180,17 @@ mgp_error mgp_path_expand(mgp_path *path, mgp_edge *edge) {
|
||||
});
|
||||
}
|
||||
|
||||
mgp_error mgp_path_pop(struct mgp_path *path) {
|
||||
return WrapExceptions([path] {
|
||||
if (path->edges.empty()) {
|
||||
throw std::out_of_range("Path contains no relationships.");
|
||||
}
|
||||
|
||||
path->vertices.pop_back();
|
||||
path->edges.pop_back();
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
size_t MgpPathSize(const mgp_path &path) noexcept { return path.edges.size(); }
|
||||
} // namespace
|
||||
|
@ -2204,6 +2204,17 @@ PyObject *PyPathExpand(PyPath *self, PyObject *edge) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *PyPathPop(PyPath *self) {
|
||||
MG_ASSERT(self->path);
|
||||
MG_ASSERT(self->py_graph);
|
||||
MG_ASSERT(self->py_graph->graph);
|
||||
|
||||
if (RaiseExceptionFromErrorCode(mgp_path_pop(self->path))) {
|
||||
return nullptr;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *PyPathSize(PyPath *self, PyObject *Py_UNUSED(ignored)) {
|
||||
MG_ASSERT(self->path);
|
||||
MG_ASSERT(self->py_graph);
|
||||
@ -2251,6 +2262,8 @@ static PyMethodDef PyPathMethods[] = {
|
||||
"Create a path with a starting vertex."},
|
||||
{"expand", reinterpret_cast<PyCFunction>(PyPathExpand), METH_O,
|
||||
"Append an edge continuing from the last vertex on the path."},
|
||||
{"pop", reinterpret_cast<PyCFunction>(PyPathPop), METH_NOARGS,
|
||||
"Remove the last node and the last relationship from the path."},
|
||||
{"size", reinterpret_cast<PyCFunction>(PyPathSize), METH_NOARGS, "Return the number of edges in a mgp_path."},
|
||||
{"vertex_at", reinterpret_cast<PyCFunction>(PyPathVertexAt), METH_VARARGS,
|
||||
"Return the vertex from a path at given index."},
|
||||
|
@ -33,6 +33,16 @@ def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||
(2, 1),
|
||||
)
|
||||
|
||||
path.pop()
|
||||
mock_path.pop()
|
||||
results["pop"] = test_utils.all_equal(
|
||||
(len(path.vertices), len(path.edges)),
|
||||
(len(mock_path.vertices), len(mock_path.edges)),
|
||||
(1, 0),
|
||||
)
|
||||
path.expand(edge_to_add)
|
||||
mock_path.expand(mock_edge_to_add)
|
||||
|
||||
NEXT_ID = 1
|
||||
results["vertices"] = test_utils.all_equal(
|
||||
all(isinstance(vertex, mgp.Vertex) for vertex in path.vertices),
|
||||
|
@ -125,6 +125,7 @@ def test_path():
|
||||
"__copy__": True,
|
||||
"is_valid": True,
|
||||
"expand": True,
|
||||
"pop": True,
|
||||
"vertices": True,
|
||||
"edges": True,
|
||||
}
|
||||
|
@ -346,6 +346,9 @@ TYPED_TEST(CppApiTestFixture, TestPath) {
|
||||
auto value_x = mgp::Value(path);
|
||||
// Use Value move constructor
|
||||
auto value_y = mgp::Value(mgp::Path(node_0));
|
||||
|
||||
path.Pop();
|
||||
ASSERT_EQ(path.Length(), 0);
|
||||
}
|
||||
|
||||
TYPED_TEST(CppApiTestFixture, TestDate) {
|
||||
|
Loading…
Reference in New Issue
Block a user