JanusGraph图是通过Schema定义,每个Schema包含边标签、属性、点标签。JanusGraph的Schema可以被显式或者隐式定义。 建议开发者在程序开发期间显式的定义图的Schema。显式的定义Schema可以提高程序的健壮性,也能够方便与同事的协作开发。注意,JanusGraph的Schema可以随着时间的推移而改变,而不会中断正常的数据库操作。扩展Schema不会减慢查询速度,也不需要数据库停机。

Schema第一次被创建时,需要指定他们的类型例如边的标签、属性的key、顶点的标签。无法更改特定元素的Schema类型。这是为了确保系统的稳定。

除了本节中介绍的Schema定义方式外,第30章高级教程中也讲了如何定义来提高性能。

1. 定义边的标签

连接两个顶点的每条边都有一个标签,用来描述他们之间的关系。例如:顶点A和顶点B之间具有朋友关系,那他们之间边的标签可以定义为friend。

要定义边的标签,通过graph实例或者management调用方法makeEdgeLabel(String),传入边标签的名称作为参数。边标签的名称必须是全局唯一的。这个方法返回一个边标签的builder,用来定义边的multiplicity。边标签的多重性定义了该标签在所有边上的多重约束,即顶点对之间的最大边数。 JanusGraph支持以下多重性设置。

1.1. 边标签的Multiplicity
  • MULTI: 允许任意一对顶点之间的同一标签有多条边。换句话说,该图是关于这种边标签的多图。边的多样性没有约束。
  • SIMPLE: 在任何一对顶点之间最多允许此类标签的一条边。换句话说,该图是关于该标签的单图。保证该标签的边在任意两个顶点之间是唯一的。
  • MANY2ONE: 在图中的任何顶点上最多允许此标签的一条出边,但不对入边施加约束。边标签mother是MANY2ONE多样性的一个例子,因为每个人最多只有一个母亲,但母亲可以有多个孩子。
  • ONE2MANY: 在图中的任何顶点上最多允许此标签的一条入边,但不对出边施加约束。边标签winnerOf是具有ONE2MANY多样性的示例,因为每个比赛最多只能赢得一个人,但是一个人可以赢得多个比赛。
  • ONE2ONE: 在图的任何顶点上最多允许此标签的一个入边和一个出边。边标签marriedTo是ONE2ONE多样性的一个例子,因为一个人与另一个人结婚。

默认的Multiplicity是MULTI。 边标签的定义是通过调用构建器上的make()方法完成的,该方法返回的是定义的边标签,如下例所示。

1
2
3
4
mgmt = graph.openManagement()
follow = mgmt.makeEdgeLabel('follow').multiplicity(MULTI).make()
mother = mgmt.makeEdgeLabel('mother').multiplicity(MANY2ONE).make()
mgmt.commit()

2. 定义属性

顶点和边的属性是键值对。 例如,属性name =’Daniel’具有键名和值’Daniel’。 属性键是JanusGraph架构的一部分,可以约束允许的数据类型和值的基数。

要定义属性键,请在打开的图形或管理事务上调用makePropertyKey(String),并提供属性键的名称作为参数。 属性键名称在图形中必须是唯一的,建议避免使用属性名称中的空格或特殊字符。 此方法返回属性键的构建器。

2.1. 属性的数据类型

使用dataType(Class)定义属性的数据类型。 JanusGraph将使用已经定义的数据类型给属性赋值,来保证添加到图中的数据有效。 例如,可以定义属性name的数据类型是String。

允许把任何(可序列化的)的值赋值给数据类型为Object.class的属性。 但是,建议尽可能的使用具体的数据类型。 设置的数据类型必须是具体的类,而不能是接口或抽象类。 JanusGraph确保类相等,因此不允许添加已设置数据类型的子类。

JanusGraph支持以下数据类型。

类型 描述
String
Character
Boolean
Byte
Short
Integer
Long
Float
Double
Date
Geoshape
UUID
2.2. 属性的Cardinality

使用Cardinality来定义顶点上相关属性的Cardinality。

  • SINGLE:对于这个值,每个属性最多允许一个值。 换句话说,这个键→值对在图中的所有顶点中都是唯一的。 属性birthDate是具有SINGLE基数的例子,因为每个人只有一个出生日期。
  • LIST:允许每个顶点的这个属性有任意多个值。 换句话说,这个属性的值是一个list列表。 假设我们将传感器作为图中的顶点,则属性sensorReading是一个具有LIST基数的例子,需要记录大量的传感器数据。
  • SET:允许多个值,但每个顶点的此属性值不能重复。 换句话说,此属性与一组值相关联。 如果我们想要记录一个人的所有姓名(包括昵称,婚前姓名等),则属性name具有SET基数。

