有很多方法可以做到这一点,但我认为TinkerPop社区通常采用以下方法:
g.V().has('event','id','1').
fold().
coalesce(unfold(),
addV('event').property('id','1'))
基本上,它使用has()
查找“事件”,并使用fold()
步骤强制转换为列表。该列表将为空或其中有一个Vertex
。然后使用coalesce()
,尝试unfold()
列表,如果它具有立即返回的Vertex
,否则执行addV()
。
如果想要在找到元素时更新现有属性,只需在coalesce()
之后添加property()
步骤即可:
g.V().has('event','id','1').
fold().
coalesce(unfold(),
addV('event').property('id','1')).
property('description','This is an event')
如果您需要知道返回的顶点是“新”的还是旧的,那么可以像这样做:
g.V().has('event','id','1').
fold().
coalesce(unfold().
project('vertex','exists').
by(identity()).
by(constant(true)),
addV('event').property('id','1').
project('vertex','exists').
by(identity()).
by(constant(false)))
关于这个主题的更多阅读可以在这个问题上找到: "为什么需要使用coalesce进行条件插入来折叠/展开?"
还要注意,可选边缘插入在这里描述: "使用gremlin添加不存在的边缘".
最后需要注意的是,虽然这个问题是关于CosmosDB的,但答案通常适用于所有TinkerPop-enabled图形。当然,图形如何优化这个Gremlin是一个单独的问题。如果一个图形具有本地upsert功能,则该功能可能会或可能不会在这个Gremlin的背后使用,因此可能有更好的方法通过图形系统本地API实现upsert(当然,选择这条路会降低代码的可移植性)。
更新:自TinkerPop 3.6.0起,fold()/coalesce()/unfold()
模式已被新的mergeV()
和mergeE()
步骤大量替换,这极大地简化了执行类似upsert操作所需的Gremlin。在3.6.0及更高版本中,您将使用以下内容替换第一个示例:
g.mergeV([(label): 'event', id: '1'])
或者更好的做法是,将名为“id”的属性键视为 T
的实际顶点标识符(我已添加了“name”属性键以帮助说明):
g.mergeV([(label): 'event', (id): '1', name: 'stephen'])
上面的代码将在地图中搜索带有
T.label
、
T.id
和 "name" 的顶点。如果找到,它会返回该顶点。如果没有找到,则使用这些值创建一个新的
Vertex
。如果您拥有
T.id
,最好直接使用如下方式:
g.mergeV([(id): '1']).
option(onCreate, [(label): 'event', name: 'stephen'])
通过这种方式,您将搜索条件限制为仅标识符,足以唯一标识它并避免使用其他过滤器。如果未找到顶点,则会触发onCreate
以使用提供的Map
与搜索条件Map
共同创建顶点。
g.V('1234').fold().as('existing').coalesce(unfold(), addV().property(id,'1234')).as('result').select('existing','result')
这个方法很好用,当节点被创建时返回{ 'existing': [], result: { ... } }
,但是如果节点已经存在,则会重复返回结果:{ 'existing': [{ /*node payload*/ }], result: { /*same node payload again*/ } }
。 - Cristian Diaconescustore()
):g.V('1235').fold().store('existing').coalesce(unfold(), addV().property(id,'1235').store('new')).cap('existing','new')
。这样,要么 'existing' 要么 'new' 始终为空。如果有更好的方式来暴露布尔标志和载荷的单个属性,请告诉我。 - Cristian Diaconescu