diff --git a/include/mg_procedure.h b/include/mg_procedure.h index 312693ba0..63ce6fc88 100644 --- a/include/mg_procedure.h +++ b/include/mg_procedure.h @@ -539,6 +539,16 @@ const struct mgp_edge *mgp_edges_iterator_get( /// NULL is returned if the end of the iteration has been reached. const struct mgp_edge *mgp_edges_iterator_next(struct mgp_edges_iterator *it); +/// ID of an edge; valid during a single query execution. +struct mgp_edge_id { + int64_t as_int; +}; + +/// Get the ID of given edge. +/// The ID is only valid for a single query execution, you should never store it +/// globally in a query module. +struct mgp_edge_id mgp_edge_get_id(const struct mgp_edge *e); + /// Copy a mgp_edge. /// Returned pointer must be freed with mgp_edge_destroy. /// NULL is returned if unable to allocate a mgp_edge. diff --git a/include/mgp.py b/include/mgp.py index 467164a3f..e411d6f5f 100644 --- a/include/mgp.py +++ b/include/mgp.py @@ -165,8 +165,11 @@ class EdgeType: return self.name == other return NotImplemented - def __hash__(self) -> int: - return hash(self.name) + +if sys.version_info >= (3, 5, 2): + EdgeId = typing.NewType('EdgeId', int) +else: + EdgeId = int class Edge: @@ -194,6 +197,13 @@ class Edge: '''Return True if `self` is in valid context and may be used.''' return self._edge.is_valid() + @property + def id(self) -> EdgeId: + '''Raise InvalidContextError.''' + if not self.is_valid(): + raise InvalidContextError() + return self._edge.get_id() + @property def type(self) -> EdgeType: '''Raise InvalidContextError.''' @@ -229,7 +239,7 @@ class Edge: return self._edge == other._edge def __hash__(self) -> int: - return hash((self.from_vertex, self.to_vertex, self.type)) + return hash(self.id) if sys.version_info >= (3, 5, 2): diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp index 70224a228..7f036e229 100644 --- a/src/query/procedure/mg_procedure_impl.cpp +++ b/src/query/procedure/mg_procedure_impl.cpp @@ -1141,6 +1141,10 @@ const mgp_edge *mgp_edges_iterator_next(mgp_edges_iterator *it) { } } +mgp_edge_id mgp_edge_get_id(const mgp_edge *e) { + return mgp_edge_id{.as_int = e->impl.Gid().AsInt()}; +} + mgp_edge *mgp_edge_copy(const mgp_edge *v, mgp_memory *memory) { return new_mgp_object<mgp_edge>(memory, v->impl, v->from.graph); } diff --git a/src/query/procedure/py_module.cpp b/src/query/procedure/py_module.cpp index c139fbb35..0f843fca1 100644 --- a/src/query/procedure/py_module.cpp +++ b/src/query/procedure/py_module.cpp @@ -835,6 +835,14 @@ PyObject *PyEdgeIsValid(PyEdge *self, PyObject *Py_UNUSED(ignored)) { return PyBool_FromLong(self->py_graph && self->py_graph->graph); } +PyObject *PyEdgeGetId(PyEdge *self, PyObject *Py_UNUSED(ignored)) { + CHECK(self); + CHECK(self->edge); + CHECK(self->py_graph); + CHECK(self->py_graph->graph); + return PyLong_FromLongLong(mgp_edge_get_id(self->edge).as_int); +} + PyObject *PyEdgeIterProperties(PyEdge *self, PyObject *Py_UNUSED(ignored)) { CHECK(self); CHECK(self->edge); @@ -884,6 +892,8 @@ static PyMethodDef PyEdgeMethods[] = { METH_NOARGS, "__reduce__ is not supported."}, {"is_valid", reinterpret_cast<PyCFunction>(PyEdgeIsValid), METH_NOARGS, "Return True if Edge is in valid context and may be used."}, + {"get_id", reinterpret_cast<PyCFunction>(PyEdgeGetId), METH_NOARGS, + "Return edge id."}, {"get_type_name", reinterpret_cast<PyCFunction>(PyEdgeGetTypeName), METH_NOARGS, "Return the edge's type name."}, {"from_vertex", reinterpret_cast<PyCFunction>(PyEdgeFromVertex),