默认的cardinality值为SINGLE。 注意,边的属性和属性的cardinality是SINGLE。 不支持为边或属性上的单个key赋多个值。

1
2
3
4
5
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()

3. 关系类型

边的标签和属性共同称为关系类型。 关系类型的名称在图中必须是唯一的,这意味着属性和边标签不能具有相同的名称。 JanusGraph API中有一些方法可以查询或检验关系类型中是否包含属性和边标签。

1
2
3
4
5
mgmt = graph.openManagement()
if (mgmt.containsRelationType('name'))
name = mgmt.getPropertyKey('name')
mgmt.getRelationTypes(EdgeLabel.class)
mgmt.commit()

4. 定义顶点标签

像边一样,顶点也有标签。 与边标签不同,顶点标签是可选的。 顶点标签可用于区分不同类型的顶点,例如, 用户顶点和产品顶点。

虽然标签在概念和数据模型上是可选的,但JanusGraph在内部实现中会为所有顶点分配一个标签。 addVertex方法创建顶点时使用JanusGraph的默认标签。

要创建标签,请调用方法makeVertexLabel(String).make(),并提供顶点标签的name作为参数。 顶点标签的名称在图中必须是唯一的。

1
2
3
4
5
6
7
8
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()

5. 自动创建Schema

如果未明确定义边标签,属性或顶点标签,则在边,顶点或属性首次使用时,它会被隐式定义。 JanusGraph默认的的DefaultSchemaMaker配置会定义这些类型。

默认情况下,隐式创建的边标签具有多重性MULTI,隐式创建的属性具有基数SINGLE和数据类型是Object.class。 用户可以通过实现和注册自己的DefaultSchemaMaker来控制Schema的自动创建方式。

强烈建议显示的定义所有的Schema并且通过在JanusGraph配置中设置schema.default = none来禁用自动创建Schema。

6. 更改Schema

边标签,属性键或顶点标签的定义一旦提交则无法更改。 但是,可以通过JanusGraphManagement.changeName(JanusGraphSchemaElement,String)来更改Schema中元素的名字,如以下示例所示,其中属性place被重命名为location。

1
2
3
4
mgmt = graph.openManagement()
place = mgmt.getPropertyKey('place')
mgmt.changeName(place, 'location')
mgmt.commit()

请注意,在正在运行的遍历查询和集群中的JanusGraph实例中,Schema名字的更改可能不会立即生效。虽然通过后端存储向所有JanusGraph实例通知了Schema名字更改,但它可能需要一段时间才能生效,并且如果遇到某些故障情况(如网络分区)可能需要重启实例。因此,用户必须确保满足以下任一条件:

  • 重命名的标签或key当前未使用(即写入或读取),并且在所有JanusGraph实例都知道name更改之前不会使用。
  • 执行的遍历主动适配短暂的中间状态,其中旧名称或新名称基于特定的JanusGraph实例和名称更改的状态。例如,这意味着遍历可能同时查询两个name。

如果需要重新定义现有的Schema类型,建议将此类型的名称更改为当前未使用并且永远不会使用的名称。之后,可以使用原来的name定义新标签或key,从而有效地替换旧标签或key。但注意,这不会影响使用先前的类型去写入顶点,边或属性。不支持在线重新定义现有图元素,必须通过图批量转换完成。

7. Schema约束

Schema的定义允许用户显式配置属性和连接约束。属性可以绑定到特定的顶点标签和/或边标签。此外,连接约束允许用户明确定义哪两个顶点标签被连接通过边缘标签。这些约束用于确保图与给定的模型匹配。例如,对于众神的图,god可以是另一个god的兄弟,而不是怪物,god可以拥有年龄属性,但位置不能具有年龄年龄。这些约束默认情况下是禁用的。

通过设置schema.constraints = true来启用这些schema约束。这个设置依赖schema.default配置。如果schema.default设置为none,则会因违反schema约束而抛出IllegalArgumentException。如果schema.default未设置为none,则会自动创建schema约束,而不会引发异常。激活schema约束对现有数据没有影响,因为这些schema约束仅在插入过程中应用。因此,这些约束完全不会影响数据的读取。

可以使用JanusGraphManagement.addProperties(VertexLabel,PropertyKey …)将多个属性绑定到同一顶点,例如:

1
2
3
4
5
6
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 …)将多个属性绑定到同一条边,例如:

1
2
3
4
5
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)定义边的出点和入点的连接,例如:

1
2
3
4
5
6
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()

欢迎扫码关注公众号,更好的交流

欢迎扫码关注公众号,更好的交流