diff --git a/goblin/mapper.py b/goblin/mapper.py index 0255376635c3421daa1a624543104f4951bc8f46..b68e6d4323500b420ddf32392f3a79c4e8f7862e 100644 --- a/goblin/mapper.py +++ b/goblin/mapper.py @@ -60,6 +60,7 @@ def get_metaprops(vertex_property, mapping): def map_vertex_to_ogm(result, props, element, *, mapping=None): """Map a vertex returned by DB to OGM vertex""" + props.pop('id') label = props.pop('label') for db_name, value in props.items(): @@ -92,7 +93,7 @@ def map_vertex_to_ogm(result, props, element, *, mapping=None): setattr(element, name, value) if metaprop_dict: vert_prop = getattr(element, name) - vert_prop.mapper_func(metaprop_dict, vert_prop) + vert_prop.__mapping__.mapper_func(metaprop_dict, vert_prop) setattr(element, '__label__', label) setattr(element, 'id', result.id) return element diff --git a/goblin/session.py b/goblin/session.py index 9798436f485e35cb6800771aa987d16d20d25e5b..ca0530b76dbe924eda37aef812cf3f7e730dc7a0 100644 --- a/goblin/session.py +++ b/goblin/session.py @@ -32,7 +32,7 @@ from aiogremlin.gremlin_python.process.traversal import ( from aiogremlin.gremlin_python.structure.graph import Vertex, Edge from goblin import exception, mapper -from goblin.element import GenericVertex, GenericEdge +from goblin.element import GenericVertex, GenericEdge, VertexProperty from goblin.manager import VertexPropertyManager @@ -193,6 +193,7 @@ class Session: if not current: current = self.app.vertices.get( props.get('label'), GenericVertex)() + props = await self._get_vertex_properties(current, props) else: props = await self._get_vertex_properties(current, props) if isinstance(obj, Edge): @@ -220,9 +221,11 @@ class Session: async def _get_vertex_properties(self, element, props): new_props = {} for key, val in props.items(): - if isinstance(getattr(element, key, None), VertexPropertyManager): - vert_prop = await self._g.V( - props['id']).properties(key).valueMap(True).toList() + + if isinstance(element.__properties__.get(key), VertexProperty): + trav = self._g.V( + props['id']).properties(key).valueMap(True) + vert_prop = await trav.toList() new_props[key] = vert_prop else: new_props[key] = val @@ -304,7 +307,7 @@ class Session: result = await self._save_element( vertex, self._check_vertex, self._add_vertex, - self.update_vertex) + self._update_vertex) hashable_id = self._get_hashable_id(result.id) self.current[hashable_id] = result return result @@ -323,7 +326,7 @@ class Session: result = await self._save_element( edge, self._check_edge, self._add_edge, - self.update_edge) + self._update_edge) hashable_id = self._get_hashable_id(result.id) self.current[hashable_id] = result return result @@ -351,7 +354,7 @@ class Session: eid = Binding('eid', edge.id) return await self.g.E(eid).next() - async def update_vertex(self, vertex): + async def _update_vertex(self, vertex): """ Update a vertex, generally to change/remove property values. @@ -363,7 +366,7 @@ class Session: traversal = self._g.V(Binding('vid', vertex.id)) return await self._update_vertex_properties(vertex, traversal, props) - async def update_edge(self, edge): + async def _update_edge(self, edge): """ Update an edge, generally to change/remove property values. @@ -413,7 +416,7 @@ class Session: traversal, _, metaprops = self._add_properties(traversal, props) result = await self._simple_traversal(traversal, vertex) if metaprops: - await self._add_metaprops(result, metaprops) + await self._add_metaprops(result, metaprops, vertex) traversal = self._g.V(Binding('vid', vertex.id)) result = await self._simple_traversal(traversal, vertex) return result @@ -424,8 +427,7 @@ class Session: traversal = self._g.V(Binding('sid', edge.source.id)) traversal = traversal.addE(edge.__mapping__._label) traversal = traversal.to(__.V(Binding('tid', edge.target.id))) - traversal, _, _ = self._add_properties( - traversal, props) + traversal, _, _ = self._add_properties(traversal, props) result = await self._simple_traversal(traversal, edge) return result @@ -442,12 +444,11 @@ class Session: return await self._g.E(eid).next() async def _update_vertex_properties(self, vertex, traversal, props): + await self._g.V(vertex.id).properties().drop().iterate() traversal, removals, metaprops = self._add_properties(traversal, props) - for k in removals: - await self._g.V(Binding('vid', vertex.id)).properties(k).drop().next() result = await self._simple_traversal(traversal, vertex) if metaprops: - removals = await self._add_metaprops(result, metaprops) + removals = await self._add_metaprops(result, metaprops, vertex) for db_name, key, value in removals: await self._g.V(Binding('vid', vertex.id)).properties( db_name).has(key, value).drop().next() @@ -464,15 +465,21 @@ class Session: await self._g.E(eid).properties(k).drop().next() return await self._simple_traversal(traversal, edge) - async def _add_metaprops(self, result, metaprops): + async def _add_metaprops(self, result, metaprops, vertex): potential_removals = [] for metaprop in metaprops: db_name, (binding, value), metaprops = metaprop for key, val in metaprops.items(): if val: - traversal = self._g.V(Binding('vid', result.id)).properties( - db_name).hasValue(value).property(key, val) - await traversal.next() + prop_name = vertex.__mapping__.db_properties[db_name][0] + vp = vertex.__properties__[prop_name] + if vp.cardinality == Cardinality.single: + traversal = self._g.V(Binding('vid', result.id)).properties( + db_name).property(key, val) + else: + traversal = self._g.V(Binding('vid', result.id)).properties( + db_name).hasValue(value).property(key, val) + await traversal.iterate() else: potential_removals.append((db_name, key, value)) return potential_removals diff --git a/tests/test_vertex_properties_functional.py b/tests/test_vertex_properties_functional.py index 1c828ce5f993ee10656d9bdeb06516dd8ce28d05..0e83dff2c048fd21efe1a0b230c55dc68eebd6c1 100644 --- a/tests/test_vertex_properties_functional.py +++ b/tests/test_vertex_properties_functional.py @@ -16,6 +16,8 @@ # along with Goblin. If not, see <http://www.gnu.org/licenses/>. import pytest +from aiogremlin import Graph +from aiogremlin.gremlin_python.process.traversal import Cardinality @pytest.mark.asyncio @@ -81,6 +83,36 @@ async def test_add_update_set_card_property(app, place): await app.close() +@pytest.mark.asyncio +async def test_metas(app, place, remote_connection): + g = Graph().traversal().withRemote(remote_connection) + # Property API + v = await g.addV('person').property('name', 'dave').next() + props = await g.V(v.id).properties().toList() + meta = await g.V(v.id).properties('name').property('nickname', 'davebshow').next() + nickname = await g.V(v.id).properties('name').valueMap(True).next() + # List card + v2 = await g.addV('person').property(Cardinality.list_, 'name', 'dave').property(Cardinality.list_, 'name', 'dave brown').next() + props2 = await g.V(v2.id).properties().toList() + meta2 = await g.V(v2.id).properties('name').hasValue('dave').property('nickname', 'davebshow').next() + nickname2 = await g.V(v2.id).properties('name').valueMap(True).next() + + session = await app.session() + place.historical_name = ['Detroit'] + place.historical_name('Detroit').notes = 'rock city' + place.historical_name('Detroit').year = 1900 + detroit = await session.save(place) + dprops = await g.V(detroit.id).properties().toList() + trav = g.V(detroit.id).properties('historical_name').valueMap(True) + dmetas = await trav.next() + + new_session = await app.session() + new_detroit = await new_session.g.V(detroit.id).next() + + await remote_connection.close() + await app.close() + + @pytest.mark.xfail(pytest.config.getoption('provider') == 'dse', reason='temporary') @pytest.mark.asyncio async def test_add_update_metas(app, place): @@ -98,6 +130,11 @@ async def test_add_update_metas(app, place): assert result.historical_name('Detroit').notes == 'comeback city' assert result.historical_name('Detroit').year == 2016 + new_session = await app.session() + result = await new_session.g.V(place.id).next() + assert result.historical_name('Detroit').notes == 'comeback city' + assert result.historical_name('Detroit').year == 2016 + place.historical_name('Detroit').notes = None place.historical_name('Detroit').year = None result = await session.save(place)