假设我有一个如下图所示的图表
graph = TinkerGraph.open()
g = graph.traversal()
v1 = g.addV('CC').property('name','t1')
v2 = g.addV('KK').property('name','t1')
我想找到所有与
CC
具有相同 'name'
的 KK
。我可以写:
g.V().hasLabel('CC').as('c').values('name').as('cn').V().hasLabel('KK').values('name').as('k').where('cn',eq('k')).select('c')
这模仿了SQL中的连接,但是这样写性能看起来很糟糕。从SQL2Gremlin,他们有“连接”两个节点(如果两个节点之间有边连接)的示例。我想知道gremlin中是否有任何连接方法可以预先未知是否存在连接两个节点的路径?换句话说,在 gremlin 中编写“join”的最佳方法是什么,而我们不知道这两个节点是否直接连接或通过路径连接?
非常感谢!
你的直觉基本上是正确的。 “连接”是两个顶点之间已实现的关系(即边)。这通常是图数据库所能获得的收益。对于图来说,以 SQL 样式对属性执行顶点到顶点连接通常效率不高。
对于您的查询,您可以将其重新写入此表单以更加清晰:
gremlin> g.V().hasLabel('CC').as('c').
......1> V().hasLabel('KK').
......2> where(eq('c')).
......3> by('name').
......4> select('c')
==>v[0]
但是性能可能会保持不变,因为我认为目前没有任何图形系统会优化这种遍历。不会使用任何索引,您将留下“CC”和“KK”的完整图形扫描来获取结果。显然,这在大图上是非常昂贵的。
Gremlin 用户邮件列表这里对此主题进行了一些讨论,其中提出了一些很好的观点。值得注意的是,乔什·佩里曼(Josh Perryman)写道(除其他优点外):
SQL 风格的连接是对图形数据库引擎的一种非常糟糕的使用。喜欢 丹尼尔建议应预先计算连接并在以下位置添加边缘 写入时间/数据我开玩笑。
这是必然和设计的。边缘基本物化 加入。图形数据库针对它们进行了优化,磁盘或缓存读取 手术。关系数据库针对连接、查询时间进行了优化 电脑操作。
预先计算边缘通常要便宜得多 在加载数据之前分离引擎而不是在数据加载之后这样做 进入图表。例外情况是当边缘已确定时 基于通过图的多跳路径。对于该用例,图表 dB 最好。
斯蒂芬·马莱特写道:
因为我认为目前没有任何图形系统会优化这种遍历。
pymogwai 正在尝试朝这个方向发展,它是本机 python gremlin 实现。我们打算为其使用 SPOG 索引。看 https://github.com/juupje/pyMogwai/issues/13
和https://github.com/juupje/pyMogwai/blob/main/mogwai/core/hd_index.py
基于A。 Harth 和 S. Decker,“从 Web 查询 RDF 的优化索引结构”,第三届拉丁美洲 Web 大会 (LA-WEB'2005),阿根廷布宜诺斯艾利斯,2005 年,第 10 页,doi: 10.1109/LAWEB .2005.25.
'''
Created on 2024-11-10
@author: wf
'''
import json
from mogwai.core.mogwaigraph import MogwaiGraph, MogwaiGraphConfig
from mogwai.core.traversal import MogwaiGraphTraversalSource
from tests.basetest import Basetest
class TestSpogJoin(Basetest):
"""Test SPOG index based joins"""
def setUp(self, debug=True):
Basetest.setUp(self, debug=debug)
def test_spog_join(self):
"""Test joining vertices by property values using SPOG index"""
# Create test graph with CC and KK vertices having same names
graph = MogwaiGraph(config=MogwaiGraphConfig(name_field='_node_name',index_config='minimal'))
# Add test vertices
v1 = graph.add_labeled_node('CC', 't1', {'name': 't1'})
v2 = graph.add_labeled_node('CC', 't2', {'name': 't2'})
v3 = graph.add_labeled_node('KK', 'k1', {'name': 't1'})
v4 = graph.add_labeled_node('KK', 'k2', {'name': 't3'})
# Use SPOG join
graph.join('CC', 'KK', 'name', 'name', 'same_name')
# Verify joins
g=MogwaiGraphTraversalSource(graph)
joins = g.E().has_label('same_name').count().next().run()
self.assertEqual(joins, 1)
if self.debug:
self.show_indices(graph.spog_index)
def show_indices(self, spog_index, limit=3):
"""Show SPOG index contents"""
for index_name in spog_index.config.active_indices:
if index := spog_index.indices.get(index_name):
print(f"Index: {index_name}")
keys = list(index.lookup.keys())[:limit]
lookup = {k: list(index.lookup[k])[:limit]
for k in keys}
print(json.dumps(lookup, indent=2, default=str))
Index: PO
{
"label": [
"KK",
"CC",
"same_name"
],
"name": [
"t2",
"same_name",
"t3"
],
"same_name": [
2
]
}
Index: PG
{
"label": [
"node-label",
"edge-label"
],
"name": [
"edge-name",
"node-name",
"node-property"
],
"same_name": [
"edge-link"
]
}
Index: SG
{
"0": [
"node-property",
"edge-label",
"edge-link"
],
"1": [
"node-label",
"node-name",
"node-property"
],
"2": [
"node-label",
"node-name",
"node-property"
]
}
Index: SO
{
"0": [
"t1",
2,
"CC"
],
"1": [
"t2",
"CC"
],
"2": [
"t1",
"KK",
"k1"
]
}
Index: OS
{
"CC": [
0,
1
],
"t1": [
0,
2
],
"t2": [
1
]
}
Index: GO
{
"node-label": [
"KK",
"CC"
],
"node-name": [
"t1",
"t2",
"k2"
],
"node-property": [
"t1",
"t2",
"t3"
]
}
Index: PS
{
"label": [
0,
1,
2
],
"name": [
0,
1,
2
],
"same_name": [
0
]
}
Index: GP
{
"node-label": [
"label"
],
"node-name": [
"name"
],
"node-property": [
"name"
]
}
test test_spog_join, debug=True took 0.0 s
----------------------------------------------------------------------
Ran 1 test in 0.006s