在Gremlin/Tinkerpop中,我想在顶点上执行版本化的插入。如果存在现有顶点,我只希望在版本与版本号属性匹配的情况下对其进行变异。
下面是试图使用gremlinjs进行此操作的代码。它无法创建顶点,随后的查询将无法找到它。
(以前的版本出现了编译错误,但这是javscriptism的一个无文档语法问题)
更新见评论,以回答什么是错误的。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;
};参考文献:
版本
发布于 2019-10-23 12:02:50
我试着用“现代”玩具图来改变你的代码,你的代码被证明是对的。我相信以下是你所做的事情的精神所在:
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()逻辑,它是否正常工作?换句话说,你能让基本的重新插入操作开始工作吗?如果不是,那么这个问题似乎孤立于查询的这一部分,尽管您似乎遵循了推荐的实践,所以我不确定可能出了什么问题。
发布于 2019-10-26 21:39:57
下面的gremlinjs代码只在版本号属性匹配的情况下更新顶点。这允许使用乐观并发对顶点进行安全的读-修改-写入(例如,冲突应该很少见,或者您应该做其他的事情)。
代码可用作gist: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();
});https://stackoverflow.com/questions/58513680
复制相似问题