Schema 初始化策略
除了提供 Groovy 代码给 JanusGraph 服务器来初始化 schema 或执行任何自定义逻辑之外,JanusGraph 还提供了在启动时执行自定义 schema 初始化策略的能力。这些策略旨在帮助用户更轻松地定义和维护他们的 schema,但它们不能替代通过直接使用 JanusGraphManagement 开发的自定义 schema 管理流程。
每次通过 JanusGraphFactory 或 ConfiguredGraphFactory 启动 JanusGraph 实例时,都会在启动前执行 schema 初始化策略。可以通过 schema.init.strategy 配置参数选择 schema 初始化策略。默认选择 none,这意味着 schema 初始化过程被跳过。
参数 schema.init.schema-drop-before-startup 可用于配置在启动时删除 schema 和数据(在测试环境中可能很方便)。
JSON Schema Initialization
当在 schema.init.strategy 中选择 json 配置选项时,它会通过 JSON 格式的 schema 定义触发 schema 初始化,该定义可以通过文件或字符串提供。
此策略的配置在 schema.init.json 命名空间下提供。设置 JSON schema 初始化最简单的方法是将 JSON schema 文件路径提供给 schema.init.json.file 或直接将 JSON schema 插入到 schema.init.json.string 配置选项中。
JSON Schema Format
通过 JSON 定义的 schema 必须是 org.janusgraph.core.schema.json.definition.JsonSchemaDefinition 类的反序列化 JSON 版本,其中顶级对象元素将是以下内容
vertexLabelsedgeLabelspropertyKeyscompositeIndexesvertexCentricEdgeIndexesvertexCentricPropertyIndexesmixedIndexes
每个值都是一个数组,表示顶点标签、边标签、属性键、复合索引、顶点中心边索引、顶点中心属性索引或混合索引的列表。
JSON 顶点标签定义
每个顶点标签对象包含以下键
label-string数据类型。(必需)staticVertex-boolean数据类型。partition-boolean数据类型。ttl-number数据类型。
JSON 边标签定义
每个边标签对象包含以下键
label-string数据类型。(必需)multiplicity-string数据类型。允许值:MULTI,SIMPLE,ONE2MANY,MANY2ONE,ONE2ONE。unidirected-boolean数据类型。ttl-number数据类型。
JSON 属性键定义
每个属性键对象包含以下键
key-string数据类型。(必需)className-string数据类型。这必须是所选属性数据类型的完整类路径。例如,java.lang.String,java.lang.Long,org.janusgraph.core.attribute.Geoshape。(必需)cardinality-string数据类型。允许值:SINGLE,LIST,SET。ttl-number数据类型。
JSON 复合索引定义
每个复合索引对象包含以下键
name-string数据类型。(必需)typeClass-string数据类型。被索引元素数据类型的完整类路径。允许值:org.apache.tinkerpop.gremlin.structure.Vertex,org.apache.tinkerpop.gremlin.structure.Edge。(必需)indexOnly-string数据类型。顶点或边标签。unique-boolean数据类型。consistency-string数据类型。允许值:DEFAULT,LOCK,FORK。keys- 一个对象数组,表示org.janusgraph.core.schema.json.definition.index.JsonIndexedPropertyKeyDefinition(参见索引定义后的定义)。这些是索引键。(必需)inlinePropertyKeys- 要内联到复合索引中的属性键数组。目前仅支持顶点复合索引。有关此功能的更多信息,请参阅文档。
JSON 混合索引定义
每个混合索引对象包含以下键
name-string数据类型。(必需)typeClass-string数据类型。被索引元素数据类型的完整类路径。允许值:org.apache.tinkerpop.gremlin.structure.Vertex,org.apache.tinkerpop.gremlin.structure.Edge。(必需)indexOnly-string数据类型。顶点或边标签。indexBackend-string数据类型。索引后端配置的名称。(必需)keys- 一个对象数组,表示org.janusgraph.core.schema.json.definition.index.JsonIndexedPropertyKeyDefinition(参见索引定义后的定义)。这些是索引键。(必需)
JSON 顶点中心边索引定义
每个顶点中心边索引对象包含以下键
name-string数据类型。(必需)propertyKeys- 字符串数组(每个值都是string数据类型)。这些是用于索引的边属性。(必需)order-string数据类型。允许值:asc,desc。indexedEdgeLabel-string数据类型。要索引的边标签。(必需)direction-string数据类型。允许值:OUT,IN,BOTH。
JSON 顶点中心属性索引定义
每个顶点中心属性索引对象包含以下键
name-string数据类型。(必需)propertyKeys- 字符串数组(每个值都是string数据类型)。这些是用于索引的元属性。(必需)order-string数据类型。允许值:asc,desc。indexedPropertyKey-string数据类型。要索引的属性键。(必需)
JSON 复合索引和混合索引中定义的属性键的定义 (keys)
作为复合索引或混合索引的 keys 表示的每个属性键对象包含以下键
propertyKey-string数据类型。(必需)parameters- 一个对象数组,表示org.janusgraph.core.schema.json.definition.JsonParameterDefinition。这些是可选参数,用于让索引了解属性键的其他配置。(参见下面的描述)
JSON 复合索引和混合索引中属性键定义的参数定义 (parameters)
每个参数都是一个配置,用于让底层索引后端更好地配置相关属性。每个这样的参数包含以下键
key-string数据类型。(必需)value-string数据类型。(必需)parser-string数据类型。这必须是用于解析参数value的解析器的完整类路径或预定义的快捷方式。此解析器必须实现org.janusgraph.core.schema.json.parser.JsonParameterParser接口并具有无参数构造函数。如果未提供,则默认使用string解析器。
预定义的 parser 快捷方式
string- 不更改value并按原样使用(String数据类型)。enum- 将提供的字符串(定义为<full class path>.<enum option>)替换为实际的枚举值。例如,如果value具有字符串org.janusgraph.core.schema.Mapping.STRING,它将被替换为实际的STRING枚举,并且不会被视为字符串。boolean- 将值解析为Boolean。byte- 将值解析为Byte。short- 将值解析为Short。integer- 将值解析为Integer。long- 将值解析为Long。float- 将值解析为Float。double- 将值解析为Double。
参数可能包含定义为 ParameterType.customParameterName("<your custom key>") 的自定义键。为了在 JSON 中定义此类键,需要为 key 使用特定的前缀 - `%`custom%`(总共前缀由 10 个字符组成。如果某些字符在此处未正确呈现,您可以随时参考 org.janusgraph.graphdb.types.ParameterType.CUSTOM_PARAMETER_PREFIX 以查找这 10 个前缀字符)。因此,要定义像 similarity 这样的自定义键,您应该将键写为:%`custom%`similarity
JSON Schema 定义示例
遵循上述规则(在 JSON Schema Format 中定义),JSON schema 定义示例如下所示。
注意
以下 schema 有效且已使用 Cassandra 和 ElasticSearch 进行测试。它使用了一些与最终一致数据库和特别是 ElasticSearch 相关的参数。因此,某些特定于数据库的 schema 定义可能无法与其他数据库正常工作(如 similarity 或 string-analyzer)。下面的 schema 仅用于演示目的。
{
"vertexLabels": [
{
"label": "normalVertex"
},
{
"label": "partitionedVertex",
"partition": true
},
{
"label": "unmodifiableVertex",
"staticVertex": true
},
{
"label": "temporaryVertexForTwoHours",
"staticVertex": true,
"ttl": 7200000
}
],
"edgeLabels": [
{
"label": "normalSimpleEdge",
"multiplicity": "SIMPLE",
"unidirected": false
},
{
"label": "unidirectedMultiEdge",
"multiplicity": "MULTI",
"unidirected": true
},
{
"label": "temporaryEdgeForOneHour",
"ttl": 3600000
},
{
"label": "edgeWhichUsesLocksInEventualConsistentDBs",
"consistency": "LOCK"
},
{
"label": "edgeWhichUsesForkingInEventualConsistentDBs",
"consistency": "FORK"
}
],
"propertyKeys": [
{
"key": "normalProperty",
"className": "java.lang.Long"
},
{
"key": "stringProperty",
"className": "java.lang.String",
"cardinality": "SINGLE"
},
{
"key": "anotherStringProperty",
"className": "java.lang.String",
"cardinality": "SINGLE"
},
{
"key": "listProperty",
"className": "java.lang.Long",
"cardinality": "LIST"
},
{
"key": "geoshapeProperty",
"className": "org.janusgraph.core.attribute.Geoshape"
},
{
"key": "setProperty",
"className": "java.lang.String",
"cardinality": "SET"
},
{
"key": "propertyWhichUsesLocksInEventualConsistentDBs",
"className": "java.lang.Long",
"cardinality": "SET",
"consistency": "LOCK"
},
{
"key": "temporaryPropertyForOneHour",
"className": "java.lang.Long",
"ttl": 3600000
}
],
"compositeIndexes": [
{
"name": "simpleCompositeIndex",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"keys": [
{
"propertyKey": "normalProperty"
}
]
},
{
"name": "indexOnlyForNormalVerticesOnListProperty",
"indexOnly": "normalVertex",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"keys": [
{
"propertyKey": "listProperty"
}
]
},
{
"name": "compositeIndexOnEdge",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Edge",
"keys": [
{
"propertyKey": "normalProperty"
}
]
},
{
"name": "uniqueCompositeIndexWithLocking",
"indexOnly": "unmodifiableVertex",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"unique": true,
"consistency": "LOCK",
"keys": [
{
"propertyKey": "stringProperty"
}
]
},
{
"name": "multiKeysCompositeIndex",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"keys": [
{
"propertyKey": "normalProperty"
},
{
"propertyKey": "anotherStringProperty"
}
]
},
{
"name": "compositeIndexWithInlinedProperties",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"keys": [
{
"propertyKey": "setProperty"
}
],
"inlinePropertyKeys": ["normalProperty", "stringProperty", "anotherStringProperty"]
}
],
"vertexCentricEdgeIndexes": [
{
"name": "vertexCentricBothDirectionsEdgeIndex",
"indexedEdgeLabel": "normalSimpleEdge",
"direction": "BOTH",
"propertyKeys": [
"normalProperty"
],
"order": "asc"
},
{
"name": "vertexCentricUnidirectedEdgeIndexOnMultipleProperties",
"indexedEdgeLabel": "unidirectedMultiEdge",
"direction": "OUT",
"propertyKeys": [
"stringProperty",
"anotherStringProperty"
],
"order": "desc"
}
],
"vertexCentricPropertyIndexes": [
{
"name": "normalVertexCentricPropertyKey",
"indexedPropertyKey": "listProperty",
"propertyKeys": [
"normalProperty"
],
"order": "asc"
}
],
"mixedIndexes": [
{
"name": "simpleMixedIndexOnMultipleProperties",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"indexBackend": "search",
"keys": [
{
"propertyKey": "normalProperty"
},
{
"propertyKey": "geoshapeProperty"
}
]
},
{
"name": "mixedIndexWithParametersOnProperties",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
"indexBackend": "search",
"keys": [
{
"propertyKey": "stringProperty",
"parameters": [
{
"key": "string-analyzer",
"value": "standard",
"parser": "string"
},
{
"key": "mapping",
"value": "org.janusgraph.core.schema.Mapping.STRING",
"parser": "enum"
}
]
}
]
},
{
"name": "mixedIndexWithCustomParameterKeyAndParserFullClassPath",
"indexOnly": "unidirectedMultiEdge",
"typeClass": "org.apache.tinkerpop.gremlin.structure.Edge",
"indexBackend": "search",
"keys": [
{
"propertyKey": "anotherStringProperty",
"parameters": [
{
"key": "%`custom%`similarity",
"value": "boolean",
"parser": "string"
},
{
"key": "mapping",
"value": "org.janusgraph.core.schema.Mapping.TEXTSTRING",
"parser": "org.janusgraph.core.schema.json.parser.EnumJsonParameterParser"
}
]
}
]
}
]
}
JSON Schema 初始化流程
目前,JSON schema 初始化流程很简单,不包括任何 schema 更新或 schema 迁移功能。流程分为多个阶段
- 创建简单元素:
PropertyKey、VertexLabel、EdgeLabel。 - 创建索引:复合索引、顶点中心边索引、顶点中心属性索引、混合索引。
- 索引激活阶段(通过
schema.init.json.indices-activation配置)。
注意
JSON schema 导入器的当前实现只创建元素,但即使您更改属性、边、顶点或索引的定义,它也从不更新它们。如果存在具有此类名称的元素,schema 导入器将跳过它而不对元素进行任何更新。此外,它从不从图中删除任何现有的 schema 元素。对于 schema 迁移和删除过程,请直接使用 graph.openManagement()。
由于集群中创建了僵尸 JanusGraph 实例而难以初始化 schema 的用户可以利用配置选项 schema.init.json.force-close-other-instances。此选项将自动关闭集群中的所有 JanusGraph 实例(包括活动实例)。
警告
当使用 schema.init.json.force-close-other-instances 时,JanusGraph 将强制关闭集群中的任何其他实例。但是,它们可能不知道这一点,并继续在集群中工作而没有收到任何 schema 更新。这可能导致脑裂问题并损坏当前数据。建议改用 graph.unique-instance-id 和 graph.replace-instance-if-exists 选项,以防止创建 JanusGraph 僵尸实例。
JSON Schema 定义 API
也可以手动触发 JSON schema 定义 API,而不是依赖启动 schema 初始化过程。JSON schema 初始化的所有帮助方法都位于 org.janusgraph.core.schema.JsonSchemaInitStrategy 中。
// Schema from file
JsonSchemaInitStrategy.initializeSchemaFromFile(graph, "/path/to/schema.json")
// Schema from string
JsonSchemaInitStrategy.initializeSchemaFromString(graph, "{ \"vertexLabels\": [ { \"label\": \"my_vertex\" } ] }")
此外,还有额外的 initializeSchemaFromFile 和 initializeSchemaFromString 方法,可以在其中直接提供所有配置选项,而不是从图的配置中获取它们
initializeSchemaFromFile(JanusGraph graph, boolean createSchemaElements, boolean createSchemaIndices, IndicesActivationType indicesActivationType, boolean forceRollBackActiveTransactions, boolean forceCloseOtherInstances, long indexStatusTimeout, String jsonSchemaFilePath)initializeSchemaFromString(JanusGraph graph, boolean createSchemaElements, boolean createSchemaIndices, IndicesActivationType indicesActivationType, boolean forceRollBackActiveTransactions, boolean forceCloseOtherInstances, long indexStatusTimeout, String jsonSchemaString)