前言
如果做知识图谱的大家都知道janusgraph,neo4j等等,janusgraph图数据库的分布式底层依赖于hbase,性能简直太差了。所以公司一直让我找一款性能比较好,而且上手容易的一款图库。最近在做图数据库的对比,发现了市场一款新概念的图数据仓库,AbutionGraph是通用的HOLAP大数据存储和分析平台,实时大数据图谱式查询分析;
我们以神图demo,做个语法上的对比
图中,对应的类型解释如下:
符号类型 | 含义 |
---|---|
粗体key | 图索引键 |
加粗 加星号的key | 图唯一索引键 |
下划线key | 顶点为中心的索引键,vertex-centric index |
空心箭头edge | 不可重复边,两个节点之间最多只能有一个该类型边 |
实心箭头edge | 单向边,只能A–>B,不可以B–>A |
包含类型
主要包含6种节点类型:
- location:位置(sky:天空,sea:海,tartarus:塔耳塔洛斯)
- titan:巨人(saturn:罗马神话中的农神)
- god:神(jupiter,neptune,pluto)
- demigod:半神(hercules)
- human:人类(alcmene)
- monster:怪物(nemean,hydra,cerberus)
主要包含6中边类型:
- father:父亲
- mother:母亲
- brother:兄弟
- battled:战斗
- lives:生活在
- pet:宠物
一.JanusGraph诸神图代码示例
1、获取图管理对象实例
if (graph instanceof StandardJanusGraph) {
Preconditions.checkState(mixedIndexNullOrExists((StandardJanusGraph)graph, mixedIndexName),
ERR_NO_INDEXING_BACKEND, mixedIndexName);
}
// Create Schema
JanusGraphManagement management = graph.openManagement();
2、创建属性和对应索引
// ===创建name属性; String、唯一CompositeIndex、锁机制保证name的强一致性
final PropertyKey name = management.makePropertyKey("name").dataType(String.class).make();
JanusGraphManagement.IndexBuilder nameIndexBuilder = management.buildIndex("name", Vertex.class).addKey(name);
if (uniqueNameCompositeIndex)
nameIndexBuilder.unique();
JanusGraphIndex nameIndex = nameIndexBuilder.buildCompositeIndex();
// 此处的LOCK,在name索引上添加了LOCK标识,标识这在并发修改相同的name属性时,必须通过锁机制(本地锁+分布式锁)保证并发修改;
management.setConsistency(nameIndex, ConsistencyModifier.LOCK);
// ===创建age属性;Integer、mixed index
final PropertyKey age = management.makePropertyKey("age").dataType(Integer.class).make();
if (null != mixedIndexName)
management.buildIndex("vertices", Vertex.class).addKey(age).buildMixedIndex(mixedIndexName);
// ===创建time属性
final PropertyKey time = management.makePropertyKey("time").dataType(Integer.class).make();
// ===创建reason属性
final PropertyKey reason = management.makePropertyKey("reason").dataType(String.class).make();
// ===创建place属性
final PropertyKey place = management.makePropertyKey("place").dataType(Geoshape.class).make();
// 为reason 和 place属性创建mixed index索引edges
if (null != mixedIndexName)
management.buildIndex("edges", Edge.class).addKey(reason).addKey(place).buildMixedIndex(mixedIndexName);
3、创建edge类型和对应索引
// 创建边类型:father, many to one
management.makeEdgeLabel("father").multiplicity(Multiplicity.MANY2ONE).make();
// 创建边类型:mother, many to one
management.makeEdgeLabel("mother").multiplicity(Multiplicity.MANY2ONE).make();
// 创建边类型:battled, 签名密匙为time:争斗次数,
EdgeLabel battled = management.makeEdgeLabel("battled").signature(time).make();
// 为battled边创建一个以顶点为中心的 中心索引(vertex-centric index),索引属性time; 双向索引,可以从 神->怪物 也可以 怪物->神
// 将查询节点对应的 battled 边时,可以使用这个vertex-centric索引,索引属性为 time;
// vertex-centric index为了解决大节点问题,一个节点存在过多的边!
management.buildEdgeIndex(battled, "battlesByTime", Direction.BOTH, Order.desc, time);
// 创建边类型:lives,签名密匙为reason
management.makeEdgeLabel("lives").signature(reason).make();
// 创建边类型:pet
management.makeEdgeLabel("pet").make();
// 创建边类型:brother
management.makeEdgeLabel("brother").make();
4、创建vertex类型
// 创建节点label
management.makeVertexLabel("titan").make();
management.makeVertexLabel("location").make();
management.makeVertexLabel("god").make();
management.makeVertexLabel("demigod").make();
management.makeVertexLabel("human").make();
management.makeVertexLabel("monster").make();
5、提交创建的schema数据
management.commit();
6、插入数据
获取图事务对象:
JanusGraphTransaction tx = graph.newTransaction();
插入节点数据:
// 插入节点
Vertex saturn = tx.addVertex(T.label, "titan", "name", "saturn", "age", 10000);
Vertex sky = tx.addVertex(T.label, "location", "name", "sky");
Vertex sea = tx.addVertex(T.label, "location", "name", "sea");
Vertex jupiter = tx.addVertex(T.label, "god", "name", "jupiter", "age", 5000);
Vertex neptune = tx.addVertex(T.label, "god", "name", "neptune", "age", 4500);
Vertex hercules = tx.addVertex(T.label, "demigod", "name", "hercules", "age", 30);
Vertex alcmene = tx.addVertex(T.label, "human", "name", "alcmene", "age", 45);
Vertex pluto = tx.addVertex(T.label, "god", "name", "pluto", "age", 4000);
Vertex nemean = tx.addVertex(T.label, "monster", "name", "nemean");
Vertex hydra = tx.addVertex(T.label, "monster", "name", "hydra");
Vertex cerberus = tx.addVertex(T.label, "monster", "name", "cerberus");
Vertex tartarus = tx.addVertex(T.label, "location", "name", "tartarus");
插入边数据:
// 插入边数据
jupiter.addEdge("father", saturn);
jupiter.addEdge("lives", sky, "reason", "loves fresh breezes");
jupiter.addEdge("brother", neptune);
jupiter.addEdge("brother", pluto);
neptune.addEdge("lives", sea).property("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, "place", Geoshape.point(38.1f, 23.7f));
hercules.addEdge("battled", hydra, "time", 2, "place", Geoshape.point(37.7f, 23.9f));
hercules.addEdge("battled", cerberus, "time", 12, "place", Geoshape.point(39f, 22f));
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);
提交事务,持久化数据:
// 提交事务,持久化提交的数据到磁盘
tx.commit();
二.AbutionGraph实现如下:
1.开启图链接
Graph graph = G.Graph("god").schema(mkSchema()).build();
2.建模
Schema schema = Schema.Builder()
.entity(
Dimension.label("titan", "太阳神").property("age", Integer.class).build(),
Dimension.label("god", "上帝").property("age", Integer.class).build(),
Dimension.label("demigod", "小神").property("age", Integer.class).build(),
Dimension.label("human", "人类").property("age", Integer.class).build(),
Dimension.label("monster", "怪物").build(),
Dimension.label("location", "场景").build())
.edge(Dimension.label("father", "父亲").build(),
Dimension.label("brother", "兄弟").build(),
Dimension.label("mother", "母亲").build(),
Dimension.label("battled", "战争").property("time", Integer.class)
.property("place", Geoshape.class).build(),
Dimension.label("battledAggregation", "战争聚合").property("totalTime", Integer.class, Agg.Sum())
.groupBy().build(),
Dimension.label("pet", "宠物").build(),
Dimension.label("lives", "生活").property("reason", String.class).build())
.build();
注意:上面建模多了一个战争聚合是AbutionGraph独有的动态图技术,具有自动聚合特性。当指定groupBy()的时候,还需要指定相应聚合的方式,这里才用累加的方式。
3.插入数据
声明一个用户,这个是AbutionGraph独有的权限控制
User user = new User();
插入节点数据
Entity saturn = Knowledge.dimV("titan").vertex("saturn").property("age", 10000).build();
Entity sky = Knowledge.dimV("location").vertex("sky").build();
Entity sea = Knowledge.dimV("location").vertex("sea").build();
Entity jupiter = Knowledge.dimV("god").vertex("jupiter").property("age", 5000).build();
Entity neptune = Knowledge.dimV("god").vertex("neptune").property("age", 4500).build();
Entity hercules = Knowledge.dimV("demigod").vertex("hercules").property("age", 30).build();
Entity alcmene = Knowledge.dimV("human").vertex("alcmene").property("age", 45).build();
Entity pluto = Knowledge.dimV("god").vertex("pluto").property("age", 4000).build();
Entity nemean = Knowledge.dimV("monster").vertex("nemean").build();
Entity hydra = Knowledge.dimV("monster").vertex("hydra").build();
Entity cerberus = Knowledge.dimV("monster").vertex("cerberus").build();
Entity tartarus = Knowledge.dimV("location").vertex("tartarus").build();
//单条插入
graph.addKnowledge(saturn).exec(user);
//批量插入
graph.addKnowledge(sky,sea,jupiter,neptune,hercules,alcmene,pluto,nemean,hydra,cerberus,tartarus).exec(user);
插入边数据
//jupiter relation
Edge eg = Knowledge.dimE("father").edge("jupiter", "saturn").build();
Edge eg1 = Knowledge.dimE("lives").edge("jupiter", "sky").property("reason", "loves fresh breezes").build();
Edge eg2 = Knowledge.dimE("brother").edge("jupiter", "neptune").build();
Edge eg3 = Knowledge.dimE("brother").edge("jupiter", "pluto").build();
//neptune relation
Edge eg4 = Knowledge.dimE("lives").edge("neptune", "sea").property("reason", "loves waves").build();
Edge eg5 = Knowledge.dimE("brother").edge("neptune", "jupiter").build();
Edge eg6 = Knowledge.dimE("brother").edge("neptune", "pluto").build();
//hercules relation
Edge eg7 = Knowledge.dimE("father").edge("hercules", "jupiter").build();
Edge eg8 = Knowledge.dimE("mother").edge("hercules", "alcmene").build();
Edge eg9 = Knowledge.dimE("battled").edge("hercules", "nemean").property("time", 1).property("place", Geoshape.point(38.1,23.7)).build();
Edge eg10 = Knowledge.dimE("battled").edge("hercules", "hydra").property("time", 2).property("place", Geoshape.point(37.7, 23.9)).build();
Edge eg11 = Knowledge.dimE("battled").edge("hercules", "cerberus").property("time", 12).property("place", Geoshape.point(39,22)).build();
//pluto relation
Edge eg12 = Knowledge.dimE("brother").edge("pluto", "jupiter").build();
Edge eg13 = Knowledge.dimE("brother").edge("pluto", "neptune").build();
Edge eg14 = Knowledge.dimE("lives").edge("pluto", "tartarus").property("reason", "no fear of death").build();
Edge eg15 = Knowledge.dimE("pet").edge("pluto", "cerberus").build();
//cerberus relation
Edge eg16 = Knowledge.dimE("lives").edge("cerberus", "tartarus").build();
List<Edge> elements = Lists.newArrayList(eg, eg1, eg2, eg3, eg4, eg5, eg6, eg6, eg7, eg8, eg9, eg10, eg11, eg12, eg13, eg14, eg15, eg16);
4.提交数据
AddKnowledge addKnowledge = new AddKnowledge.Builder().input(elements).build();
graph.execute(addKnowledge, user);
注意:看到提交数据的时候创建了一个user对象,这个AbutionGraph独有的权限管理,这里不深入展开,如果想了解更多点我。
三.janusGraph和AbutionGraph诸神图对比
1.建模
Janusgraph:需要单独点和边以及指定属性的索引
AbutionGraph:在维度上自动建立了索引,不需要单独指定,同时支持自定义id。可以通过正则等模糊匹配到vertex。
2.插入数据
JanusGraph:支持批量实时导入数据,但是仅支持小量的数据。
AbutionGraph:既支持单条插入,同时也支持多条插入。如果是聚合的属性,普通的插入即可,数据库会自动进行聚合。支持大量的数据实时导入。
性能对比请查看
3.语法对比
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
gremlin> g.V().has('name', 'hercules')
==>v[24] //返回的是id
gremlin> g.V().has('name', 'hercules').out('father')
==>v[16]
gremlin> g.V().has('name', 'hercules').out('father').out('father')
==>v[20]
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
gremlin> g.V().has('name', 'hercules').values('name')
==>hercules
gremlin> g.V().has('name', 'hercules').out('father').values('name')
==>jupiter
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
请注意下面的遍历显示了'hercules'的整个父系树分支。提供这种复杂的遍历是为了展示Gremlin语言的灵活性和表现力。对Gremlin的熟练掌握为JanusGraph用户提供了流畅地获取Janusgraph图形结构的能力。
gremlin> g.V().has('name', 'hercules').repeat(out('father')).emit().values('name')
==>jupiter
==>saturn
下面提供了一些更多的遍历示例。
gremlin> hercules = g.V().has('name', 'hercules').next()
==>v[1536]
gremlin> g.V(hercules).out('father', 'mother').label()
==>god
==>human
gremlin> g.V(hercules).out('battled').label()
==>monster
==>monster
==>monster
gremlin> g.V(hercules).out('battled').valueMap()
==>{name=nemean}
==>{name=hydra}
==>{name=cerberus}
添加语法:
gremlin> theseus = graph.addVertex('human')
==>v[3328]
gremlin> theseus.property('name', 'theseus')
==>null
gremlin> cerberus = g.V().has('name', 'cerberus').next()
==>v[2816]
gremlin> battle = theseus.addEdge('battled', cerberus, 'time', 22)
==>e[7eo-2kg-iz9-268][3328-battled->2816]
gremlin> battle.values('time')
==>22
AbutionGraph:
//查询所有点
jshell> var exec = g.V().exec(user);
==> [Entity[vertex=hercules,dimension=demigod,properties=Properties[age=<java.lang.Integer>30]], Entity[vertex=hydra,dimension=monster,properties=Properties[]], Entity[vertex=cerberus,dimension=monster,properties=Properties[]], Entity[vertex=nemean,dimension=monster,properties=Properties[]], Entity[vertex=saturn,dimension=titan,properties=Properties[age=<java.lang.Integer>10000]], Entity[vertex=sky,dimension=location,properties=Properties[]], Entity[vertex=tartarus,dimension=location,properties=Properties[]], Entity[vertex=sea,dimension=location,properties=Properties[]], Entity[vertex=jupiter,dimension=god,properties=Properties[age=<java.lang.Integer>5000]], Entity[vertex=neptune,dimension=god,properties=Properties[age=<java.lang.Integer>4500]], Entity[vertex=pluto,dimension=god,properties=Properties[age=<java.lang.Integer>4000]], Entity[vertex=alcmene,dimension=human,properties=Properties[age=<java.lang.Integer>45]]]
//查询年龄小于50的顶点
jshell> var exec = g.V().dims().has("age").by(P.LessThan(50)).exec(user);
==> [Entity[vertex=hercules,dimension=demigod,properties=Properties[age=<java.lang.Integer>30]], Entity[vertex=alcmene,dimension=human,properties=Properties[age=<java.lang.Integer>45]]]
//查询年龄小于50并且维度是human的顶点
jshell> var exec = g.V().dim("human").has("age").by(P.LessThan(50)).exec(user);
==>[{"class":"cn.thutmose.abution.graph.data.knowhow.Entity","dimension":"human","vertex":"alcmene","properties":{"age":45}}]
//查询 hercules 的一度顶点id
jshell> var exec = g.V("hercules").Out().exec(user);
==> [nemean, cerberus, hydra, jupiter, alcmene]
//查询 hercules 的爸爸
jshell> var exec = g.V("hercules").Out().dim("father").exec(user);
==> [jupiter]
//查询 hercules 的爸爸的爸爸
jshell> var exec = g.V("hercules").Out().dim("father").Out().dim("father").exec(user);
==> [saturn]
//查询 hercules的爸爸的爸爸另一种写法
jshell> var exec1 = g.V("hercules").While().gql(G.GetNeighborIds().ToSet().ToEntityIds().Out().dim("father").getChain()).maxRepeats(2).exec(user);
==> [{"class":"cn.thutmose.abution.graph.data.knowhow.id.EntityKey","vertex":"saturn"}]
四.总结
1.语法方面:janusGraph使用了gremlin语言,abutionGraph使用了类似gremlin的语法。语法比较相似,风格也差不多。好处是AbutionGraph支持原子级别的权限,上手还是比较简单。
2.性能方面:我从AbutionGraph官方文档上看性能是吊打JanusGraph,由于目前还是在写demo看是否容易上手,接下来会对AbutionGraph进行性能测试。
如果你也是正在使用AbutionGraph希望您和我一样少走弯路,多多交流