Давайте сделаем это шаг за шагом:
Найдите пары вершин, а также соберите их соответствующих соседей:
g.V().match(
__.as('v1').out().dedup().fold().as('v1n'),
__.as('v1').V().as('v2'),
__.as('v2').out().dedup().fold().as('v2n')).
where('v1', neq('v2'))
Убедитесь, что v1
не является соседом v2
и наоборот:
g.V().match(
__.as('v1').out().dedup().fold().as('v1n'),
__.as('v1').V().as('v2'),
__.as('v2').out().dedup().fold().as('v2n')).
where('v1', neq('v2').and(without('v2n'))).
where('v2', without('v1n'))
Затем вычислите количество пересекающихся соседей и общее количество соседей:
g.V().match(
__.as('v1').out().dedup().fold().as('v1n'),
__.as('v1').V().as('v2'),
__.as('v2').out().dedup().fold().as('v2n')).
where('v1', neq('v2').and(without('v2n'))).
where('v2', without('v1n')).as('m').
project('v1','v2','i','u').
by(select('v1')).
by(select('v2')).
by(select('v1n').as('n').
select('m').
select('v2n').unfold().
where(within('n')).
count()).
by(union(select('v1n'),
select('v2n')).unfold().
dedup().count())
И, наконец, вычислите сходство по Джакарду, разделив i
наu
(также убедитесь, что вершины без соседей отфильтрованы для предотвращения деления на 0):
g.V().match(
__.as('v1').out().dedup().fold().as('v1n'),
__.as('v1').V().as('v2'),
__.as('v2').out().dedup().fold().as('v2n')).
where('v1', neq('v2').and(without('v2n'))).
where('v2', without('v1n')).as('m').
project('v1','v2','i','u').
by(select('v1')).
by(select('v2')).
by(select('v1n').as('n').
select('m').
select('v2n').unfold().
where(within('n')).
count()).
by(union(select('v1n'),
select('v2n')).unfold().
dedup().count()).
filter(select('u').is(gt(0))).
project('v1','v2','j').
by(select('v1')).
by(select('v2')).
by(math('i/u'))
И последнее: поскольку сравнение вершин v1
и v2
такое же, как сравнение v2
и v1
, запрос должен учитывать только один случай.Один из способов сделать это - убедиться, что идентификатор v1
меньше идентификатора v2
:
g.V().match(
__.as('v1').out().dedup().fold().as('v1n'),
__.as('v1').V().as('v2'),
__.as('v2').out().dedup().fold().as('v2n')).
where('v1', lt('v2')).
by(id).
where('v1', without('v2n')).
where('v2', without('v1n')).as('m').
project('v1','v2','i','u').
by(select('v1')).
by(select('v2')).
by(select('v1n').as('n').
select('m').
select('v2n').unfold().
where(within('n')).
count()).
by(union(select('v1n'),
select('v2n')).unfold().
dedup().count()).
filter(select('u').is(gt(0))).
project('v1','v2','j').
by(select('v1')).
by(select('v2')).
by(math('i/u'))
Выполнение этого обхода над современным игрушечным графом даетследующий результат:
gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().match(
......1> __.as('v1').out().dedup().fold().as('v1n'),
......2> __.as('v1').V().as('v2'),
......3> __.as('v2').out().dedup().fold().as('v2n')).
......4> where('v1', lt('v2')).
......5> by(id).
......6> where('v1', without('v2n')).
......7> where('v2', without('v1n')).as('m').
......8> project('v1','v2','i','u').
......9> by(select('v1')).
.....10> by(select('v2')).
.....11> by(select('v1n').as('n').
.....12> select('m').
.....13> select('v2n').unfold().
.....14> where(within('n')).
.....15> count()).
.....16> by(union(select('v1n'),
.....17> select('v2n')).unfold().
.....18> dedup().count()).
.....19> filter(select('u').is(gt(0))).
.....20> project('v1','v2','j').
.....21> by(select('v1')).
.....22> by(select('v2')).
.....23> by(math('i/u'))
==>[v1:v[1],v2:v[5],j:0.0]
==>[v1:v[1],v2:v[6],j:0.3333333333333333]
==>[v1:v[2],v2:v[4],j:0.0]
==>[v1:v[2],v2:v[6],j:0.0]
==>[v1:v[4],v2:v[6],j:0.5]
==>[v1:v[5],v2:v[6],j:0.0]