本示例将TitanDB Getting Started 为模板来演示HugeGraph的使用方法。通过对比HugeGraph和TitanDB,了解HugeGraph和TitanDB的差异。
HugeGraph和TitanDB都是基于Apache TinkerPop 3框架的图数据库,均支持[Gremlin](https://tinkerpop.apache .org/gremlin.html)图查询语言,在使用方法和接口方面具有很多相似的地方。然而HugeGraph是全新设计开发的,其代码结构清晰,功能较为丰富,接口更为友好等特点。
HugeGraph相对于TitanDB而言,其主要特点如下:
- HugeGraph拥有较为完善的工具组件。HugeGraph目前有HugeApi、HugeGraph-Client、HugeGraph-Loader、HugeGraph-Studio、HugeGraph-Spark等完善的工具组件,可以完成系统集成、数据载入、图可视化查询、Spark 连接等功能;
- HugeGraph具有Server和Client的概念,第三方系统可以通过jar引用、client、api等多种方式接入,而TitanDB仅支持jar引用方式接入。
- HugeGraph的Schema需要显式定义,所有的插入和查询均需要通过严格的schema校验,目前暂不支持schema的隐式创建。
- HugeGraph充分利用后端存储系统的特点来实现数据高效存取,而TitanDB以统一的Kv结构无视后端的差异性。
- HugeGraph的更新操作可以实现按需操作(例如:更新某个属性)性能更好。TitanDB的更新是read and update方式。
- HugeGraph的VertexId和EdgeId均支持拼接,可实现自动去重,同时查询性能更好。TitanDB的所有Id均是自动生成,查询需要经索引。
本示例通过Property Graph Model图数据模型来描述希腊神话中各人物角色的关系(也被成为人物关系图谱),具体关系详见下图。
其中,圆形节点代表实体(Vertex),箭头代表关系(Edge),方框的内容为属性。
该关系图谱中有两类顶点,分别是人物(character)和位置(location)如下表:
名称 | 类型 | 属性 |
---|---|---|
character | vertex | name,age,type |
location | vertex | name |
有六种关系,分别是父子(father)、母子(mother)、兄弟(brother)、战斗(battled)、居住(lives)、拥有宠物(pet) 关于关系图谱的具体信息如下:
名称 | 类型 | source vertex label | target vertex label | 属性 |
---|---|---|---|---|
father | edge | character | character | - |
mother | edge | character | character | - |
brother | edge | character | character | - |
pet | edge | character | character | - |
lives | edge | character | location | reason |
HugeGraph需要进行严格的schema校验,对于edge label而言需要明确每一个edge label的source vertex label和target vertex label, 且相同的一组source vertex label和target vertex label只能有一个唯一的edge label。 因此,如果character和character有一种关系为father,在同一个图内部god和human之间的边就不能叫father。
关于Edge Label
强约束 "相同的一组src vertex label
和tgt vertex label
只能有一个唯一的edge label
" 的原因有2个:
-
如果
source
和target
有多种edge label
, 同时每一种edge label
拥有不同的属性,那么edge label
的属性就可能产生歧义,无法区分属性到底是属于哪条edge
的。 -
HugeGraph的
edgeId
是通过src+label+tgt
拼接而成,同时HugeGraph支持多种Id生成策略,在解析edgeId
的时候需要获取vertex label
中的Id策略配置才能正确解析vertexId
。 在edge label
满足 " 相同的一组source vertex label
和target vertex label
只能有一个唯一的edge label
"约束的前提下, 可以通过edge label
获取唯一的src vertex label
和tgt vertex label
,而不需要冗余存储src vertex label
和tgt vertex label
信息。
因此本例子将原TitanDB中的monster, god, human, demigod均使用相同的vertex label: character
来表示, 同时增加属性type来标识人物的类型。 edge label
与原TitanDB保持一致。当然为了满足edge label
约束,也可以通过调整edge label
的name
来实现。
HugeGraph需要显示创建Schema,因此需要依次创建PropertyKey、VertexLabel、EdgeLabel,如果有需要索引还需要创建IndexLabel。
schema = hugegraph.schema()
schema.propertyKey("name").asText().ifNotExist().create()
schema.propertyKey("age").asInt().ifNotExist().create()
schema.propertyKey("time").asInt().ifNotExist().create()
schema.propertyKey("reason").asText().ifNotExist().create()
schema.propertyKey("type").asText().ifNotExist().create()
schema.vertexLabel("character").properties("name", "age", "type").primaryKeys("name").nullableKeys("age").ifNotExist().create()
schema.vertexLabel("location").properties("name").primaryKeys("name").ifNotExist().create()
schema.edgeLabel("father").link("character", "character").ifNotExist().create()
schema.edgeLabel("mother").link("character", "character").ifNotExist().create()
schema.edgeLabel("battled").link("character", "character").properties("time").ifNotExist().create()
schema.edgeLabel("lives").link("character", "location").properties("reason").nullableKeys("reason").ifNotExist().create()
schema.edgeLabel("pet").link("character", "character").ifNotExist().create()
schema.edgeLabel("brother").link("character", "character").ifNotExist().create()
// add vertices
Vertex saturn = graph.addVertex(T.label, "character", "name", "saturn", "age", 10000, "type", "titan")
Vertex sky = graph.addVertex(T.label, "location", "name", "sky")
Vertex sea = graph.addVertex(T.label, "location", "name", "sea")
Vertex jupiter = graph.addVertex(T.label, "character", "name", "jupiter", "age", 5000, "type", "god")
Vertex neptune = graph.addVertex(T.label, "character", "name", "neptune", "age", 4500, "type", "god")
Vertex hercules = graph.addVertex(T.label, "character", "name", "hercules", "age", 30, "type", "demigod")
Vertex alcmene = graph.addVertex(T.label, "character", "name", "alcmene", "age", 45, "type", "human")
Vertex pluto = graph.addVertex(T.label, "character", "name", "pluto", "age", 4000, "type", "god")
Vertex nemean = graph.addVertex(T.label, "character", "name", "nemean", "type", "monster")
Vertex hydra = graph.addVertex(T.label, "character", "name", "hydra", "type", "monster")
Vertex cerberus = graph.addVertex(T.label, "character", "name", "cerberus", "type", "monster")
Vertex tartarus = graph.addVertex(T.label, "location", "name", "tartarus")
// add edges
jupiter.addEdge("father", saturn)
jupiter.addEdge("lives", sky, "reason", "loves fresh breezes")
jupiter.addEdge("brother", neptune)
jupiter.addEdge("brother", pluto)
neptune.addEdge("lives", sea, "reason", "loves waves")
neptune.addEdge("brother", jupiter)
neptune.addEdge("brother", pluto)
hercules.addEdge("father", jupiter)
hercules.addEdge("mother", alcmene)
hercules.addEdge("battled", nemean, "time", 1)
hercules.addEdge("battled", hydra, "time", 2)
hercules.addEdge("battled", cerberus, "time", 12)
pluto.addEdge("brother", jupiter)
pluto.addEdge("brother", neptune)
pluto.addEdge("lives", tartarus, "reason", "no fear of death")
pluto.addEdge("pet", cerberus)
cerberus.addEdge("lives", tartarus)
HugeGraph默认是自动生成Id,如果用户通过primaryKeys
指定VertexLabel
的primaryKeys
字段列表后,VertexLabel
的Id策略将会自动切换到primaryKeys
策略。 启用primaryKeys
策略后,HugeGraph通过vertexLabel+primaryKeys
拼接生成VertexId
,可实现自动去重,同时无需额外创建索引即可以使用primaryKeys
中的属性进行快速查询。 例如 "character" 和 "location" 都有primaryKeys("name")
属性,因此在不额外创建索引的情况下可以通过g.V().hasLabel('character') .has('name','hercules')
查询vertex 。
1. Find the grand father of hercules
g.V().hasLabel('character').has('name','hercules').out('father').out('father')
也可以通过repeat
方式:
g.V().hasLabel('character').has('name','hercules').repeat(__.out('father')).times(2)
2. Find the name of hercules's father
g.V().hasLabel('character').has('name','hercules').out('father').value('name')
3. Find the characters with age > 100
g.V().hasLabel('character').has('age',gt(100))
4. Find who are pluto's cohabitants
g.V().hasLabel('character').has('name','pluto').out('lives').in('lives').values('name')
5. Find pluto can't be his own cohabitant
pluto = g.V().hasLabel('character').has('name', 'pluto')
g.V(pluto).out('lives').in('lives').where(is(neq(pluto)).values('name')
// use 'as'
g.V().hasLabel('character').has('name', 'pluto').as('x').out('lives').in('lives').where(neq('x')).values('name')
6. Pluto's Brothers
pluto = g.V().hasLabel('character').has('name', 'pluto').next()
// where do pluto's brothers live?
g.V(pluto).out('brother').out('lives').values('name')
// which brother lives in which place?
g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place')
// what is the name of the brother and the name of the place?
g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name')
推荐使用HugeGraph-Studio 通过可视化的方式来执行上述代码。另外也可以通过HugeGraph-Client、HugeApi、GremlinConsole和GremlinDriver等多种方式执行上述代码。
HugeGraph目前支持Gremlin的语法,用户可以通过Gremlin语句实现各种查询需求,但是目前HugeGraph暂不支持全文检索功能。