在Gremlin中,如何修改顶点';仅当版本属性与数字匹配时,才使用s属性?

在Gremlin中,如何修改顶点';仅当版本属性与数字匹配时,才使用s属性?,gremlin,tinkerpop,janusgraph,gremlinjs,Gremlin,Tinkerpop,Janusgraph,Gremlinjs,在Gremlin/Tinkerpop中,我希望在顶点上执行版本化的upsert。如果存在一个现有顶点,我只想在版本与版本号属性匹配时对其进行变异 下面是尝试使用gremlinjs执行此操作的代码。它无法创建顶点,以后的查询无法找到它 (此的早期版本有一个编译错误,但这是一个未记录的脚本语法问题) [更新]关于问题所在,请参见回答中的评论。 工作版本 参考资料: 版本 janusgraph/janusgraph:最新版本 gremlinjs 3.4.4 我使用“现代”玩具图形尝试了您的代

在Gremlin/Tinkerpop中,我希望在顶点上执行版本化的upsert。如果存在一个现有顶点,我只想在版本与版本号属性匹配时对其进行变异

下面是尝试使用
gremlinjs
执行此操作的代码。它无法创建顶点,以后的查询无法找到它

(此的早期版本有一个编译错误,但这是一个未记录的脚本语法问题)

[更新]关于问题所在,请参见回答中的评论。 工作版本

参考资料:
版本
  • janusgraph/janusgraph:最新版本
  • gremlinjs 3.4.4

我使用“现代”玩具图形尝试了您的代码变体,结果证明您的代码对我来说是正确的。我相信以下内容抓住了你所做工作的精神:

gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().property('version',1).iterate()
gremlin> name = 'marko'
==>marko
gremlin> oldVersion = 1
==>1
gremlin> version = 2
==>2
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>edited
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> name = 'stephen'
==>stephen
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> oldVersion = 2
==>2
gremlin> version = 3
==>3
gremlin> 
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>edited
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> g.V().has('person','name','stephen').elementMap()
==>[id:19,label:person,name:stephen,version:3]
根据你对问题的描述,我建议你尽量简化一下。你说问题在于:

它无法创建顶点,以后的查询无法找到它


如果删除
choose()
逻辑,它是否正常工作?换句话说,您能让基本的upsert操作正常工作吗?如果不是,那么问题似乎与查询的这一部分无关,尽管您所做的似乎遵循了推荐的做法,所以我不确定有什么问题

我使用“现代”玩具图尝试了您的代码变体,结果证明您的代码对我来说是正确的。我相信以下内容抓住了你所做工作的精神:

gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().property('version',1).iterate()
gremlin> name = 'marko'
==>marko
gremlin> oldVersion = 1
==>1
gremlin> version = 2
==>2
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>edited
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> name = 'stephen'
==>stephen
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> oldVersion = 2
==>2
gremlin> version = 3
==>3
gremlin> 
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>edited
gremlin> g.V().has('person','name',name).
......1>   fold().
......2>   coalesce(unfold(), 
......3>            addV('person').property('name',name).property('version',version)).
......4>   choose(values('version').is(oldVersion),
......5>          property('version', version).constant('edited'),
......6>          constant('same'))
==>same
gremlin> g.V().has('person','name','stephen').elementMap()
==>[id:19,label:person,name:stephen,version:3]
根据你对问题的描述,我建议你尽量简化一下。你说问题在于:

它无法创建顶点,以后的查询无法找到它


如果删除
choose()
逻辑,它是否正常工作?换句话说,您能让基本的upsert操作正常工作吗?如果不是,那么问题似乎与查询的这一部分无关,尽管您所做的似乎遵循了推荐的做法,所以我不确定有什么问题

只有当版本号属性匹配时,gremlinjs中的以下代码才会更新顶点。这允许使用乐观并发性对顶点进行安全的读-修改-写操作(即碰撞应该很少,或者您应该做其他事情)

代码作为要点提供:


gremlinjs中的以下代码仅在版本号属性匹配时更新顶点。这允许使用乐观并发性对顶点进行安全的读-修改-写操作(即碰撞应该很少,或者您应该做其他事情)

代码作为要点提供:


