元数据及数据模型
每个JanusGraph图的元数据都由边标签、属性键及其使用的顶点标签组成。JanusGraph的元数据既可以显式定义,也可以隐式定义。建议用户在应用程序开发期间显式定义图的元数据。显式定义图的元数据是开发健壮应用程序的重要组成部分,同时提高协作软件的开发效率。注意,JanusGraph的元数据锁着时间的推移而不断的演进,而不会中断正常的数据库操作。扩展JanusGraph的元数据不会减慢查询响应速度,也不需要数据库停机维护。
JanusGraph图的元数据类型(既边标签、属性健或)顶点标签在第一次创建时分配给图形中的元素。之后无法对特定的元素更改分配的元数据类型。这样做确保了一个稳定type系统。
除了本节介绍的图元数据定义选项之外,元数据类型还提供了性能调整选项,将在Advanced Schema中讨论的。
元数据显示
在管理API中有一些方法可以查看图中特定元素的元数据。这些方法有mgmt.printIndexes(), mgmt.printPropertyKeys(), mgmt.printVertexLabels(), and mgmt.printEdgeLabels(). 还可以通过printSchema()查看所有的元数据组合。
mgmt = graph.openManagement()
mgmt.printSchema()
定义边标签
边连接着图的两个顶点并通过便签来定义顶点之间关系的语义。例如:顶点A和B之间有一个标签为friend的边表名A和B之间是朋友关系。
为一个边定义标签,可以通过调用makeEdgeLabel(String)方法将边的标签作为参数传入。在一个图中边的标签名称必须是唯一的。上述这个方法返回一个builder,可以用来对边的多样性。边多样性的定义对边在顶点间的使用进行约束,换句话说,同样标签的边在同一对顶点之间可以存在的数量进行约束。JanusGraph有对边多样性的定义有以下五种:
- MULTI:允许任意一对顶点之间具有多个相同标签的边。换句话说,相对于这样的边标签,图是一个多样的图。对边的多样性不做约束。
- SIMPLE:在任何一对顶点之间最多只允许一条这样标签的边。换句话说,相对于标签图是一个简单的图。确保给定标签和顶点对应的边是唯一的。
- MANY2ONE:在图形中的任何顶点上最多允许此类标签的一个出边,但不对入边做任何约束。例如边标签mother就是一个MANY2ONE的例子,因为每个人最多有一个母亲,但母亲可以有多个孩子。
- ONE2MANY:在图形中的任何顶点上最多允许此类标签的一个入边,但不对出边做约束。例如边标签winnerOf是一个ONE2MANY的例子,因为每个比赛最多由一个人赢得,但一个人可以赢得多个比赛
- ONE2ONE:在图形中的任何顶点上,最多只允许该标签的一个入边和一个出边。例如边标签marriedTo是一个ONE2ONE的例子,因为一个人只与另外一个人结婚。
JanusGraph对边多样性的默认定义是MULTI.对于边的多样性的定义通过调用make()来完成,如下所示:
mgmt = graph.openManagement()
follow = mgmt.makeEdgeLabel('follow').multiplicity(MULTI).make()
mother = mgmt.makeEdgeLabel('mother').multiplicity(MANY2ONE).make()
mgmt.commit()
定义属性键值
图的顶点和边的属性都是键值对的方式。例如,属性name='Daniel'具有键name和值“Daniel”。属性的键是JanusGraph元数据的一部分,可以约束允许值的数据类型和基数。
要定义属性键,请在打开的图形或管理事务上调用makePropertyKey(String),并提供属性键的名称作为参数。属性键名称在图形中必须唯一,建议在属性名称中避免使用空格或特殊字符。此方法返回属性键的builder。
注意:在属性键创建期间,考虑同时创建图形索引以获得更好的性能, 可参照Index Performance.
Janusgraph使用数据类型(java类)定义属性键的数据类型。JanusGraph将强制与键关联的所有值都具有已配置的数据类型,从而确保添加到图中的数据是有效的。例如,可以定义name键具有String数据类型。请注意不支持基本数据类型,要使用相应的封装类,例如我们定义整型数据类型的时候应使用Integer而不是int。
Janusgraph可以使用一个具体的Java类来自定义属性键,不能使用接口、抽象类,也不允许添加已配置数据类型的子类。
JanusGraph原生数据类型
类型名称 | 说明 |
---|---|
String | 字符序列 |
Character | 单个字符 |
Boolean | true 或 false |
Byte | byte 值 |
Short | 短整型值 |
Integer | 整型值 |
Long | 长整型值 |
Float | 4字节浮点数 |
Double | 8字节浮点数 |
Date | 时间值 (java.util.Date) |
Geoshape | 地理形状,如点、圆或框 |
UUID | 通用唯一标识符 (java.util.UUID) |
Use 基数(Cardinality) 定义与任何给定顶点上的键关联的值的允许基数。
- SINGLE: 对于此类键,每个元素最多允许一个值。换句话说,key→value映射对于图中的所有元素都是唯一的。属性键birthDate是一个具有单一基数的示例,因为每个人只有一个出生日期。
- LIST:允许此类键的每个元素具有任意数量的值。换句话说,键与允许重复值的值列表相关联。假设我们将传感器建模为图中的顶点,属性键sensorReading是一个具有列表基数的示例,允许记录大量(可能重复的)传感器读数。
- SET:允许此键的每个元素有多个值,但不允许有重复的值。换句话说,键与一组值相关联。属性键name设置了基数,如果我们要捕获个人的所有姓名(包括昵称、婚前姓等)。
默认的基数设置是SINGLE。所以请注意,边和属性上使用的属性键具有基数SINGLE。不支持为边缘或属性上的单个键附加多个值
mgmt = graph.openManagement()
birthDate = mgmt.makePropertyKey('birthDate').dataType(Long.class).cardinality(Cardinality.SINGLE).make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
sensorReading = mgmt.makePropertyKey('sensorReading').dataType(Double.class).cardinality(Cardinality.LIST).make()
mgmt.commit()
关系类型
边缘标签和属性键统称为关系类型。关系类型的名称在图形中必须唯一,这意味着属性键和边缘标签不能具有相同的名称。JanusGraph API中有一些方法可以查询存在或检索包含属性键和边缘标签的关系类型。
mgmt = graph.openManagement()
if (mgmt.containsRelationType('name'))
name = mgmt.getPropertyKey('name')
mgmt.getRelationTypes(EdgeLabel.class)
mgmt.commit()
顶点标签定义
和边一样顶点也有标签,但是顶点的标签是可选的。顶点的标签是用来帮助区分顶点的不同类型。例如:用户类型的顶点和商品类型的顶点。
尽管标签在概念和数据模型级别是可选的,JanusGraph为所有顶点分配一个标签作为内部实现细节。 通过addVertex方法来创建顶点并使用JanusGraph的默认标签。
要创建标签,请对打开的图形或管理事务调用makeVertexLabel(String).make()方法,并提供顶点标签的名称作为参数。顶点标签名称在图形中必须唯一。
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
mgmt.commit()
// Create a labeled vertex
person = graph.addVertex(label, 'person')
// Create an unlabeled vertex
v = graph.addVertex()
graph.tx().commit()
自动生成元数据
如果么有显式的定义边标签、属性键或顶点标签,则在首次添加边、顶点或设置属性时,JanusGraph将会隐式定义它。defaultschemaker隐式的定义了这些类型。
默认情况下,边标签的多样式设置为MULTI,属性键的基数设置为SINGLE,数据类型设置为Object.class。用户可以通过实现和注册自己的DefaultSchemaMaker来控制schema的自动创建。
当为不同于SINGLE的vertex属性定义基数时,在第一个查询(即定义新的顶点属性键的查询)中,该基数应用于vertex属性的所有值。
强烈建议显式定义所有schema元素,并通过在JanusGraph设置schema.default=none关闭默认Schema生成器。
修改元数据
边标签、属性键或顶点标签的定义一旦提交到图形中就不能更改。但是,元数据的名称可以通过JanusGraphManagement.changeName(JanusGraphSchemaElement,String)进行修改,如下例所示,其中属性键place被重命名为location。
mgmt = graph.openManagement()
place = mgmt.getPropertyKey('place')
mgmt.changeName(place, 'location')
mgmt.commit()
请注意,模式名称的更改在集群中当前运行的事务和其他JanusGraph图实例中可能不会立即可见。虽然Schema名称更改通过存储后端通知给所有JanusGraph实例,但更改可能需要一段时间才能生效,并且可能需要在某些故障情况下(如网络分区)重新启动实例(如果它们与重命名一致)。因此,用户必须确保以下任何一项有效:
- 重命名的标签或键当前未处于活动使用状态(即写入或读取),并在所有JanusGraph实例感知到重命名之前不会使用。
- Schema修改的过程中可能存在一段时间有的实例使用了新的名称也有些实例还没来及更改依然使用着就的名称,在这期间事务的查询结果可能存在两个名称。
如果需要重新定义现有Schema类型,建议将此类型的名称更改为当前不在使用的名称。之后,可以用原始名称定义新的标签或键,从而有效地替换旧的标签或键。但是,请注意,这不会影响先前使用现有类型写入的顶点、边或属性。重新定义现有的图形元素必须通过离线批处理进行转换完成。
Schema约束
Schema的定义可以显示的说明属性和连接的约束。这些约束可用于确保图与设计的领域模型一致。例如诸神图,一个god可以是另一个god的兄弟,但和一个monster 则不行,一个god可以有age属性,但是location就不行。默认情况下是没有这些约束的。
通过设置schema.constraints=true来启用这些Schema约束。这些具体约束通过schema.default设置来确定。如果schema.default设置为none。如果违反约束就会引发IllegalArgumentException。如果schema.default设置不为none,就是自动创建Schema约束,但是不会引发异常。激活Schema约束对于已经录入的数据不会产生影响,因为这个约束只应用于插入图数据的时候。多个属性可以通过JanusGraphManagement.addProperties(VertexLabel, PropertyKey...)绑定到一个顶点,如下所示:
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
birthDate = mgmt.makePropertyKey('birthDate').dataType(Long.class).cardinality(Cardinality.SINGLE).make()
mgmt.addProperties(person, name, birthDate)
mgmt.commit()
多个属性可以通过JanusGraphManagement.addProperties(EdgeLabel, PropertyKey...)绑定到一条边,如下所示:
mgmt = graph.openManagement()
follow = mgmt.makeEdgeLabel('follow').multiplicity(MULTI).make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
mgmt.addProperties(follow, name)
mgmt.commit()
通过JanusGraphManagement.addConnection(EdgeLabel, VertexLabel out, VertexLabel in)可以创建一个连接,如下所示:
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
company = mgmt.makeVertexLabel('company').make()
works = mgmt.makeEdgeLabel('works').multiplicity(MULTI).make()
mgmt.addConnection(works, person, company)
mgmt.commit()