在Gremlin/Tinkerpop中,我想在Vertex上执行版本更新。如果存在现有的Vertex,我只想在版本匹配版本号属性时对其进行修改。
下面是尝试使用gremlinjs
执行此操作的代码。它无法创建顶点,稍后的查询无法找到它。
(此版本的先前版本有编译错误,但这是一个javscript主义未记录的语法问题)
[更新]请参阅回答问题的评论。工作版本https://gist.github.com/pszabop/3b07fa7caadf1dbd86953a713ed96ce0
//
// 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;
if (!o.id) {
o.id = uuidv4();
}
if (!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
// 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),
__.in_()
.property('lastUpdate', o.__lastUpdate) // updated properties go here
.property('version', o.__version) // updated properties go here
.property('obj', JSON.stringify(o)), // updated properties go here
__.out()
).next();
return o;
};
gremlinjs中的以下代码仅在版本号属性匹配时更新顶点。这允许使用乐观并发对顶点进行安全的读-修改-写(即碰撞应该很少,或者您应该做其他事情)
代码可作为要点:https://gist.github.com/pszabop/3b07fa7caadf1dbd86953a713ed96ce0
//
// 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();
});
我使用“现代”玩具图尝试了您的代码变体,结果证明您的代码对我来说是正确的。我相信以下内容抓住了您所做工作的精神:
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操作正常工作吗?如果不是这样,那么问题似乎只存在于查询的这一部分,尽管您所做的似乎遵循了推荐的做法,所以我不确定有什么地方不对劲。
我有一个图,我想沿着包含与顶点上的属性匹配的属性的边。例如。 所以在上面的情况下,假设我从第一个顶点开始,我想跟随属性“version”等于它的“vertValue”的边。 我已经使用进行了一个查询,其中能够跟踪边缘: 问题是,我想返回实际的边缘对象。然而,当我需要在边的“vert”值上做“where”时,在查询中我正在做一个值(“vert”),所以我得到的结果只是我正在测试的值,而不是边对象。我
我提出的问题与您在这里看到的几乎相同,但有一个约束条件,即必须使用groovy而不是java语法。理想情况下,答案应该非常简洁。 我有一个简单的人顶点图。每个人都有一个“年龄”属性,列出该人的年龄(以年为单位)。还有“worksFor”标记的边连接成对的人顶点。我希望看到所有边缘,边缘两端的人都具有相同的年龄属性。 然后我想问一个类似的问题,两个年龄相差不到3年。 如前所述,这应该是groovy语
我需要从一个顶点开始,找到所有相关的顶点,直到结束。标准是匹配边inV顶点中的任何一个边属性(属性)。如果边缘属性“value”与inV顶点“attribute”名称不匹配,我应该跳过该顶点。边的属性值作为属性名称传播到inV顶点中 我使用下面的查询,但这给了我父节点、下一个节点和之间的边的json输出。通过输出am写入逻辑,仅拾取与边缘属性匹配的下一个属性。如果属性匹配可以通过gremlin查询
只有当新项目的日期比现有项目更近时,我才想更新DynamoDB中的项目。目前,我正在查询现有项目,在代码中进行比较,然后写入数据库。我想知道是否有办法让DynamoDB为我进行检查。我已经研究过使用预期,但是它的比较运算符需要引入一个参数,这违背了目的,因为这意味着无论如何都必须查询现有项目。 我和8Java一起工作。
我有一个用户名数组(例如,)要添加到图中的“user”标签下。 现在我首先要检查用户名是否已经存在(),然后仅在“user”标签下添加username属性不匹配的内容。 此外,这可以在单个gremlin查询或groovy脚本中完成吗? 我正在使用titan graph数据库、tinkerpop3和gremlin REST服务器。
我正在尝试使用DynamoDB JavaScript shell创建一个简单的表,我得到了这个异常: 下面是我试图创建的表: 但是,如果我将第二个属性添加到KeySchema,它就可以正常工作。a工作台下方: 我不想将范围添加到密钥架构。知道怎么修吗?