谢谢你,你的例子让我明白使用
\uuuu.in()
是为了遍历边,我应该做的是继续修改,例如
\uuuu.property('version',o.\uu version).
在@PaulS进行测试的工作版本的要点-很高兴听到你解决了你的问题!请你把要点换成一个单独的答案,并接受它,这样这个问题就可以标记为已回答了?谢谢谢谢你,你的例子让我明白使用
\uuuu.in()
是为了遍历边,我应该做的是继续修改,例如
\uuuu.property('version',o.\uu version).
在@PaulS进行测试的工作版本的要点-很高兴听到你解决了你的问题!请你把要点换成一个单独的答案,并接受它,这样这个问题就可以标记为已回答了?谢谢
//
// o.id o.__version, and o.__lastUpdate have special meaning or are reserved
//
graphdb.upsertVertexVersioned = async function(type, o) {
  const g = traversal().withRemote(this.connection);
  let oldVersion;

  // don't modify original in case of exceptions
  // return the new object and let user decide to reassign or not
  o = Object.assign({}, o);

  if (!o.id) {
    o.id = uuidv4();
  }
  if (!Number.isInteger(o.__version)) {
    o.__version = 0;
    oldVersion = 0;
  } else {
    oldVersion = o.__version;
    o.__version++;
  }
  o.__lastUpdate = Date.now();

  // @see http://tinkerpop.apache.org/docs/current/recipes/#element-existence
  // @see https://stackoverflow.com/questions/58513680/in-gremlin-how-do-i-modify-a-vertexs-properties-only-if-a-version-property-mat
  // The pattern we are using is keys get copied into properties that can be used
  // by the graph database for its work, and then the
  // entire object is JSON serialized into a generic `obj` property.
  // XXX TBD use graphson?
  const v1 = await g.V().has(type, 'id', o.id)
  .fold()
  .coalesce(__.unfold(),
    __.addV(type).property('id', o.id)
    .property('version', o.__version)
  ).choose(__.values('version').is(oldVersion),
    __.property('lastUpdate', o.__lastUpdate)                  // updated properties go here
    .property('version', o.__version)
    .property('obj', JSON.stringify(o)).constant('edited'),
    __.constant('unchanged')
  ).next();

  if (v1.value === 'unchanged') {
    throw new Error('version mismatch, vertex not updated');
  }

  return o;
};

test('test vertex versioned upsert and get', async function(t) {

  graphdb.open();

  // initial write and verify
  const o = { randomText: uuidv4(), foo: 'bar'}
  const osent1 = await graphdb.upsertVertexVersioned('testtype', o);
  t.ok(osent1.id, 'a random ID was assigned');
  const oget1 = await graphdb.getVertex('testtype', osent1.id);
  t.equal(oget1.randomText, o.randomText, 'random text was as written');
  t.equal(oget1.id, osent1.id, 'ID was as assigned');
  t.equal(oget1.foo, 'bar', 'field foo is "bar"');

  // make sure version gets updated when field foo is modified
  oget1.foo = 'beyond all repair';
  const osent2 = await graphdb.upsertVertexVersioned('testtype', oget1);
  t.equal(osent2.__version, 1, 'version was changed from 0 to 1');
  const oget2 = await graphdb.getVertex('testtype', oget1.id);
  t.equal(oget2.randomText, o.randomText, 'random text was as written and was unchanged on second write');
  t.equal(oget2.id, osent1.id, 'ID was as assigned');
  t.equal(oget2.foo, 'beyond all repair', 'field foo was changed to "beyond all repair"');

  // if we are using a stale copy of the object an update should not happen
  osent1.foo = 'illegal update';
  try {
    const osent3 = await graphdb.upsertVertexVersioned('testtype', osent1);
    t.fail('should never returned from an incorrect version update');
  } catch (err) {
    t.ok(err.toString().includes('not updated'), 'error message is correct on illegal version update attempt');
  }
  const oget3 = await graphdb.getVertex('testtype', oget1.id);
  t.equal(oget3.randomText, o.randomText, 'random text was as written and was unchanged on second write');
  t.equal(oget3.id, osent1.id, 'ID was as assigned');
  t.equal(oget3.foo, 'beyond all repair', 'field foo was unchanged after failed update');
  graphdb.close();

});