diff --git a/docs/changelog.md b/docs/changelog.md
index 202809fac2..ba7b0b63f5 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -105,15 +105,14 @@ For more information on features and bug fixes in 1.1.0, see the GitHub mileston
Inlining vertex properties into a Composite Index structure can offer significant performance and efficiency benefits.
See [documentation](./schema/index-management/index-performance.md#inlining-vertex-properties-into-a-composite-index) on how to inline vertex properties into a composite index.
-**Important Notes on Compatibility**
-
-1. **Backward Incompatibility**
- Once a JanusGraph instance adopts this new schema feature, it cannot be rolled back to a prior version of JanusGraph.
- The changes in the schema structure are not compatible with earlier versions of the system.
-
-2. **Migration Considerations**
- It is critical that users carefully plan their migration to this new version, as there is no automated or manual rollback process
- to revert to an older version of JanusGraph once this feature is used.
+!!! warning
+ Important Notes on Compatibility.
+ 1. Backward Incompatibility:
+ Once a JanusGraph instance adopts this new schema feature, it cannot be rolled back to a prior version of JanusGraph.
+ The changes in the schema structure are not compatible with earlier versions of the system.
+ 2. Migration Considerations:
+ It is critical that users carefully plan their migration to this new version, as there is no automated or manual rollback process
+ to revert to an older version of JanusGraph once this feature is used.
##### BerkeleyJE ability to overwrite arbitrary settings applied at `EnvironmentConfig` creation
@@ -124,6 +123,11 @@ All configurations values should be specified as `String` and be formated the sa
[documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html).
Example: `storage.berkeleyje.ext.je.lock.timeout=5000 ms`
+##### JSON schema initializer
+
+For simplicity JSON schema initialization options has been added into JanusGraph.
+See [documentation](./schema/schema-init-strategies.md) to learn more about JSON schema initialization process.
+
### Version 1.0.1 (Release Date: ???)
/// tab | Maven
diff --git a/docs/configs/janusgraph-cfg.md b/docs/configs/janusgraph-cfg.md
index 6d6eafc7f8..1e24ff6398 100644
--- a/docs/configs/janusgraph-cfg.md
+++ b/docs/configs/janusgraph-cfg.md
@@ -384,6 +384,29 @@ Schema related configuration options
| schema.default | Configures the DefaultSchemaMaker to be used by this graph. Either one of the following shorthands can be used:
- `default` (a blueprints compatible schema maker with MULTI edge labels and SINGLE property keys),
- `tp3` (same as default, but has LIST property keys),
- `none` (automatic schema creation is disabled)
- `ignore-prop` (same as none, but simply ignore unknown properties rather than throw exceptions)
- or to the full package and classname of a custom/third-party implementing the interface `org.janusgraph.core.schema.DefaultSchemaMaker` | String | default | MASKABLE |
| schema.logging | Controls whether logging is enabled for schema makers. This only takes effect if you set `schema.default` to `default` or `ignore-prop`. For `default` schema maker, warning messages will be logged before schema types are created automatically. For `ignore-prop` schema maker, warning messages will be logged before unknown properties are ignored. | Boolean | false | MASKABLE |
+### schema.init
+Configuration options for schema initialization on startup.
+
+
+| Name | Description | Datatype | Default Value | Mutability |
+| ---- | ---- | ---- | ---- | ---- |
+| schema.init.drop-before-startup | Drops the entire schema with graph data before JanusGraph schema initialization. Note that the schema will be dropped regardless of the selected initialization strategy, including when `schema.init.strategy` is set to `none`. | Boolean | false | LOCAL |
+| schema.init.strategy | Specifies the strategy for schema initialization before starting JanusGraph. You must provide the full class path of a class that implements the `SchemaInitStrategy` interface and has parameterless constructor.
The following shortcuts are also available:
- `none` - Skips schema initialization.
- `json` - Schema initialization via provided JSON file or JSON string.
| String | none | LOCAL |
+
+### schema.init.json
+Options for JSON schema initialization strategy.
+
+
+| Name | Description | Datatype | Default Value | Mutability |
+| ---- | ---- | ---- | ---- | ---- |
+| schema.init.json.await-index-status-timeout | Timeout for awaiting index status operation defined in milliseconds. If the status await timeouts the exception will be thrown during schema initialization process. | Long | 180000 | LOCAL |
+| schema.init.json.file | File path to JSON formated schema definition. | String | (no default value) | LOCAL |
+| schema.init.json.force-close-other-instances | Force closes other JanusGraph instances before schema initialization, regardless if they are active or not. This is a dangerous operation. This option exists to help people initialize schema who struggle with zombie JanusGraph instances. It's not recommended to be used unless you know what you are doing. Instead of this parameter, it's recommended to check `graph.unique-instance-id` and `graph.replace-instance-if-exists` options to not create zombie instances in the cluster. | Boolean | false | LOCAL |
+| schema.init.json.indices-activation | Indices activation type:
- `reindex_and_enable_updated_only` - Reindex process will be triggered for any updated index. After this all updated indexes will be enabled.
- `reindex_and_enable_non_enabled` - Reindex process will be triggered for any index which is not enabled (including previously created indices). After reindexing all indices will be enabled.
- `skip_activation` - Skip reindex process for any updated indexes.
- `force_enable_updated_only` - Force enable all updated indexes without running any reindex process (previous data may not be available for such indices).
- `force_enable_non_enabled` - Force enable all indexes (including previously created indices) without running any reindex process (previous data may not be available for such indices).
| String | reindex_and_enable_non_enabled | LOCAL |
+| schema.init.json.skip-elements | Skip creation of VertexLabel, EdgeLabel, and PropertyKey. | Boolean | false | LOCAL |
+| schema.init.json.skip-indices | Skip creation of indices. | Boolean | false | LOCAL |
+| schema.init.json.string | JSON formated schema definition string. This option takes precedence if both `file` and `string` are used. | String | (no default value) | LOCAL |
+
### storage
Configuration options for the storage backend. Some options are applicable only for certain backends.
diff --git a/docs/schema/schema-init-strategies.md b/docs/schema/schema-init-strategies.md
new file mode 100644
index 0000000000..adcf097df5
--- /dev/null
+++ b/docs/schema/schema-init-strategies.md
@@ -0,0 +1,434 @@
+# Schema Initialization Strategies
+
+Besides the ability to provide groovy code to JanusGraph server to initialize
+schema or execute any custom logic, JanusGraph provides ability to execute
+custom schema initialization strategies on startup.
+These strategies are made to help users define and maintain their schema easier,
+but they are not replacement to custom schema management processes which can
+be developed by using `JanusGraphManagement` directly.
+
+Each time JanusGraph instance is started via `JanusGraphFactory` or `ConfiguredGraphFactory`,
+a schema initialization strategy gets executed before startup.
+Schema initialization strategy can be chosen via `schema.init.strategy` configuration parameter.
+By default `none` is selected, meaning schema initialization process is skipped.
+
+Parameter `schema.init.schema-drop-before-startup` can be used to configure
+schema and data removal on startup (can be convenient in testing environments).
+
+## JSON Schema Initialization
+
+When `json` configuration option is selected in `schema.init.strategy` it
+triggers schema initialization via JSON formated schema definition which
+can be provided via file or string.
+
+Configurations for this strategy are provided under `schema.init.json` namespace.
+The easiest way to set up JSON schema initialization it by providing JSON schema
+file path to `schema.init.json.file` or directly insert JSON schema into
+`schema.init.json.string` configuration option.
+
+### JSON Schema Format
+
+Schema defined via JSON must be a deserialized JSON version of
+`org.janusgraph.core.schema.json.definition.JsonSchemaDefinition` class where top
+level object elements will be the following:
+- `vertexLabels`
+- `edgeLabels`
+- `propertyKeys`
+- `compositeIndexes`
+- `vertexCentricEdgeIndexes`
+- `vertexCentricPropertyIndexes`
+- `mixedIndexes`
+
+Each value is an array representing a list of vertex labels, edges labels, property
+keys, composite indexes, vertex-centric edge indexes, vertex-centric property indexes,
+or mixed indexes.
+
+#### JSON Vertex Label Definition
+
+Each vertex label object consists of the following keys:
+- `label` - `string` datatype. (**Required**)
+- `staticVertex` - `boolean` datatype.
+- `partition` - `boolean` datatype.
+- `ttl` - `number` datatype.
+
+#### JSON Edge Label Definition
+
+Each edge label object consists of the following keys:
+- `label` - `string` datatype. (**Required**)
+- `multiplicity` - `string` datatype. Allowed values: `MULTI`, `SIMPLE`, `ONE2MANY`, `MANY2ONE`, `ONE2ONE`.
+- `unidirected` - `boolean` datatype.
+- `ttl` - `number` datatype.
+
+#### JSON Property Key Definition
+
+Each property key object consists of the following keys:
+- `key` - `string` datatype. (**Required**)
+- `className` - `string` datatype. This must be a full class path of the selected property datatype. For example, `java.lang.String`, `java.lang.Long`,`org.janusgraph.core.attribute.Geoshape`. (**Required**)
+- `cardinality` - `string` datatype. Allowed values: `SINGLE`, `LIST`, `SET`.
+- `ttl` - `number` datatype.
+
+#### JSON Composite Index Definition
+
+Each composite index object consists of the following keys:
+- `name` - `string` datatype. (**Required**)
+- `typeClass` - `string` datatype. Full class path of the datatype of an indexed element. Allowed values: `org.apache.tinkerpop.gremlin.structure.Vertex`, `org.apache.tinkerpop.gremlin.structure.Edge`. (**Required**)
+- `indexOnly` - `string` datatype. Vertex or Edge label.
+- `unique` - `boolean` datatype.
+- `consistency` - `string` datatype. Allowed values: `DEFAULT`, `LOCK`, `FORK`.
+- `keys` - an array of objects representing `org.janusgraph.core.schema.json.definition.index.JsonIndexedPropertyKeyDefinition` (see definition below after indexes definition). These are used index keys. (**Required**)
+- `inlinePropertyKeys` - an array of property keys to be inlined into the composite index. Currently supported for vertex composite indexes only. See [documentation](./schema/index-management/index-performance.md#inlining-vertex-properties-into-a-composite-index) for more information about this feature.
+
+#### JSON Mixed Index Definition
+
+Each mixed index object consists of the following keys:
+- `name` - `string` datatype. (**Required**)
+- `typeClass` - `string` datatype. Full class path of the datatype of an indexed element. Allowed values: `org.apache.tinkerpop.gremlin.structure.Vertex`, `org.apache.tinkerpop.gremlin.structure.Edge`. (**Required**)
+- `indexOnly` - `string` datatype. Vertex or Edge label.
+- `indexBackend` - `string` datatype. Name for index backend configuration. (**Required**)
+- `keys` - an array of objects representing `org.janusgraph.core.schema.json.definition.index.JsonIndexedPropertyKeyDefinition` (see definition below after indexes definition). These are used index keys. (**Required**)
+
+#### JSON Vertex-Centric Edge Index Definition
+
+Each vertex-centric edge index object consists of the following keys:
+- `name` - `string` datatype. (**Required**)
+- `propertyKeys` - an array of strings (each value has `string` datatype). These are the Edge properties which will be used for the index. (**Required**)
+- `order` - `string` datatype. Allowed values: `asc`, `desc`.
+- `indexedEdgeLabel` - `string` datatype. Edge label to be indexed. (**Required**)
+- `direction` - `string` datatype. Allowed values: `OUT`, `IN`, `BOTH`.
+
+#### JSON Vertex-Centric Property Index Definition
+
+Each vertex-centric property index object consists of the following keys:
+- `name` - `string` datatype. (**Required**)
+- `propertyKeys` - an array of strings (each value has `string` datatype). These are the meta-properties which will be used for the index. (**Required**)
+- `order` - `string` datatype. Allowed values: `asc`, `desc`.
+- `indexedPropertyKey` - `string` datatype. Property key to be indexed. (**Required**)
+
+#### JSON Definition of Property Keys defined in Composite and Mixed indexes (`keys`)
+
+Each property key object defined as `keys` representation of composite or mixed index consists of the following keys:
+- `propertyKey` - `string` datatype. (**Required**)
+- `parameters` - an array of objects representing `org.janusgraph.core.schema.json.definition.JsonParameterDefinition`. These are optional parameters to let index know of additional configurations for the property key. (See description below)
+
+#### JSON Definition of Parameters defined in Property Keys for Composite and Mixed indexes (`parameters`)
+
+Each parameter is a configuration for to let underlying index backend configure relative properties better. Each such parameter consists of the following keys:
+- `key` - `string` datatype. (**Required**)
+- `value` - `string` datatype. (**Required**)
+- `parser` - `string` datatype. This must be a full class pass of the parser which will be used to parse `value` of the parameter or a pre-defined shortcut. This parser must implement `org.janusgraph.core.schema.json.parser.JsonParameterParser` interface and have a parameterless constructor. If none is provided then `string` parser is used by default.
+
+Pre-defined `parser` shortcuts:
+- `string` - doesn't change `value` and uses it as is (`String` datatype).
+- `enum` - replaces the provided string (defined as `.`) to actual enum value.
+For example, if `value` has a string `org.janusgraph.core.schema.Mapping.STRING` it will be replaced to actual `STRING` enum,
+and it won't be treated as a string.
+- `boolean` - parses value to `Boolean`.
+- `byte` - parses value to `Byte`.
+- `short` - parses value to `Short`.
+- `integer` - parses value to `Integer`.
+- `long` - parses value to `Long`.
+- `float` - parses value to `Float`.
+- `double` - parses value to `Double`.
+
+Parameters may include [custom keys](../index-backend/field-mapping.md#custom-parameters) defined as `ParameterType.customParameterName("")`.
+To define such keys in JSON it's necessary to use specific prefix for `key` - %\`custom%\` (in total the prefix consists of 10 characters.
+In case some characters are not rendered correctly here, you can always reference to `org.janusgraph.graphdb.types.ParameterType.CUSTOM_PARAMETER_PREFIX`
+to find out those 10 prefix characters).
+As such, to define custom keys like `similarity` you should write your key as: %\`custom%\`similarity
+
+### JSON Schema definition example
+
+Following the rules above (defined in `JSON Schema Format`) an example of JSON schema definition could look like the one below.
+
+!!! note
+ Below schema is valid and tested using Cassandra and ElasticSearch. It uses some of the parameters related to
+ either eventual consistent databases and ElasticSearch in particular. Thus, some of the database specific schema definitions
+ may not work properly with other databases (like `similarity` or `string-analyzer`).
+ The schema below is used for demonstration purposes only.
+
+```json
+{
+ "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 initialization flow
+
+Currently, JSON schema initialization flow is simple and doesn't include any schema update or schema migration features.
+The flow is split on multiple phases:
+1) Creation of simple elements: `PropertyKey`, `VertexLabel`, `EdgeLabel`.
+2) Creation of indices: Composite indexes, Vertex-centric Edge indexes, Vertex-centric Property indexes, Mixed indexes.
+3) Indexes activation phase (configured via `schema.init.json.indices-activation`).
+
+!!! note
+ Current implementation of JSON schema importer only creates elements, but it never updates them even if you change
+ definition of your property, edge, vertex, or index. If an element with such name exists - schema importer will skip it
+ without doing any updates on the element. Also, it never deletes any existing schema elements from the graph.
+ For schema migration and removal processes use `graph.openManagement()` directly.
+
+Users who struggle to initialize schema due to created zombie JanusGraph instances in the cluster may leverage
+configuration option `schema.init.json.force-close-other-instances`. This option will automatically close all
+JanusGraph instance in the cluster (including activate instances).
+
+!!! warning
+ When using `schema.init.json.force-close-other-instances` JanusGraph will force-close any other instances in
+ the cluster. However, they may not know about it and continue to work in the cluster without receiving any schema
+ updates. This could lead to split brain problem and corrupt current data.
+ It's advised to use `graph.unique-instance-id` and `graph.replace-instance-if-exists` options instead to prevent
+ creation of JanusGraph zombie instances.
+
+### JSON Schema Definition API
+
+It's also possible to manually trigger JSON schema definition API instead of relaying on startup schema
+initialization process. All helper methods for JSON schema initialization are located in
+`org.janusgraph.core.schema.JsonSchemaInitStrategy`.
+
+```groovy
+// Schema from file
+JsonSchemaInitStrategy.initializeSchemaFromFile(graph, "/path/to/schema.json")
+
+// Schema from string
+JsonSchemaInitStrategy.initializeSchemaFromString(graph, "{ \"vertexLabels\": [ { \"label\": \"my_vertex\" } ] }")
+```
+
+Also, there are additional `initializeSchemaFromFile` and `initializeSchemaFromString` methods where it's possible to
+provide all configuration options directly instead of fetching them from the configuration of the graph:
+- `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)`
diff --git a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java
index efff716143..a69af6d185 100644
--- a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java
+++ b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java
@@ -19,6 +19,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import org.apache.commons.configuration2.MapConfiguration;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.T;
@@ -207,7 +208,7 @@ public static Map validateConfigOptions(Object... setti
return options;
}
- public void clopen(Object... settings) {
+ public void setupConfig(Object... settings){
config = getConfiguration();
if (mgmt!=null && mgmt.isOpen()) mgmt.rollback();
if (null != tx && tx.isOpen()) tx.commit();
@@ -226,6 +227,10 @@ public void clopen(Object... settings) {
if (janusGraphManagement!=null) janusGraphManagement.commit();
modifiableConfiguration.close();
}
+ }
+
+ public void clopen(Object... settings) {
+ setupConfig(settings);
if (null != graph && null != graph.tx() && graph.tx().isOpen())
graph.tx().commit();
if (null != graph && graph.isOpen())
@@ -234,6 +239,29 @@ public void clopen(Object... settings) {
open(config);
}
+ public void clopenNoDelimiterUsage(Map extraConfigs, boolean clearGraph) {
+ this.config = getConfiguration();
+ if (null != graph && null != graph.tx() && graph.tx().isOpen())
+ graph.tx().commit();
+ if (null != graph && graph.isOpen())
+ graph.close();
+ if(clearGraph) {
+ try {
+ clearGraph(config);
+ } catch (BackendException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ readConfig = new BasicConfiguration(GraphDatabaseConfiguration.ROOT_NS, config, BasicConfiguration.Restriction.NONE);
+ Map finalConfig = new HashMap<>();
+ readConfig.getAll().forEach((pathIdentifier, value) -> finalConfig.put(pathIdentifier.element.toStringWithoutRoot(), value));
+ finalConfig.putAll(extraConfigs);
+
+ graph = (StandardJanusGraph) JanusGraphFactory.open(new MapConfiguration(finalConfig));
+ features = graph.getConfiguration().getStoreFeatures();
+ tx = graph.newTransaction();
+ mgmt = graph.openManagement();
+ }
public static TestConfigOption option(ConfigOption option, String... umbrella) {
return new TestConfigOption(option,umbrella);
diff --git a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java
index b604fe4ad2..c3e66e83e4 100644
--- a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java
+++ b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java
@@ -22,6 +22,7 @@
import com.google.common.collect.Sets;
import io.github.artsok.RepeatedIfExceptionsTest;
import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.NotImplementedException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
@@ -70,6 +71,7 @@
import org.janusgraph.core.schema.ConsistencyModifier;
import org.janusgraph.core.schema.DisableDefaultSchemaMaker;
import org.janusgraph.core.schema.IgnorePropertySchemaMaker;
+import org.janusgraph.core.schema.IndicesActivationType;
import org.janusgraph.core.schema.JanusGraphDefaultSchemaMaker;
import org.janusgraph.core.schema.JanusGraphIndex;
import org.janusgraph.core.schema.JanusGraphManagement;
@@ -77,6 +79,7 @@
import org.janusgraph.core.schema.Mapping;
import org.janusgraph.core.schema.RelationTypeIndex;
import org.janusgraph.core.schema.SchemaAction;
+import org.janusgraph.core.schema.SchemaInitType;
import org.janusgraph.core.schema.SchemaStatus;
import org.janusgraph.core.util.ManagementUtil;
import org.janusgraph.diskstorage.Backend;
@@ -169,6 +172,9 @@
import org.slf4j.LoggerFactory;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
@@ -234,6 +240,10 @@
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.REPEAT_STEP_BATCH_MODE;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.REPLACE_INSTANCE_IF_EXISTS;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_CONSTRAINTS;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_DROP_BEFORE_INIT;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_STR;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_STRATEGY;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCRIPT_EVAL_ENABLED;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCRIPT_EVAL_ENGINE;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.STORAGE_BACKEND;
@@ -4521,6 +4531,29 @@ private boolean isSortedByID(VertexList vl) {
return true;
}
+ @Test
+ public void testSimpleJsonSchemaImportFromProperty() throws IOException, BackendException {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ InputStream resourceStream = loader.getResourceAsStream("test_simple_schema_example.json");
+ String jsonSchemaExample = IOUtils.toString(resourceStream, StandardCharsets.UTF_8.name());
+
+ Map extraConfig = new HashMap<>();
+ extraConfig.put(SCHEMA_INIT_STRATEGY.toStringWithoutRoot(), SchemaInitType.JSON.getConfigName());
+ extraConfig.put(SCHEMA_DROP_BEFORE_INIT.toStringWithoutRoot(), true);
+ extraConfig.put(SCHEMA_INIT_JSON_STR.toStringWithoutRoot(), jsonSchemaExample);
+ extraConfig.put(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE.toStringWithoutRoot(), IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.getConfigName());
+ clopenNoDelimiterUsage(extraConfig, true);
+
+ assertEquals(Long.class, mgmt.getPropertyKey("time").dataType());
+ assertEquals(Double.class, mgmt.getPropertyKey("doubleProp").dataType());
+ assertEquals(Cardinality.LIST, mgmt.getPropertyKey("longPropCardinalityList").cardinality());
+ assertEquals("organization", mgmt.getVertexLabel("organization").name());
+ assertEquals(Multiplicity.SIMPLE, mgmt.getEdgeLabel("connects").multiplicity());
+ assertTrue(mgmt.getEdgeLabel("connects").isDirected());
+ assertEquals(Multiplicity.MULTI, mgmt.getEdgeLabel("viewed").multiplicity());
+ assertTrue(mgmt.getEdgeLabel("viewed").isUnidirected());
+ }
+
@Test
public void testEdgesExceedCacheSize() {
// Add a vertex with as many edges as the tx-cache-size. (20000 by default)
diff --git a/janusgraph-backend-testutils/src/main/resources/test_simple_schema_example.json b/janusgraph-backend-testutils/src/main/resources/test_simple_schema_example.json
new file mode 100644
index 0000000000..08fac5a325
--- /dev/null
+++ b/janusgraph-backend-testutils/src/main/resources/test_simple_schema_example.json
@@ -0,0 +1,48 @@
+{
+ "vertexLabels": [
+ {
+ "label": "organization"
+ },
+ {
+ "label": "user"
+ }
+ ],
+
+ "edgeLabels": [
+ {
+ "label": "connects",
+ "multiplicity": "SIMPLE",
+ "unidirected": false
+ },
+ {
+ "label": "viewed",
+ "multiplicity": "MULTI",
+ "unidirected": true
+ }
+ ],
+
+ "propertyKeys": [
+ {
+ "key": "time",
+ "className": "java.lang.Long"
+ },
+ {
+ "key": "doubleProp",
+ "className": "java.lang.Double"
+ },
+ {
+ "key": "integerProp",
+ "className": "java.lang.Integer"
+ },
+ {
+ "key": "longPropCardinalityList",
+ "className": "java.lang.Long",
+ "cardinality": "LIST"
+ },
+ {
+ "key": "name",
+ "className": "java.lang.String",
+ "cardinality": "SINGLE"
+ }
+ ]
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/ConfiguredGraphFactory.java b/janusgraph-core/src/main/java/org/janusgraph/core/ConfiguredGraphFactory.java
index b0e7b25b71..b25d1338f1 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/core/ConfiguredGraphFactory.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/ConfiguredGraphFactory.java
@@ -18,9 +18,9 @@
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.MapConfiguration;
import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.janusgraph.core.util.GraphFactoryUtils;
import org.janusgraph.diskstorage.BackendException;
import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration;
-import org.janusgraph.graphdb.configuration.builder.GraphDatabaseConfigurationBuilder;
import org.janusgraph.graphdb.database.StandardJanusGraph;
import org.janusgraph.graphdb.database.management.ManagementSystem;
import org.janusgraph.graphdb.management.ConfigurationManagementGraph;
@@ -87,7 +87,7 @@ public static synchronized JanusGraph create(final String graphName) {
final JanusGraphManager jgm = JanusGraphManagerUtility.getInstance();
Preconditions.checkNotNull(jgm, JANUS_GRAPH_MANAGER_EXPECTED_STATE_MSG);
final CommonsConfiguration config = new CommonsConfiguration(ConfigurationUtil.loadMapConfiguration(templateConfigMap));
- final JanusGraph g = (JanusGraph) jgm.openGraph(graphName, (String gName) -> new StandardJanusGraph(new GraphDatabaseConfigurationBuilder().build(config)));
+ final JanusGraph g = (JanusGraph) jgm.openGraph(graphName, (String gName) -> GraphFactoryUtils.defineSchemaAndStart((config)));
configManagementGraph.createConfiguration(new MapConfiguration(templateConfigMap));
return g;
}
@@ -112,7 +112,7 @@ public static JanusGraph open(String graphName) {
final JanusGraphManager jgm = JanusGraphManagerUtility.getInstance();
Preconditions.checkNotNull(jgm, JANUS_GRAPH_MANAGER_EXPECTED_STATE_MSG);
final CommonsConfiguration config = new CommonsConfiguration(ConfigurationUtil.loadMapConfiguration(graphConfigMap));
- return (JanusGraph) jgm.openGraph(graphName, (String gName) -> new StandardJanusGraph(new GraphDatabaseConfigurationBuilder().build(config)));
+ return (JanusGraph) jgm.openGraph(graphName, (String gName) -> GraphFactoryUtils.defineSchemaAndStart(config));
}
/**
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphFactory.java b/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphFactory.java
index 39545dbc38..3ab3c04003 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphFactory.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphFactory.java
@@ -23,6 +23,7 @@
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.janusgraph.core.log.LogProcessorFramework;
import org.janusgraph.core.log.TransactionRecovery;
+import org.janusgraph.core.util.GraphFactoryUtils;
import org.janusgraph.diskstorage.Backend;
import org.janusgraph.diskstorage.BackendException;
import org.janusgraph.diskstorage.StandardStoreManager;
@@ -33,7 +34,6 @@
import org.janusgraph.diskstorage.configuration.WriteConfiguration;
import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration;
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
-import org.janusgraph.graphdb.configuration.builder.GraphDatabaseConfigurationBuilder;
import org.janusgraph.graphdb.database.StandardJanusGraph;
import org.janusgraph.graphdb.log.StandardLogProcessorFramework;
import org.janusgraph.graphdb.log.StandardTransactionLogProcessor;
@@ -162,7 +162,7 @@ public static JanusGraph open(ReadConfiguration configuration, String backupName
final JanusGraphManager jgm = JanusGraphManagerUtility.getInstance();
if (null != graphName) {
Preconditions.checkNotNull(jgm, JANUS_GRAPH_MANAGER_EXPECTED_STATE_MSG);
- return (JanusGraph) jgm.openGraph(graphName, gName -> new StandardJanusGraph(new GraphDatabaseConfigurationBuilder().build(configuration)));
+ return (JanusGraph) jgm.openGraph(graphName, gName -> GraphFactoryUtils.defineSchemaAndStart(configuration));
} else {
if (jgm != null) {
log.warn("You should supply \"graph.graphname\" in your .properties file configuration if you are opening " +
@@ -173,7 +173,7 @@ public static JanusGraph open(ReadConfiguration configuration, String backupName
"\"graph.graphname\" so these graphs should be accessed dynamically by supplying a .properties file here " +
"or by using the ConfiguredGraphFactory.");
}
- return new StandardJanusGraph(new GraphDatabaseConfigurationBuilder().build(configuration));
+ return GraphFactoryUtils.defineSchemaAndStart(configuration);
}
}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/IndicesActivationType.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/IndicesActivationType.java
new file mode 100644
index 0000000000..86a2d7768a
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/IndicesActivationType.java
@@ -0,0 +1,36 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.janusgraph.graphdb.configuration.ConfigName;
+
+public enum IndicesActivationType implements ConfigName {
+ SKIP_ACTIVATION("skip_activation"),
+ REINDEX_AND_ENABLE_UPDATED_ONLY("reindex_and_enable_updated_only"),
+ REINDEX_AND_ENABLE_NON_ENABLED("reindex_and_enable_non_enabled"),
+ FORCE_ENABLE_UPDATED_ONLY("force_enable_updated_only"),
+ FORCE_ENABLE_NON_ENABLED("force_enable_non_enabled");
+
+ private final String configurationOptionName;
+
+ IndicesActivationType(String configurationOptionName){
+ this.configurationOptionName = configurationOptionName;
+ }
+
+ @Override
+ public String getConfigName() {
+ return configurationOptionName;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/JsonSchemaInitStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/JsonSchemaInitStrategy.java
new file mode 100644
index 0000000000..fc6c7851f6
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/JsonSchemaInitStrategy.java
@@ -0,0 +1,265 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.apache.commons.lang3.StringUtils;
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.Namifiable;
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.RelationType;
+import org.janusgraph.core.schema.json.creator.GeneralJsonSchemaCreator;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.creator.SchemaCreationException;
+import org.janusgraph.core.schema.json.definition.JsonSchemaDefinition;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonGraphCentricIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonVertexCentricIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricEdgeIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricPropertyIndexDefinition;
+import org.janusgraph.core.util.JsonUtil;
+import org.janusgraph.core.util.ManagementUtil;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+import org.janusgraph.graphdb.database.StandardJanusGraph;
+import org.janusgraph.graphdb.database.management.ManagementSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class JsonSchemaInitStrategy implements SchemaInitStrategy{
+
+ private static final Logger LOG = LoggerFactory.getLogger(JsonSchemaInitStrategy.class);
+
+ @Override
+ public JanusGraph initializeSchemaAndStart(GraphDatabaseConfiguration graphDatabaseConfiguration) {
+
+ JanusGraph graph = new StandardJanusGraph(graphDatabaseConfiguration);
+
+ try{
+ if(StringUtils.isNotEmpty(graphDatabaseConfiguration.getSchemaInitJsonString())) {
+
+ initializeSchemaFromString(graph, graphDatabaseConfiguration.getSchemaInitJsonString());
+
+ } else if(StringUtils.isNotEmpty(graphDatabaseConfiguration.getSchemaInitJsonFile())){
+
+ initializeSchemaFromFile(graph, graphDatabaseConfiguration.getSchemaInitJsonFile());
+
+ } else {
+ throw new SchemaCreationException("When JSON schema initialization is enabled it's necessary to provide either JSON file or string for schema initialization.");
+ }
+ } catch (Throwable throwable){
+ graph.close();
+ if(throwable instanceof SchemaCreationException){
+ throw (SchemaCreationException)throwable;
+ }
+ throw new SchemaCreationException(throwable);
+ }
+
+ return graph;
+ }
+
+ public static void initializeSchemaFromFile(JanusGraph graph, String jsonSchemaFilePath) {
+ GraphDatabaseConfiguration graphDatabaseConfiguration = ((StandardJanusGraph) graph).getConfiguration();
+ IndicesActivationType indicesActivationType = graphDatabaseConfiguration.getSchemaInitJsonIndicesActivationType();
+ boolean createSchemaElements = !graphDatabaseConfiguration.getSchemaInitJsonSkipElements();
+ boolean createSchemaIndices = !graphDatabaseConfiguration.getSchemaInitJsonSkipIndices();
+ long indexStatusTimeout = graphDatabaseConfiguration.getSchemaInitJsonIndexStatusAwaitTimeout();
+
+ initializeSchemaFromFile(graph, createSchemaElements, createSchemaIndices, indicesActivationType, true,
+ graphDatabaseConfiguration.getSchemaInitJsonForceCloseOtherInstances(), indexStatusTimeout, jsonSchemaFilePath);
+ }
+
+ public static void initializeSchemaFromString(JanusGraph graph, String jsonSchemaString) {
+ GraphDatabaseConfiguration graphDatabaseConfiguration = ((StandardJanusGraph) graph).getConfiguration();
+ IndicesActivationType indicesActivationType = graphDatabaseConfiguration.getSchemaInitJsonIndicesActivationType();
+ boolean createSchemaElements = !graphDatabaseConfiguration.getSchemaInitJsonSkipElements();
+ boolean createSchemaIndices = !graphDatabaseConfiguration.getSchemaInitJsonSkipIndices();
+ long indexStatusTimeout = graphDatabaseConfiguration.getSchemaInitJsonIndexStatusAwaitTimeout();
+
+ initializeSchemaFromString(graph, createSchemaElements, createSchemaIndices, indicesActivationType, true,
+ graphDatabaseConfiguration.getSchemaInitJsonForceCloseOtherInstances(), indexStatusTimeout, jsonSchemaString);
+ }
+
+ public static void initializeSchemaFromFile(JanusGraph graph, boolean createSchemaElements, boolean createSchemaIndices,
+ IndicesActivationType indicesActivationType,
+ boolean forceRollBackActiveTransactions, boolean forceCloseOtherInstances,
+ long indexStatusTimeout, String jsonSchemaFilePath) {
+
+ JsonSchemaDefinition generalDefinition;
+ try {
+ generalDefinition = JsonUtil.jsonFilePathToObject(jsonSchemaFilePath, JsonSchemaDefinition.class);
+ } catch (IOException e) {
+ throw new SchemaCreationException(e);
+ }
+
+ initializeSchema(graph, createSchemaElements, createSchemaIndices, indicesActivationType,
+ forceRollBackActiveTransactions, forceCloseOtherInstances, indexStatusTimeout, generalDefinition);
+ }
+
+ public static void initializeSchemaFromString(JanusGraph graph, boolean createSchemaElements, boolean createSchemaIndices,
+ IndicesActivationType indicesActivationType,
+ boolean forceRollBackActiveTransactions, boolean forceCloseOtherInstances,
+ long indexStatusTimeout, String jsonSchemaString) {
+
+ JsonSchemaDefinition generalDefinition;
+ try {
+ generalDefinition = JsonUtil.jsonStringToObject(jsonSchemaString, JsonSchemaDefinition.class);
+ } catch (IOException e) {
+ throw new SchemaCreationException(e);
+ }
+
+ initializeSchema(graph, createSchemaElements, createSchemaIndices, indicesActivationType,
+ forceRollBackActiveTransactions, forceCloseOtherInstances, indexStatusTimeout, generalDefinition);
+ }
+
+ public static void initializeSchema(JanusGraph graph, boolean createSchemaElements, boolean createSchemaIndices,
+ IndicesActivationType indicesActivationType,
+ boolean forceRollBackActiveTransactions, boolean forceCloseOtherInstances,
+ long indexStatusTimeout, JsonSchemaDefinition generalDefinition) {
+
+ LOG.info("Starting schema initialization.");
+
+ if(forceCloseOtherInstances){
+ ManagementUtil.forceCloseOtherInstances(graph);
+ }
+
+ if(forceRollBackActiveTransactions){
+ ManagementUtil.forceRollbackAllTransactions(graph);
+ }
+
+ final ManagementSystem schemaCreationMgmt = (ManagementSystem) graph.openManagement();
+ JsonSchemaCreationContext schemaCreationContext = new JsonSchemaCreationContext(
+ schemaCreationMgmt,
+ createSchemaElements,
+ createSchemaIndices);
+
+ GeneralJsonSchemaCreator generalJsonSchemaCreator = new GeneralJsonSchemaCreator();
+
+ // initialize schema
+ generalJsonSchemaCreator.create(generalDefinition, schemaCreationContext);
+
+ List updatedButNonEnabledIndicesNames = schemaCreationContext.getCreatedOrUpdatedIndices().stream()
+ .filter(indexDefinition -> !isIndexEnabled(indexDefinition, schemaCreationMgmt))
+ .map(AbstractJsonIndexDefinition::getName)
+ .collect(Collectors.toList());
+
+ schemaCreationMgmt.commit();
+
+ // process indices activation (re-index and enable)
+ processIndicesActivation(graph, updatedButNonEnabledIndicesNames, indicesActivationType, indexStatusTimeout);
+
+ LOG.info("Schema initialization completed.");
+ }
+
+ private static void processIndicesActivation(JanusGraph graph, List updatedButNonEnabledIndicesNames,
+ IndicesActivationType indicesActivationType, long indexStatusTimeout){
+
+ if(IndicesActivationType.SKIP_ACTIVATION.equals(indicesActivationType)){
+ return;
+ }
+
+ ManagementSystem schemaFetchMgmt = (ManagementSystem) graph.openManagement();
+
+ List nonEnabledGraphIndices = schemaFetchMgmt.getGraphIndices(SchemaStatus.ENABLED);
+ List nonEnabledRelationTypeIndices = schemaFetchMgmt.getVertexCentricIndices(SchemaStatus.ENABLED);
+
+ if(IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.equals(indicesActivationType) || IndicesActivationType.FORCE_ENABLE_UPDATED_ONLY.equals(indicesActivationType)){
+ List nonEnabledGraphIndicesCopy = nonEnabledGraphIndices;
+ List nonEnabledRelationTypeIndicesCopy = nonEnabledRelationTypeIndices;
+ nonEnabledGraphIndices = new ArrayList<>(nonEnabledGraphIndices.size());
+ nonEnabledRelationTypeIndices = new ArrayList<>(nonEnabledRelationTypeIndices.size());
+ for (JanusGraphIndex index : nonEnabledGraphIndicesCopy) {
+ if(updatedButNonEnabledIndicesNames.contains(index.name())){
+ nonEnabledGraphIndices.add(index);
+ }
+ }
+ for (RelationTypeIndex index : nonEnabledRelationTypeIndicesCopy) {
+ if(updatedButNonEnabledIndicesNames.contains(index.name())){
+ nonEnabledRelationTypeIndices.add(index);
+ }
+ }
+ }
+
+ List nonEnabledGraphIndexNames = nonEnabledGraphIndices.stream().map(JanusGraphIndex::name).collect(Collectors.toList());
+ Map nonEnabledRelationTypeIndexNamesAndTypes = nonEnabledRelationTypeIndices.stream().collect(Collectors.toMap(Namifiable::name, o -> o.getType().name()));
+
+ schemaFetchMgmt.rollback();
+
+ List nonEnabledIndexNames = Stream.concat(nonEnabledGraphIndexNames.stream(), nonEnabledRelationTypeIndexNamesAndTypes.keySet().stream()).collect(Collectors.toList());
+
+ if(!nonEnabledIndexNames.isEmpty()){
+
+ String nonEnabledIndexNamesJoined = String.join(", ", nonEnabledIndexNames);
+ if(!nonEnabledIndexNames.isEmpty() && LOG.isInfoEnabled()){
+ LOG.info("Found {} non-enabled indices. Awaiting for their status updates. Indexes [{}].",
+ nonEnabledIndexNames.size(), nonEnabledIndexNamesJoined);
+ }
+
+ if(IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.equals(indicesActivationType) || IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.equals(indicesActivationType)){
+
+ ManagementUtil.reindexAndEnableIndices(graph, nonEnabledGraphIndexNames, nonEnabledRelationTypeIndexNamesAndTypes, indexStatusTimeout);
+
+ } else if(IndicesActivationType.FORCE_ENABLE_UPDATED_ONLY.equals(indicesActivationType) || IndicesActivationType.FORCE_ENABLE_NON_ENABLED.equals(indicesActivationType)){
+
+ ManagementUtil.forceEnableIndices(graph, nonEnabledGraphIndexNames, nonEnabledRelationTypeIndexNamesAndTypes, indexStatusTimeout);
+
+ } else {
+ throw new IllegalStateException("Unknown indicesActivationType: " + indicesActivationType);
+ }
+ }
+ }
+
+ private static boolean isIndexEnabled(AbstractJsonIndexDefinition createdIndexesDefinition, JanusGraphManagement graphManagement){
+ return isIndexHasStatus(createdIndexesDefinition, graphManagement, SchemaStatus.ENABLED);
+ }
+
+ private static boolean isIndexHasStatus(AbstractJsonIndexDefinition createdIndexesDefinition, JanusGraphManagement graphManagement, SchemaStatus schemaStatus){
+ if(createdIndexesDefinition instanceof AbstractJsonVertexCentricIndexDefinition){
+
+ AbstractJsonVertexCentricIndexDefinition vertexCentricIndex =
+ (AbstractJsonVertexCentricIndexDefinition) createdIndexesDefinition;
+ String indexedElement;
+ if(vertexCentricIndex instanceof JsonVertexCentricEdgeIndexDefinition){
+ indexedElement = ((JsonVertexCentricEdgeIndexDefinition) vertexCentricIndex).getIndexedEdgeLabel();
+ } else if (vertexCentricIndex instanceof JsonVertexCentricPropertyIndexDefinition){
+ indexedElement = ((JsonVertexCentricPropertyIndexDefinition) vertexCentricIndex).getIndexedPropertyKey();
+ } else {
+ throw new SchemaCreationException("Unknown index type definition: "+createdIndexesDefinition.getClass().getName());
+ }
+ RelationType relationType = graphManagement.getRelationType(indexedElement);
+ RelationTypeIndex relationIndex = graphManagement.getRelationIndex(relationType, vertexCentricIndex.getName());
+ return schemaStatus.equals(relationIndex.getIndexStatus());
+
+ } else if (createdIndexesDefinition instanceof AbstractJsonGraphCentricIndexDefinition) {
+
+ JanusGraphIndex janusGraphIndex = graphManagement.getGraphIndex(createdIndexesDefinition.getName());
+ for(PropertyKey propertyKey : janusGraphIndex.getFieldKeys()){
+ if(!schemaStatus.equals(janusGraphIndex.getIndexStatus(propertyKey))){
+ return false;
+ }
+ }
+ return true;
+
+ } else {
+ throw new SchemaCreationException("Unknown index type definition: "+createdIndexesDefinition.getClass().getName());
+ }
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/NoneSchemaInitStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/NoneSchemaInitStrategy.java
new file mode 100644
index 0000000000..773029356c
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/NoneSchemaInitStrategy.java
@@ -0,0 +1,29 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+import org.janusgraph.graphdb.database.StandardJanusGraph;
+
+/**
+ * Skips any schema initialization on startup
+ */
+public class NoneSchemaInitStrategy implements SchemaInitStrategy{
+ @Override
+ public JanusGraph initializeSchemaAndStart(GraphDatabaseConfiguration graphDatabaseConfiguration) {
+ return new StandardJanusGraph(graphDatabaseConfiguration);
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitStrategy.java
new file mode 100644
index 0000000000..6059716bca
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitStrategy.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+
+public interface SchemaInitStrategy {
+
+ JanusGraph initializeSchemaAndStart(GraphDatabaseConfiguration graphDatabaseConfiguration);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitType.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitType.java
new file mode 100644
index 0000000000..29341176e0
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitType.java
@@ -0,0 +1,33 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.janusgraph.graphdb.configuration.ConfigName;
+
+public enum SchemaInitType implements ConfigName {
+ NONE("none"), // skips any schema initialization during startup
+ JSON("json"); // initializes schema using provided JSON file
+
+ private final String configurationOptionName;
+
+ SchemaInitType(String configurationOptionName){
+ this.configurationOptionName = configurationOptionName;
+ }
+
+ @Override
+ public String getConfigName() {
+ return configurationOptionName;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitializationManager.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitializationManager.java
new file mode 100644
index 0000000000..9d150a8ceb
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/SchemaInitializationManager.java
@@ -0,0 +1,78 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema;
+
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.JanusGraphFactory;
+import org.janusgraph.core.schema.json.creator.SchemaCreationException;
+import org.janusgraph.diskstorage.BackendException;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+import org.janusgraph.graphdb.database.StandardJanusGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SchemaInitializationManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SchemaInitializationManager.class);
+
+ public static final Map SCHEMA_INIT_STRATEGIES;
+
+ static {
+ SCHEMA_INIT_STRATEGIES = new HashMap<>(1);
+ SCHEMA_INIT_STRATEGIES.put(SchemaInitType.NONE.getConfigName(), new NoneSchemaInitStrategy());
+ SCHEMA_INIT_STRATEGIES.put(SchemaInitType.JSON.getConfigName(), new JsonSchemaInitStrategy());
+ }
+
+ public static JanusGraph initializeSchemaAndStart(GraphDatabaseConfiguration graphDatabaseConfiguration){
+
+ // drop schema before init is selected
+ if(graphDatabaseConfiguration.isDropSchemaBeforeInit()){
+ try {
+ LOG.info("Dropping current JanusGraph schema and data.");
+ JanusGraphFactory.drop(new StandardJanusGraph(graphDatabaseConfiguration));
+ LOG.info("Current JanusGraph schema and data were removed.");
+ } catch (BackendException e) {
+ throw new SchemaCreationException(e);
+ }
+ }
+
+ // get schema initialization strategy
+ String schemaInitStrategyPath = graphDatabaseConfiguration.getSchemaInitStrategy();
+ SchemaInitStrategy schemaInitStrategy = SCHEMA_INIT_STRATEGIES.get(schemaInitStrategyPath);
+ if(schemaInitStrategy == null){
+ try {
+ Class schemaInitStrategyClass = Class.forName(schemaInitStrategyPath);
+ schemaInitStrategy = (SchemaInitStrategy) schemaInitStrategyClass.getDeclaredConstructor().newInstance();
+ } catch (ClassNotFoundException | InvocationTargetException | InstantiationException |
+ IllegalAccessException | NoSuchMethodException e) {
+ throw new SchemaCreationException(e);
+ }
+ }
+
+ // initialize schema
+ JanusGraph graph = schemaInitStrategy.initializeSchemaAndStart(graphDatabaseConfiguration);
+
+ if(graph == null){
+ graph = new StandardJanusGraph(graphDatabaseConfiguration);
+ }
+
+ return graph;
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/GeneralJsonSchemaCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/GeneralJsonSchemaCreator.java
new file mode 100644
index 0000000000..812727e002
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/GeneralJsonSchemaCreator.java
@@ -0,0 +1,113 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.janusgraph.core.schema.json.creator.index.JsonCompositeIndexCreator;
+import org.janusgraph.core.schema.json.creator.index.JsonMixedIndexCreator;
+import org.janusgraph.core.schema.json.creator.index.JsonVertexCentricEdgeIndexCreator;
+import org.janusgraph.core.schema.json.creator.index.JsonVertexCentricPropertyIndexCreator;
+import org.janusgraph.core.schema.json.definition.JsonEdgeLabelDefinition;
+import org.janusgraph.core.schema.json.definition.JsonPropertyKeyDefinition;
+import org.janusgraph.core.schema.json.definition.JsonSchemaDefinition;
+import org.janusgraph.core.schema.json.definition.JsonVertexLabelDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonCompositeIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonMixedIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricEdgeIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricPropertyIndexDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GeneralJsonSchemaCreator implements JsonSchemaCreator{
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GeneralJsonSchemaCreator.class);
+
+ private final JsonPropertySchemaCreator propertySchemaCreator = new JsonPropertySchemaCreator();
+ private final JsonVertexSchemaCreator vertexSchemaCreator = new JsonVertexSchemaCreator();
+ private final JsonEdgeSchemaCreator edgeSchemaCreator = new JsonEdgeSchemaCreator();
+ private final JsonCompositeIndexCreator compositeIndexCreator = new JsonCompositeIndexCreator();
+ private final JsonMixedIndexCreator mixedIndexCreator = new JsonMixedIndexCreator();
+ private final JsonVertexCentricEdgeIndexCreator vertexCentricEdgeIndexCreator = new JsonVertexCentricEdgeIndexCreator();
+ private final JsonVertexCentricPropertyIndexCreator vertexCentricPropertyIndexCreator = new JsonVertexCentricPropertyIndexCreator();
+
+ @Override
+ public boolean create(JsonSchemaDefinition generalDefinition, JsonSchemaCreationContext context) {
+
+ LOGGER.info("Starting general schema initialization.");
+
+ boolean wasUpdated = false;
+
+ if(context.isCreateSchemaElements()){
+ // Create all properties
+ if(CollectionUtils.isNotEmpty(generalDefinition.getPropertyKeys())){
+ for(JsonPropertyKeyDefinition definition : generalDefinition.getPropertyKeys()){
+ wasUpdated |= propertySchemaCreator.create(definition, context);
+ }
+ }
+
+ // Create all vertices
+ if(CollectionUtils.isNotEmpty(generalDefinition.getVertexLabels())){
+ for(JsonVertexLabelDefinition definition : generalDefinition.getVertexLabels()){
+ wasUpdated |= vertexSchemaCreator.create(definition, context);
+ }
+ }
+
+ // Create all edges
+ if(CollectionUtils.isNotEmpty(generalDefinition.getEdgeLabels())){
+ for(JsonEdgeLabelDefinition definition : generalDefinition.getEdgeLabels()){
+ wasUpdated |= edgeSchemaCreator.create(definition, context);
+ }
+ }
+ }
+
+ if(context.isCreateIndices()){
+ // Create all composite indices
+ if(CollectionUtils.isNotEmpty(generalDefinition.getCompositeIndexes())){
+ for(JsonCompositeIndexDefinition definition : generalDefinition.getCompositeIndexes()){
+ wasUpdated |= compositeIndexCreator.create(definition, context);
+ }
+ }
+
+ // Create all mixed indices
+ if(CollectionUtils.isNotEmpty(generalDefinition.getMixedIndexes())){
+ for(JsonMixedIndexDefinition definition : generalDefinition.getMixedIndexes()){
+ wasUpdated |= mixedIndexCreator.create(definition, context);
+ }
+ }
+
+ // Create all vertex-centric edge indices
+ if(CollectionUtils.isNotEmpty(generalDefinition.getVertexCentricEdgeIndexes())){
+ for(JsonVertexCentricEdgeIndexDefinition definition : generalDefinition.getVertexCentricEdgeIndexes()){
+ wasUpdated |= vertexCentricEdgeIndexCreator.create(definition, context);
+ }
+ }
+
+ // Create all vertex-centric property indices
+ if(CollectionUtils.isNotEmpty(generalDefinition.getVertexCentricPropertyIndexes())){
+ for(JsonVertexCentricPropertyIndexDefinition definition : generalDefinition.getVertexCentricPropertyIndexes()){
+ wasUpdated |= vertexCentricPropertyIndexCreator.create(definition, context);
+ }
+ }
+ }
+
+ if(wasUpdated){
+ LOGGER.info("Schema initialization complete.");
+ } else {
+ LOGGER.info("There was no any changes during schema initialization. Schema initialization is skipped.");
+ }
+
+ return wasUpdated;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonEdgeSchemaCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonEdgeSchemaCreator.java
new file mode 100644
index 0000000000..9c32ea3fe1
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonEdgeSchemaCreator.java
@@ -0,0 +1,76 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+import org.janusgraph.core.EdgeLabel;
+import org.janusgraph.core.schema.EdgeLabelMaker;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.definition.JsonEdgeLabelDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+
+public class JsonEdgeSchemaCreator implements JsonSchemaCreator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JsonEdgeSchemaCreator.class);
+
+ @Override
+ public boolean create(JsonEdgeLabelDefinition definition, JsonSchemaCreationContext context) {
+
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+
+ if(isEdgeExists(graphManagement, definition)) {
+ LOGGER.info("Creation of the edge {} was skipped because it is already exists.", definition.getLabel());
+ return false;
+ }
+
+ EdgeLabel edgeLabel = createEdge(graphManagement, definition);
+ if(definition.getTtl() != null){
+ graphManagement.setTTL(edgeLabel, Duration.ofMillis(definition.getTtl()));
+ }
+ if(definition.getConsistency() != null){
+ graphManagement.setConsistency(edgeLabel, definition.getConsistency());
+ }
+
+ LOGGER.info("Edge {} was created", definition.getLabel());
+
+ return true;
+
+ }
+
+ private EdgeLabel createEdge(JanusGraphManagement graphManagement,
+ JsonEdgeLabelDefinition definition) {
+
+ EdgeLabelMaker edgeLabelMaker = graphManagement.makeEdgeLabel(definition.getLabel());
+
+ if(Boolean.TRUE.equals(definition.getUnidirected())) {
+ edgeLabelMaker.unidirected();
+ } else {
+ edgeLabelMaker.directed();
+ }
+
+ if(definition.getMultiplicity() != null){
+ edgeLabelMaker.multiplicity(definition.getMultiplicity());
+ }
+
+ return edgeLabelMaker.make();
+ }
+
+ private boolean isEdgeExists(JanusGraphManagement graphManagement,
+ JsonEdgeLabelDefinition definition) {
+ return graphManagement.containsEdgeLabel(definition.getLabel());
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonPropertySchemaCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonPropertySchemaCreator.java
new file mode 100644
index 0000000000..435f2d8354
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonPropertySchemaCreator.java
@@ -0,0 +1,78 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.PropertyKeyMaker;
+import org.janusgraph.core.schema.json.definition.JsonPropertyKeyDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+
+public class JsonPropertySchemaCreator implements JsonSchemaCreator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JsonPropertySchemaCreator.class);
+
+ @Override
+ public boolean create(JsonPropertyKeyDefinition definition, JsonSchemaCreationContext context) {
+
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+
+ if(isPropertyExists(graphManagement, definition)) {
+ LOGGER.info("Creation of the property {} was skipped because it is already exists.", definition.getKey());
+ return false;
+ }
+
+ PropertyKey propertyKey = createProperty(graphManagement, definition);
+ if(definition.getTtl() != null){
+ graphManagement.setTTL(propertyKey, Duration.ofMillis(definition.getTtl()));
+ }
+ if(definition.getConsistency() != null){
+ graphManagement.setConsistency(propertyKey, definition.getConsistency());
+ }
+
+ LOGGER.info("Property {} was created", definition.getKey());
+
+ return true;
+ }
+
+ private PropertyKey createProperty(JanusGraphManagement graphManagement,
+ JsonPropertyKeyDefinition definition) {
+
+ Class propertyTypeClass;
+ try{
+ propertyTypeClass = Class.forName(definition.getClassName());
+ } catch (Exception e){
+ throw new SchemaCreationException(e);
+ }
+
+ PropertyKeyMaker propertyKeyMaker = graphManagement.makePropertyKey(definition.getKey())
+ .dataType(propertyTypeClass);
+
+ if(definition.getCardinality() != null){
+ propertyKeyMaker.cardinality(definition.getCardinality());
+ }
+
+ return propertyKeyMaker.make();
+ }
+
+ private boolean isPropertyExists(JanusGraphManagement graphManagement,
+ JsonPropertyKeyDefinition definition) {
+ return graphManagement.containsPropertyKey(definition.getKey());
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreationContext.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreationContext.java
new file mode 100644
index 0000000000..30f3699b94
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreationContext.java
@@ -0,0 +1,55 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonIndexDefinition;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class JsonSchemaCreationContext {
+
+ private JanusGraphManagement graphManagement;
+ private final boolean createSchemaElements;
+ private final boolean createIndices;
+ private final Set createdOrUpdatedIndices = new HashSet<>();
+
+ public JsonSchemaCreationContext(JanusGraphManagement graphManagement, boolean createSchemaElements, boolean createIndices) {
+ this.graphManagement=graphManagement;
+ this.createSchemaElements = createSchemaElements;
+ this.createIndices = createIndices;
+ }
+
+ public void setGraphManagement(JanusGraphManagement graphManagement) {
+ this.graphManagement = graphManagement;
+ }
+
+ public JanusGraphManagement getGraphManagement() {
+ return graphManagement;
+ }
+
+ public boolean isCreateSchemaElements() {
+ return createSchemaElements;
+ }
+
+ public boolean isCreateIndices() {
+ return createIndices;
+ }
+
+ public Set getCreatedOrUpdatedIndices() {
+ return createdOrUpdatedIndices;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreator.java
new file mode 100644
index 0000000000..09e43996ce
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonSchemaCreator.java
@@ -0,0 +1,21 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+public interface JsonSchemaCreator {
+
+ boolean create(T definition, JsonSchemaCreationContext context);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonVertexSchemaCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonVertexSchemaCreator.java
new file mode 100644
index 0000000000..f684130118
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/JsonVertexSchemaCreator.java
@@ -0,0 +1,71 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+import org.janusgraph.core.VertexLabel;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.VertexLabelMaker;
+import org.janusgraph.core.schema.json.definition.JsonVertexLabelDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+
+public class JsonVertexSchemaCreator implements JsonSchemaCreator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JsonVertexSchemaCreator.class);
+
+ @Override
+ public boolean create(JsonVertexLabelDefinition definition, JsonSchemaCreationContext context) {
+
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+
+ if(isVertexExists(graphManagement, definition)){
+ LOGGER.info("Creation of the vertex {} was skipped because it is already exists.", definition.getLabel());
+ return false;
+ }
+
+ VertexLabel vertexLabel = createVertex(graphManagement, definition);
+ if(definition.getTtl() != null){
+ graphManagement.setTTL(vertexLabel, Duration.ofMillis(definition.getTtl()));
+ }
+
+ LOGGER.info("Vertex {} was created", definition.getLabel());
+
+ return true;
+ }
+
+ private VertexLabel createVertex(JanusGraphManagement graphManagement,
+ JsonVertexLabelDefinition definition) {
+
+ VertexLabelMaker vertexLabelMaker = graphManagement.makeVertexLabel(definition.getLabel());
+
+ if(Boolean.TRUE.equals(definition.getPartition())) {
+ vertexLabelMaker.partition();
+ }
+
+ if(Boolean.TRUE.equals(definition.getStaticVertex())) {
+ vertexLabelMaker.setStatic();
+ }
+
+ return vertexLabelMaker.make();
+ }
+
+ private boolean isVertexExists(JanusGraphManagement graphManagement,
+ JsonVertexLabelDefinition definition) {
+ return graphManagement.containsVertexLabel(definition.getLabel());
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/SchemaCreationException.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/SchemaCreationException.java
new file mode 100644
index 0000000000..fd681969a6
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/SchemaCreationException.java
@@ -0,0 +1,27 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator;
+
+public class SchemaCreationException extends RuntimeException {
+
+ public SchemaCreationException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public SchemaCreationException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonGraphCentricIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonGraphCentricIndexCreator.java
new file mode 100644
index 0000000000..79afc707b7
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonGraphCentricIndexCreator.java
@@ -0,0 +1,101 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.JanusGraphSchemaType;
+import org.janusgraph.core.schema.Parameter;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.creator.SchemaCreationException;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonGraphCentricIndexDefinition;
+import org.janusgraph.core.schema.json.parser.JsonParameterDefinitionParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractJsonGraphCentricIndexCreator
+ extends AbstractJsonIndexCreator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractJsonGraphCentricIndexCreator.class);
+ private final JsonParameterDefinitionParser jsonParameterDefinitionParser = new JsonParameterDefinitionParser();
+
+ @Override
+ protected Index buildIndex(T definition, JsonSchemaCreationContext context) {
+
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+
+ Class extends Element> typeClass = toClass(definition.getTypeClass());
+ JanusGraphManagement.IndexBuilder indexBuilder = graphManagement.buildIndex(definition.getName(), typeClass);
+ addKeys(graphManagement, indexBuilder, definition);
+ addIndexOnly(graphManagement, indexBuilder, definition, typeClass);
+
+ return buildSpecificIndex(graphManagement, indexBuilder, definition);
+ }
+
+ @Override
+ protected boolean containsIndex(T definition, JsonSchemaCreationContext context) {
+ return context.getGraphManagement().containsGraphIndex(definition.getName());
+ }
+
+ protected void addKeys(JanusGraphManagement graphManagement,
+ JanusGraphManagement.IndexBuilder builder,
+ T definition) {
+ if(CollectionUtils.isEmpty(definition.getKeys())){
+ return;
+ }
+ definition.getKeys().forEach(entry -> {
+ if (CollectionUtils.isEmpty(entry.getParameters())) {
+ builder.addKey(graphManagement.getPropertyKey(entry.getPropertyKey()));
+ } else {
+ Parameter[] parameters = entry.getParameters().stream()
+ .map(jsonParameterDefinitionParser::parse).toArray(Parameter[]::new);
+ builder.addKey(graphManagement.getPropertyKey(entry.getPropertyKey()), parameters);
+ }
+ });
+ }
+
+ protected void addIndexOnly(JanusGraphManagement graphManagement,
+ JanusGraphManagement.IndexBuilder indexBuilder,
+ T definition, Class extends Element> typeClass) {
+ if(StringUtils.isNotEmpty(definition.getIndexOnly())){
+ JanusGraphSchemaType schemaLabel;
+ if(Vertex.class.isAssignableFrom(typeClass)){
+ schemaLabel = graphManagement.getVertexLabel(definition.getIndexOnly());
+ } else if(Edge.class.isAssignableFrom(typeClass)){
+ schemaLabel = graphManagement.getEdgeLabel(definition.getIndexOnly());
+ } else {
+ throw new SchemaCreationException("No implementation for type "+typeClass.getName());
+ }
+ indexBuilder.indexOnly(schemaLabel);
+ }
+ }
+
+ private Class extends Element> toClass(String type){
+ try{
+ return (Class extends Element>) Class.forName(type);
+ } catch (Exception e){
+ LOGGER.error("Class [{}] is not a child of {}", type, Element.class.getName());
+ throw new SchemaCreationException(e);
+ }
+ }
+
+ protected abstract Index buildSpecificIndex(JanusGraphManagement graphManagement, JanusGraphManagement.IndexBuilder indexBuilder, T definition);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonIndexCreator.java
new file mode 100644
index 0000000000..3a9a42d8b5
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonIndexCreator.java
@@ -0,0 +1,48 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreator;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonIndexDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractJsonIndexCreator implements JsonSchemaCreator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractJsonIndexCreator.class);
+
+ @Override
+ public boolean create(T definition, JsonSchemaCreationContext context) {
+
+ if (containsIndex(definition, context)) {
+ LOG.info("Index with name [{}] was skipped because it already exists.", definition.getName());
+ return false;
+ }
+
+ Index index = buildIndex(definition, context);
+ LOG.info("Index {} was created", index.name());
+
+ context.getCreatedOrUpdatedIndices().add(definition);
+
+ return true;
+ }
+
+ protected abstract boolean containsIndex(T definition, JsonSchemaCreationContext context);
+
+ protected abstract Index buildIndex(T definition, JsonSchemaCreationContext context);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonVertexCentricIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonVertexCentricIndexCreator.java
new file mode 100644
index 0000000000..f80dc2af32
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/AbstractJsonVertexCentricIndexCreator.java
@@ -0,0 +1,47 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.RelationType;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.definition.index.AbstractJsonVertexCentricIndexDefinition;
+
+public abstract class AbstractJsonVertexCentricIndexCreator
+ extends AbstractJsonIndexCreator {
+
+ private static final PropertyKey[] EMPTY_PROPERTY_KEYS = new PropertyKey[0];
+
+ @Override
+ protected boolean containsIndex(T definition, JsonSchemaCreationContext context) {
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+ RelationType relationType = graphManagement.getRelationType(getIndexedElementName(definition));
+ return graphManagement.containsRelationIndex(relationType, definition.getName());
+ }
+
+ protected PropertyKey[] toPropertyKeys(T definition, JsonSchemaCreationContext context){
+ if(CollectionUtils.isEmpty(definition.getPropertyKeys())){
+ return EMPTY_PROPERTY_KEYS;
+ }
+ return definition.getPropertyKeys().stream()
+ .map(propertyKey -> context.getGraphManagement().getPropertyKey(propertyKey))
+ .toArray(PropertyKey[]::new);
+ }
+
+ protected abstract String getIndexedElementName(T definition);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonCompositeIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonCompositeIndexCreator.java
new file mode 100644
index 0000000000..2b492b053b
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonCompositeIndexCreator.java
@@ -0,0 +1,48 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.JanusGraphIndex;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.definition.index.JsonCompositeIndexDefinition;
+
+public class JsonCompositeIndexCreator extends AbstractJsonGraphCentricIndexCreator {
+
+ @Override
+ protected Index buildSpecificIndex(JanusGraphManagement graphManagement, JanusGraphManagement.IndexBuilder indexBuilder,
+ JsonCompositeIndexDefinition definition){
+ if(Boolean.TRUE.equals(definition.getUnique())){
+ indexBuilder.unique();
+ }
+
+ if(CollectionUtils.isNotEmpty(definition.getInlinePropertyKeys())){
+ for(String inlinePropertyKey : definition.getInlinePropertyKeys()){
+ PropertyKey propertyKey = graphManagement.getPropertyKey(inlinePropertyKey);
+ indexBuilder.addInlinePropertyKey(propertyKey);
+ }
+ }
+
+ JanusGraphIndex index = indexBuilder.buildCompositeIndex();
+
+ if(definition.getConsistency() != null){
+ graphManagement.setConsistency(index, definition.getConsistency());
+ }
+
+ return index;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonMixedIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonMixedIndexCreator.java
new file mode 100644
index 0000000000..20a7f78442
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonMixedIndexCreator.java
@@ -0,0 +1,30 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.definition.index.JsonMixedIndexDefinition;
+
+public class JsonMixedIndexCreator
+ extends AbstractJsonGraphCentricIndexCreator {
+
+ @Override
+ protected Index buildSpecificIndex(JanusGraphManagement graphManagement, JanusGraphManagement.IndexBuilder indexBuilder,
+ JsonMixedIndexDefinition definition){
+
+ return indexBuilder.buildMixedIndex(definition.getIndexBackend());
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricEdgeIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricEdgeIndexCreator.java
new file mode 100644
index 0000000000..d6ab225f57
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricEdgeIndexCreator.java
@@ -0,0 +1,54 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.janusgraph.core.EdgeLabel;
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricEdgeIndexDefinition;
+
+public class JsonVertexCentricEdgeIndexCreator extends AbstractJsonVertexCentricIndexCreator {
+
+ @Override
+ protected Index buildIndex(JsonVertexCentricEdgeIndexDefinition definition, JsonSchemaCreationContext context){
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+ EdgeLabel edgeLabel = graphManagement.getEdgeLabel(definition.getIndexedEdgeLabel());
+ PropertyKey[] propertyKeys = toPropertyKeys(definition, context);
+
+ if (definition.getOrder() == null) {
+ return graphManagement.buildEdgeIndex(
+ edgeLabel,
+ definition.getName(),
+ definition.getDirection(),
+ propertyKeys
+ );
+ } else {
+ return graphManagement.buildEdgeIndex(
+ edgeLabel,
+ definition.getName(),
+ definition.getDirection(),
+ definition.getOrder(),
+ propertyKeys
+ );
+ }
+ }
+
+ @Override
+ protected String getIndexedElementName(JsonVertexCentricEdgeIndexDefinition definition) {
+ return definition.getIndexedEdgeLabel();
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricPropertyIndexCreator.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricPropertyIndexCreator.java
new file mode 100644
index 0000000000..6dc9f44b7e
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/creator/index/JsonVertexCentricPropertyIndexCreator.java
@@ -0,0 +1,51 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.creator.index;
+
+import org.janusgraph.core.PropertyKey;
+import org.janusgraph.core.schema.Index;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.json.creator.JsonSchemaCreationContext;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricPropertyIndexDefinition;
+
+public class JsonVertexCentricPropertyIndexCreator extends AbstractJsonVertexCentricIndexCreator {
+
+ @Override
+ protected Index buildIndex(JsonVertexCentricPropertyIndexDefinition definition, JsonSchemaCreationContext context){
+ JanusGraphManagement graphManagement = context.getGraphManagement();
+ PropertyKey propertyKey = graphManagement.getPropertyKey(definition.getIndexedPropertyKey());
+ PropertyKey[] propertyKeys = toPropertyKeys(definition, context);
+
+ if (definition.getOrder() == null) {
+ return graphManagement.buildPropertyIndex(
+ propertyKey,
+ definition.getName(),
+ propertyKeys
+ );
+ } else {
+ return graphManagement.buildPropertyIndex(
+ propertyKey,
+ definition.getName(),
+ definition.getOrder(),
+ propertyKeys
+ );
+ }
+ }
+
+ @Override
+ protected String getIndexedElementName(JsonVertexCentricPropertyIndexDefinition definition) {
+ return definition.getIndexedPropertyKey();
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonEdgeLabelDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonEdgeLabelDefinition.java
new file mode 100644
index 0000000000..29de21d720
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonEdgeLabelDefinition.java
@@ -0,0 +1,71 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition;
+
+import org.janusgraph.core.Multiplicity;
+import org.janusgraph.core.schema.ConsistencyModifier;
+
+public class JsonEdgeLabelDefinition {
+
+ private String label;
+
+ private Multiplicity multiplicity;
+
+ private Boolean unidirected;
+
+ private Long ttl;
+
+ private ConsistencyModifier consistency;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public Multiplicity getMultiplicity() {
+ return multiplicity;
+ }
+
+ public void setMultiplicity(Multiplicity multiplicity) {
+ this.multiplicity = multiplicity;
+ }
+
+ public Boolean getUnidirected() {
+ return unidirected;
+ }
+
+ public void setUnidirected(Boolean unidirected) {
+ this.unidirected = unidirected;
+ }
+
+ public Long getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(Long ttl) {
+ this.ttl = ttl;
+ }
+
+ public ConsistencyModifier getConsistency() {
+ return consistency;
+ }
+
+ public void setConsistency(ConsistencyModifier consistency) {
+ this.consistency = consistency;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonParameterDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonParameterDefinition.java
new file mode 100644
index 0000000000..37095b091b
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonParameterDefinition.java
@@ -0,0 +1,48 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition;
+
+public class JsonParameterDefinition {
+
+ private String key;
+
+ private String value;
+
+ private String parser;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getParser() {
+ return parser;
+ }
+
+ public void setParser(String parser) {
+ this.parser = parser;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonPropertyKeyDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonPropertyKeyDefinition.java
new file mode 100644
index 0000000000..3abfb90656
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonPropertyKeyDefinition.java
@@ -0,0 +1,71 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition;
+
+import org.janusgraph.core.Cardinality;
+import org.janusgraph.core.schema.ConsistencyModifier;
+
+public class JsonPropertyKeyDefinition {
+
+ private String key;
+
+ private String className;
+
+ private Cardinality cardinality;
+
+ private Long ttl;
+
+ private ConsistencyModifier consistency;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public Cardinality getCardinality() {
+ return cardinality;
+ }
+
+ public void setCardinality(Cardinality cardinality) {
+ this.cardinality = cardinality;
+ }
+
+ public Long getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(Long ttl) {
+ this.ttl = ttl;
+ }
+
+ public ConsistencyModifier getConsistency() {
+ return consistency;
+ }
+
+ public void setConsistency(ConsistencyModifier consistency) {
+ this.consistency = consistency;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonSchemaDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonSchemaDefinition.java
new file mode 100644
index 0000000000..08ea7f0dcc
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonSchemaDefinition.java
@@ -0,0 +1,89 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition;
+
+import org.janusgraph.core.schema.json.definition.index.JsonCompositeIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonMixedIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricEdgeIndexDefinition;
+import org.janusgraph.core.schema.json.definition.index.JsonVertexCentricPropertyIndexDefinition;
+
+import java.util.List;
+
+public class JsonSchemaDefinition {
+
+ private List vertexLabels;
+ private List edgeLabels;
+ private List propertyKeys;
+ private List compositeIndexes;
+ private List vertexCentricEdgeIndexes;
+ private List vertexCentricPropertyIndexes;
+ private List mixedIndexes;
+
+ public List getVertexLabels() {
+ return vertexLabels;
+ }
+
+ public void setVertexLabels(List vertexLabels) {
+ this.vertexLabels = vertexLabels;
+ }
+
+ public List getEdgeLabels() {
+ return edgeLabels;
+ }
+
+ public void setEdgeLabels(List edgeLabels) {
+ this.edgeLabels = edgeLabels;
+ }
+
+ public List getPropertyKeys() {
+ return propertyKeys;
+ }
+
+ public void setPropertyKeys(List propertyKeys) {
+ this.propertyKeys = propertyKeys;
+ }
+
+ public List getCompositeIndexes() {
+ return compositeIndexes;
+ }
+
+ public void setCompositeIndexes(List compositeIndexes) {
+ this.compositeIndexes = compositeIndexes;
+ }
+
+ public List getVertexCentricEdgeIndexes() {
+ return vertexCentricEdgeIndexes;
+ }
+
+ public void setVertexCentricEdgeIndexes(List vertexCentricEdgeIndexes) {
+ this.vertexCentricEdgeIndexes = vertexCentricEdgeIndexes;
+ }
+
+ public List getVertexCentricPropertyIndexes() {
+ return vertexCentricPropertyIndexes;
+ }
+
+ public void setVertexCentricPropertyIndexes(List vertexCentricPropertyIndexes) {
+ this.vertexCentricPropertyIndexes = vertexCentricPropertyIndexes;
+ }
+
+ public List getMixedIndexes() {
+ return mixedIndexes;
+ }
+
+ public void setMixedIndexes(List mixedIndexes) {
+ this.mixedIndexes = mixedIndexes;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonVertexLabelDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonVertexLabelDefinition.java
new file mode 100644
index 0000000000..7b434f46f5
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/JsonVertexLabelDefinition.java
@@ -0,0 +1,58 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition;
+
+public class JsonVertexLabelDefinition {
+
+ private String label;
+
+ private Boolean staticVertex;
+
+ private Boolean partition;
+
+ private Long ttl;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public Boolean getStaticVertex() {
+ return staticVertex;
+ }
+
+ public void setStaticVertex(Boolean staticVertex) {
+ this.staticVertex = staticVertex;
+ }
+
+ public Boolean getPartition() {
+ return partition;
+ }
+
+ public void setPartition(Boolean partition) {
+ this.partition = partition;
+ }
+
+ public Long getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(Long ttl) {
+ this.ttl = ttl;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonGraphCentricIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonGraphCentricIndexDefinition.java
new file mode 100644
index 0000000000..6748ff5d34
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonGraphCentricIndexDefinition.java
@@ -0,0 +1,48 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+import java.util.List;
+
+public abstract class AbstractJsonGraphCentricIndexDefinition extends AbstractJsonIndexDefinition {
+
+ private String indexOnly;
+ private String typeClass;
+ private List keys;
+
+ public String getIndexOnly() {
+ return indexOnly;
+ }
+
+ public void setIndexOnly(String indexOnly) {
+ this.indexOnly = indexOnly;
+ }
+
+ public String getTypeClass() {
+ return typeClass;
+ }
+
+ public void setTypeClass(String typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ public List getKeys() {
+ return keys;
+ }
+
+ public void setKeys(List keys) {
+ this.keys = keys;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonIndexDefinition.java
new file mode 100644
index 0000000000..8b1bc99833
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonIndexDefinition.java
@@ -0,0 +1,28 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+public abstract class AbstractJsonIndexDefinition {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonVertexCentricIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonVertexCentricIndexDefinition.java
new file mode 100644
index 0000000000..f95412ad40
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/AbstractJsonVertexCentricIndexDefinition.java
@@ -0,0 +1,41 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+
+import java.util.List;
+
+public abstract class AbstractJsonVertexCentricIndexDefinition extends AbstractJsonIndexDefinition {
+
+ private List propertyKeys;
+ private Order order;
+
+ public List getPropertyKeys() {
+ return propertyKeys;
+ }
+
+ public void setPropertyKeys(List propertyKeys) {
+ this.propertyKeys = propertyKeys;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+ public void setOrder(Order order) {
+ this.order = order;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonCompositeIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonCompositeIndexDefinition.java
new file mode 100644
index 0000000000..fd40dceff5
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonCompositeIndexDefinition.java
@@ -0,0 +1,52 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+import org.janusgraph.core.schema.ConsistencyModifier;
+
+import java.util.List;
+
+public class JsonCompositeIndexDefinition extends AbstractJsonGraphCentricIndexDefinition {
+
+ private ConsistencyModifier consistency;
+
+ private Boolean unique;
+
+ private List inlinePropertyKeys;
+
+ public ConsistencyModifier getConsistency() {
+ return consistency;
+ }
+
+ public void setConsistency(ConsistencyModifier consistency) {
+ this.consistency = consistency;
+ }
+
+ public Boolean getUnique() {
+ return unique;
+ }
+
+ public void setUnique(Boolean unique) {
+ this.unique = unique;
+ }
+
+ public List getInlinePropertyKeys() {
+ return inlinePropertyKeys;
+ }
+
+ public void setInlinePropertyKeys(List inlinePropertyKeys) {
+ this.inlinePropertyKeys = inlinePropertyKeys;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonIndexedPropertyKeyDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonIndexedPropertyKeyDefinition.java
new file mode 100644
index 0000000000..5036bbfc88
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonIndexedPropertyKeyDefinition.java
@@ -0,0 +1,42 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+import org.janusgraph.core.schema.json.definition.JsonParameterDefinition;
+
+import java.util.List;
+
+public class JsonIndexedPropertyKeyDefinition {
+
+ private String propertyKey;
+
+ private List parameters;
+
+ public String getPropertyKey() {
+ return propertyKey;
+ }
+
+ public void setPropertyKey(String propertyKey) {
+ this.propertyKey = propertyKey;
+ }
+
+ public List getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List parameters) {
+ this.parameters = parameters;
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonMixedIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonMixedIndexDefinition.java
new file mode 100644
index 0000000000..dc2fe1e140
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonMixedIndexDefinition.java
@@ -0,0 +1,29 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+public class JsonMixedIndexDefinition extends AbstractJsonGraphCentricIndexDefinition {
+
+ private String indexBackend;
+
+ public String getIndexBackend() {
+ return indexBackend;
+ }
+
+ public void setIndexBackend(String indexBackend) {
+ this.indexBackend = indexBackend;
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricEdgeIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricEdgeIndexDefinition.java
new file mode 100644
index 0000000000..9843075165
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricEdgeIndexDefinition.java
@@ -0,0 +1,41 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+
+public class JsonVertexCentricEdgeIndexDefinition extends AbstractJsonVertexCentricIndexDefinition {
+
+ private String indexedEdgeLabel;
+
+ private Direction direction;
+
+ public String getIndexedEdgeLabel() {
+ return indexedEdgeLabel;
+ }
+
+ public void setIndexedEdgeLabel(String indexedEdgeLabel) {
+ this.indexedEdgeLabel = indexedEdgeLabel;
+ }
+
+ public Direction getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Direction direction) {
+ this.direction = direction;
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricPropertyIndexDefinition.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricPropertyIndexDefinition.java
new file mode 100644
index 0000000000..ad268bc335
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/definition/index/JsonVertexCentricPropertyIndexDefinition.java
@@ -0,0 +1,29 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.definition.index;
+
+public class JsonVertexCentricPropertyIndexDefinition extends AbstractJsonVertexCentricIndexDefinition {
+
+ private String indexedPropertyKey;
+
+ public String getIndexedPropertyKey() {
+ return indexedPropertyKey;
+ }
+
+ public void setIndexedPropertyKey(String indexedPropertyKey) {
+ this.indexedPropertyKey = indexedPropertyKey;
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/BooleanJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/BooleanJsonParameterParser.java
new file mode 100644
index 0000000000..4fc3128d1c
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/BooleanJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class BooleanJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Boolean.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ByteJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ByteJsonParameterParser.java
new file mode 100644
index 0000000000..581d872796
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ByteJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class ByteJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Byte.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/DoubleJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/DoubleJsonParameterParser.java
new file mode 100644
index 0000000000..c34980718f
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/DoubleJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class DoubleJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Double.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/EnumJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/EnumJsonParameterParser.java
new file mode 100644
index 0000000000..5f7b9459b3
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/EnumJsonParameterParser.java
@@ -0,0 +1,50 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.apache.commons.lang.UnhandledException;
+import org.janusgraph.core.schema.Parameter;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class EnumJsonParameterParser implements JsonParameterParser {
+
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, toEnum(value));
+ }
+
+ private Object toEnum(String value){
+
+ int indexOfValue = value.lastIndexOf(".");
+
+ if(indexOfValue == -1){
+ throw new AssertionError(value+" isn't a valid enum value. Please use full path (package + class + value) to enum value. " +
+ "Example: org.janusgraph.core.schema.Mapping.TEXT");
+ }
+
+ Class enumClass;
+ Method method;
+
+ try {
+ enumClass = Class.forName(value.substring(0,indexOfValue));
+ method = enumClass.getMethod("valueOf", String.class);
+ return method.invoke(null, value.substring(indexOfValue+1));
+ } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ throw new UnhandledException(e);
+ }
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/FloatJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/FloatJsonParameterParser.java
new file mode 100644
index 0000000000..6c7d7a4ef9
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/FloatJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class FloatJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Float.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/IntegerJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/IntegerJsonParameterParser.java
new file mode 100644
index 0000000000..854961a3cf
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/IntegerJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class IntegerJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Integer.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParser.java
new file mode 100644
index 0000000000..b14be3ef16
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParser.java
@@ -0,0 +1,82 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.apache.commons.lang3.StringUtils;
+import org.janusgraph.core.schema.Parameter;
+import org.janusgraph.core.schema.json.creator.SchemaCreationException;
+import org.janusgraph.core.schema.json.definition.JsonParameterDefinition;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JsonParameterDefinitionParser {
+
+ public static final String STRING_PARAMETER_PARSER_NAME = "string";
+ public static final String ENUM_PARAMETER_PARSER_NAME = "enum";
+ public static final String BOOLEAN_PARAMETER_PARSER_NAME = "boolean";
+ public static final String BYTE_PARAMETER_PARSER_NAME = "byte";
+ public static final String SHORT_PARAMETER_PARSER_NAME = "short";
+ public static final String INTEGER_PARAMETER_PARSER_NAME = "integer";
+ public static final String LONG_PARAMETER_PARSER_NAME = "long";
+ public static final String FLOAT_PARAMETER_PARSER_NAME = "float";
+ public static final String DOUBLE_PARAMETER_PARSER_NAME = "double";
+
+ public final Map jsonParameterParsers;
+
+ public JsonParameterDefinitionParser() {
+ jsonParameterParsers = new HashMap<>();
+ jsonParameterParsers.put(STRING_PARAMETER_PARSER_NAME, new StringJsonParameterParser());
+ jsonParameterParsers.put(ENUM_PARAMETER_PARSER_NAME, new EnumJsonParameterParser());
+ jsonParameterParsers.put(BOOLEAN_PARAMETER_PARSER_NAME, new BooleanJsonParameterParser());
+ jsonParameterParsers.put(BYTE_PARAMETER_PARSER_NAME, new ByteJsonParameterParser());
+ jsonParameterParsers.put(SHORT_PARAMETER_PARSER_NAME, new ShortJsonParameterParser());
+ jsonParameterParsers.put(INTEGER_PARAMETER_PARSER_NAME, new IntegerJsonParameterParser());
+ jsonParameterParsers.put(LONG_PARAMETER_PARSER_NAME, new LongJsonParameterParser());
+ jsonParameterParsers.put(FLOAT_PARAMETER_PARSER_NAME, new FloatJsonParameterParser());
+ jsonParameterParsers.put(DOUBLE_PARAMETER_PARSER_NAME, new DoubleJsonParameterParser());
+ }
+
+ public Parameter parse(JsonParameterDefinition definition){
+
+ String parserClassPath = definition.getParser();
+ if(StringUtils.isEmpty(parserClassPath)){
+ parserClassPath = STRING_PARAMETER_PARSER_NAME;
+ }
+
+ JsonParameterParser parameterParser = jsonParameterParsers.get(parserClassPath);
+
+ if(parameterParser == null){
+
+ try {
+ Class parserClass = Class.forName(parserClassPath);
+ Object instance = parserClass.newInstance();
+
+ if (!(instance instanceof JsonParameterParser)){
+ throw new SchemaCreationException("Class "+parserClassPath+" does not implement JsonParameterParser");
+ }
+
+ parameterParser = (JsonParameterParser) instance;
+
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ throw new SchemaCreationException(e);
+ }
+
+ jsonParameterParsers.put(parserClassPath, parameterParser);
+ }
+
+ return parameterParser.parse(definition.getKey(), definition.getValue());
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterParser.java
new file mode 100644
index 0000000000..a5ff71d9a6
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/JsonParameterParser.java
@@ -0,0 +1,23 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public interface JsonParameterParser {
+
+ Parameter parse(String key, String value);
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/LongJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/LongJsonParameterParser.java
new file mode 100644
index 0000000000..5fe51df462
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/LongJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class LongJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Long.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ShortJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ShortJsonParameterParser.java
new file mode 100644
index 0000000000..26aa07f4c8
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/ShortJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class ShortJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, Short.valueOf(value));
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/StringJsonParameterParser.java b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/StringJsonParameterParser.java
new file mode 100644
index 0000000000..5dbc10e4c3
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/schema/json/parser/StringJsonParameterParser.java
@@ -0,0 +1,24 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Parameter;
+
+public class StringJsonParameterParser implements JsonParameterParser {
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, value);
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/util/GraphFactoryUtils.java b/janusgraph-core/src/main/java/org/janusgraph/core/util/GraphFactoryUtils.java
new file mode 100644
index 0000000000..7c773893d1
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/util/GraphFactoryUtils.java
@@ -0,0 +1,29 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.util;
+
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.schema.SchemaInitializationManager;
+import org.janusgraph.diskstorage.configuration.ReadConfiguration;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+import org.janusgraph.graphdb.configuration.builder.GraphDatabaseConfigurationBuilder;
+
+public class GraphFactoryUtils {
+
+ public static JanusGraph defineSchemaAndStart(ReadConfiguration configuration) {
+ GraphDatabaseConfiguration graphDatabaseConfiguration = new GraphDatabaseConfigurationBuilder().build(configuration);
+ return SchemaInitializationManager.initializeSchemaAndStart(graphDatabaseConfiguration);
+ }
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/util/JsonUtil.java b/janusgraph-core/src/main/java/org/janusgraph/core/util/JsonUtil.java
new file mode 100644
index 0000000000..6e66485d66
--- /dev/null
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/util/JsonUtil.java
@@ -0,0 +1,46 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.util;
+
+import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+public class JsonUtil {
+
+ public static T jsonResourcePathToObject(String resourcePath, Class parsedClass) throws IOException {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ InputStream resourceStream = loader.getResourceAsStream(resourcePath);
+ return jsonToObject(new InputStreamReader(resourceStream), parsedClass);
+ }
+
+ public static T jsonFilePathToObject(String filePath, Class parsedClass) throws IOException {
+ return jsonToObject(new FileReader(filePath), parsedClass);
+ }
+
+ public static T jsonStringToObject(String json, Class parsedClass) throws IOException {
+ return jsonToObject(new StringReader(json), parsedClass);
+ }
+
+ public static T jsonToObject(Reader reader, Class parsedClass) throws IOException {
+ return new ObjectMapper().readValue(reader, parsedClass);
+ }
+
+}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/util/ManagementUtil.java b/janusgraph-core/src/main/java/org/janusgraph/core/util/ManagementUtil.java
index f8b3fbc64e..7147fc4f79 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/core/util/ManagementUtil.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/core/util/ManagementUtil.java
@@ -18,23 +18,36 @@
import org.apache.commons.lang3.StringUtils;
import org.janusgraph.core.JanusGraph;
import org.janusgraph.core.JanusGraphException;
+import org.janusgraph.core.JanusGraphTransaction;
import org.janusgraph.core.PropertyKey;
import org.janusgraph.core.schema.Index;
import org.janusgraph.core.schema.JanusGraphIndex;
import org.janusgraph.core.schema.JanusGraphManagement;
import org.janusgraph.core.schema.RelationTypeIndex;
+import org.janusgraph.core.schema.SchemaAction;
+import org.janusgraph.core.schema.SchemaStatus;
import org.janusgraph.diskstorage.util.time.TimestampProvider;
import org.janusgraph.graphdb.database.StandardJanusGraph;
+import org.janusgraph.graphdb.database.management.GraphIndexStatusReport;
+import org.janusgraph.graphdb.database.management.ManagementSystem;
+import org.janusgraph.graphdb.database.management.RelationIndexStatusReport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
+import java.util.List;
+import java.util.Map;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class ManagementUtil {
+ private static final Logger LOG = LoggerFactory.getLogger(ManagementUtil.class);
+
/**
* This method blocks and waits until the provided index has been updated across the entire JanusGraph cluster
* and reached a stable state.
@@ -96,4 +109,196 @@ private static void awaitIndexUpdate(JanusGraph g, String indexName, String rela
"wait periods this is most likely caused by a failed/incorrectly shut down JanusGraph instance or a lingering transaction.");
}
+ /**
+ * Force rollback all transactions which are opened on the graph.
+ * @param graph - graph on which to rollback all current transactions
+ */
+ public static void forceRollbackAllTransactions(JanusGraph graph){
+ if(graph instanceof StandardJanusGraph){
+ for(JanusGraphTransaction janusGraphTransaction : ((StandardJanusGraph) graph).getOpenTransactions()){
+ janusGraphTransaction.rollback();
+ }
+ }
+ }
+
+ /**
+ * Force closes all instances except current instance. This is a dangerous operation as it will close any other active instances.
+ * @param graph - graph instance to keep (this instance will not be closed).
+ */
+ public static void forceCloseOtherInstances(JanusGraph graph){
+ String currentInstanceId = ((StandardJanusGraph) graph).getConfiguration().getUniqueGraphId();
+ String currentInstanceIdWithSuffix = currentInstanceId + ManagementSystem.CURRENT_INSTANCE_SUFFIX;
+ JanusGraphManagement mgmt = graph.openManagement();
+ mgmt.getOpenInstances().forEach(instanceId -> {
+ if(!currentInstanceId.equals(instanceId) && !currentInstanceIdWithSuffix.equals(instanceId)){
+ mgmt.forceCloseInstance(instanceId);
+ }
+ });
+ mgmt.commit();
+ }
+
+ public static void reindexAndEnableIndices(JanusGraph graph, List nonEnabledGraphIndexNames, Map nonEnabledRelationTypeIndexNamesAndTypes, long indexStatusTimeout){
+ nonEnabledGraphIndexNames.forEach(indexName -> awaitGraphIndexStatus(graph, indexName, indexStatusTimeout));
+ nonEnabledRelationTypeIndexNamesAndTypes.forEach((indexName, indexedElementName) -> awaitVertexCentricIndexStatus(graph, indexName, indexedElementName, indexStatusTimeout));
+
+ nonEnabledGraphIndexNames.forEach(indexName -> {
+ try {
+ LOG.info("Start re-indexing graph index {}", indexName);
+
+ ManagementSystem schemaUpdateMgmt = (ManagementSystem) graph.openManagement();
+ Index index = schemaUpdateMgmt.getGraphIndex(indexName);
+ schemaUpdateMgmt.updateIndex(index, SchemaAction.REINDEX).get();
+ schemaUpdateMgmt.commit();
+
+ LOG.info("Finished re-indexing graph index {}", indexName);
+ } catch (Exception e) {
+ LOG.error("Couldn't execute re-index for the graph index [{}]", indexName, e);
+ throw new RuntimeException(e);
+ }
+ });
+
+ nonEnabledRelationTypeIndexNamesAndTypes.forEach((indexName, indexedElementName) -> {
+ try {
+ LOG.info("Start re-indexing vertex-centric index {}", indexName);
+
+ ManagementSystem schemaUpdateMgmt = (ManagementSystem) graph.openManagement();
+ Index index = schemaUpdateMgmt.getRelationIndex(schemaUpdateMgmt.getRelationType(indexedElementName), indexName);
+ schemaUpdateMgmt.updateIndex(index, SchemaAction.REINDEX).get();
+ schemaUpdateMgmt.commit();
+
+ LOG.info("Finished re-indexing vertex-centric index {}", indexName);
+ } catch (Exception e) {
+ LOG.error("Couldn't execute re-index for the vertex-centric index [{}]", indexName, e);
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ public static void forceEnableIndices(JanusGraph graph, List nonEnabledGraphIndexNames, Map nonEnabledRelationTypeIndexNamesAndTypes, long indexStatusTimeout){
+ nonEnabledGraphIndexNames.forEach(indexName -> {
+ try{
+ awaitGraphIndexStatus(graph, indexName, indexStatusTimeout);
+ } catch (Exception e){
+ LOG.warn("Await for status update of the graph index {} finished with exception", indexName, e);
+ }
+ });
+ nonEnabledRelationTypeIndexNamesAndTypes.forEach((indexName, indexedElementName) -> {
+ try{
+ awaitVertexCentricIndexStatus(graph, indexName, indexedElementName, indexStatusTimeout);
+ } catch (Exception e){
+ LOG.warn("Await for status update of the vertex-centric index {} finished with exception", indexName, e);
+ }
+ });
+
+ nonEnabledGraphIndexNames.forEach(indexName -> {
+ try {
+ LOG.info("Start force-enabling graph index {}", indexName);
+
+ ManagementSystem schemaUpdateMgmt = (ManagementSystem) graph.openManagement();
+ Index index = schemaUpdateMgmt.getGraphIndex(indexName);
+ schemaUpdateMgmt.updateIndex(index, SchemaAction.ENABLE_INDEX).get();
+ schemaUpdateMgmt.commit();
+
+ LOG.info("Finished force-enabling graph index {}", indexName);
+ } catch (Exception e) {
+ LOG.error("Couldn't execute force-enable for the graph index [{}]", indexName, e);
+ throw new RuntimeException(e);
+ }
+ });
+
+ nonEnabledRelationTypeIndexNamesAndTypes.forEach((indexName, indexedElementName) -> {
+ try {
+ LOG.info("Start force-enabling vertex-centric index {}", indexName);
+
+ ManagementSystem schemaUpdateMgmt = (ManagementSystem) graph.openManagement();
+ Index index = schemaUpdateMgmt.getRelationIndex(schemaUpdateMgmt.getRelationType(indexedElementName), indexName);
+ schemaUpdateMgmt.updateIndex(index, SchemaAction.ENABLE_INDEX).get();
+ schemaUpdateMgmt.commit();
+
+ LOG.info("Finished force-enabling vertex-centric index {}", indexName);
+ } catch (Exception e) {
+ LOG.error("Couldn't execute force-enable for the vertex-centric index [{}]", indexName, e);
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ public static void awaitVertexCentricIndexStatus(JanusGraph janusGraph, String indexName, String indexedElement, long indexStatusTimeout) {
+
+ RelationIndexStatusReport relationIndexStatusReport;
+ try {
+
+ relationIndexStatusReport =
+ ManagementSystem.awaitRelationIndexStatus(
+ janusGraph,
+ indexName,
+ indexedElement
+ ).timeout(indexStatusTimeout, ChronoUnit.MILLIS).call();
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!relationIndexStatusReport.getSucceeded()){
+ LOG.error("Await wasn't successful for index [{}]. Actual status [{}]. Time elapsed [{}]. Target statuses [{}]",
+ indexName,
+ relationIndexStatusReport.getActualStatus().toString(),
+ relationIndexStatusReport.getElapsed().toString(),
+ StringUtils.join(relationIndexStatusReport.getTargetStatuses())
+ );
+
+ throw new IllegalStateException("Couldn't await for vertex-centric index status in time [" + indexName + "]");
+ }
+ }
+
+ public static void awaitGraphIndexStatus(JanusGraph janusGraph, String indexName, long indexStatusTimeout) {
+
+ GraphIndexStatusReport graphIndexStatusReport;
+ try {
+ graphIndexStatusReport =
+ ManagementSystem.awaitGraphIndexStatus(
+ janusGraph,
+ indexName
+ ).timeout(indexStatusTimeout, ChronoUnit.MILLIS).call();
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!graphIndexStatusReport.getSucceeded()){
+ LOG.warn("Await wasn't successful for index [{}]. Covered keys [{}]. Not covered keys [{}]. Time elapsed [{}]. Target statuses [{}]",
+ indexName,
+ StringUtils.join(graphIndexStatusReport.getConvergedKeys()),
+ StringUtils.join(graphIndexStatusReport.getNotConvergedKeys()),
+ graphIndexStatusReport.getElapsed().toString(),
+ StringUtils.join(graphIndexStatusReport.getTargetStatuses())
+ );
+
+ throw new IllegalStateException("Couldn't await for graph-centric index status in time [" + indexName + "]");
+ }
+ }
+
+ public static boolean isIndexHasStatus(Index index, SchemaStatus status){
+ if(index instanceof JanusGraphIndex){
+ return isGraphIndexHasStatus((JanusGraphIndex) index, status);
+ }
+ if(index instanceof RelationTypeIndex){
+ return isRelationIndexHasStatus((RelationTypeIndex) index, status);
+ }
+ throw new IllegalStateException("Unexpected index type: " + index.getClass() + ", indexName: " + index.name());
+ }
+
+ public static boolean isGraphIndexHasStatus(JanusGraphIndex graphIndex, SchemaStatus status){
+ for(PropertyKey propertyKey : graphIndex.getFieldKeys()){
+ if(!status.equals(graphIndex.getIndexStatus(propertyKey))){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isRelationIndexHasStatus(RelationTypeIndex relationTypeIndex, SchemaStatus status){
+ return status.equals(relationTypeIndex.getIndexStatus());
+ }
+
}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java
index 10bf05bb3d..2e95b0d2a7 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java
@@ -28,7 +28,10 @@
import org.janusgraph.core.schema.DisableDefaultSchemaMaker;
import org.janusgraph.core.schema.IgnorePropertySchemaMaker;
import org.janusgraph.core.schema.JanusGraphDefaultSchemaMaker;
+import org.janusgraph.core.schema.SchemaInitStrategy;
+import org.janusgraph.core.schema.SchemaInitType;
import org.janusgraph.core.schema.Tp3DefaultSchemaMaker;
+import org.janusgraph.core.schema.IndicesActivationType;
import org.janusgraph.diskstorage.Backend;
import org.janusgraph.diskstorage.StandardIndexProvider;
import org.janusgraph.diskstorage.StandardStoreManager;
@@ -80,6 +83,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.management.MBeanServerFactory;
@@ -437,6 +441,75 @@ public boolean apply(@Nullable String s) {
"as described in the config option 'schema.default'. If 'schema.constraints' is set to 'false' which is the default, then no schema constraints are applied.",
ConfigOption.Type.GLOBAL_OFFLINE, false);
+ public static final ConfigNamespace SCHEMA_INIT = new ConfigNamespace(SCHEMA_NS,"init",
+ "Configuration options for schema initialization on startup.");
+
+ public static final ConfigOption SCHEMA_INIT_STRATEGY = new ConfigOption<>(SCHEMA_INIT,"strategy",
+ String.format("Specifies the strategy for schema initialization before starting JanusGraph. You must provide the full " +
+ "class path of a class that implements the `%s` interface and has parameterless constructor.
" +
+ "The following shortcuts are also available:
" +
+ "- `%s` - Skips schema initialization.
" +
+ "- `%s` - Schema initialization via provided JSON file or JSON string.
",
+ SchemaInitStrategy.class.getSimpleName(),
+ SchemaInitType.NONE.getConfigName(),
+ SchemaInitType.JSON.getConfigName()),
+ ConfigOption.Type.LOCAL, SchemaInitType.NONE.getConfigName());
+
+ public static final ConfigOption SCHEMA_DROP_BEFORE_INIT = new ConfigOption<>(SCHEMA_INIT,"drop-before-startup",
+ String.format("Drops the entire schema with graph data before JanusGraph schema initialization. " +
+ "Note that the schema will be dropped regardless of the selected initialization strategy, " +
+ "including when `%s` is set to `%s`.",
+ SCHEMA_INIT_STRATEGY.toStringWithoutRoot(),
+ SchemaInitType.NONE.getConfigName()),
+ ConfigOption.Type.LOCAL, false);
+
+ public static final ConfigNamespace SCHEMA_INIT_JSON = new ConfigNamespace(SCHEMA_INIT,"json",
+ "Options for JSON schema initialization strategy.");
+
+ public static final ConfigOption SCHEMA_INIT_JSON_FILE = new ConfigOption<>(SCHEMA_INIT_JSON,"file",
+ "File path to JSON formated schema definition.", ConfigOption.Type.LOCAL, String.class);
+
+ public static final ConfigOption SCHEMA_INIT_JSON_STR = new ConfigOption<>(SCHEMA_INIT_JSON,"string",
+ "JSON formated schema definition string. This option takes precedence if both `file` and `string` are used.",
+ ConfigOption.Type.LOCAL, String.class);
+
+ public static final ConfigOption SCHEMA_INIT_JSON_SKIP_ELEMENTS = new ConfigOption<>(SCHEMA_INIT_JSON,"skip-elements",
+ "Skip creation of VertexLabel, EdgeLabel, and PropertyKey.",
+ ConfigOption.Type.LOCAL, false);
+
+ public static final ConfigOption SCHEMA_INIT_JSON_SKIP_INDICES = new ConfigOption<>(SCHEMA_INIT_JSON,"skip-indices",
+ "Skip creation of indices.",
+ ConfigOption.Type.LOCAL, false);
+
+ public static final ConfigOption SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE = new ConfigOption<>(SCHEMA_INIT_JSON,"indices-activation",
+ String.format("Indices activation type:
" +
+ "- `%s` - Reindex process will be triggered for any updated index. After this all updated indexes will be enabled.
" +
+ "- `%s` - Reindex process will be triggered for any index which is not enabled (including previously created indices). After reindexing all indices will be enabled.
" +
+ "- `%s` - Skip reindex process for any updated indexes.
" +
+ "- `%s` - Force enable all updated indexes without running any reindex process (previous data may not be available for such indices).
" +
+ "- `%s` - Force enable all indexes (including previously created indices) without running any reindex process (previous data may not be available for such indices).
",
+ IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.getConfigName(),
+ IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.getConfigName(),
+ IndicesActivationType.SKIP_ACTIVATION.getConfigName(),
+ IndicesActivationType.FORCE_ENABLE_UPDATED_ONLY.getConfigName(),
+ IndicesActivationType.FORCE_ENABLE_NON_ENABLED.getConfigName()
+ ),
+ ConfigOption.Type.LOCAL, IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.getConfigName());
+
+ public static final ConfigOption SCHEMA_INIT_JSON_FORCE_CLOSE_OTHER_INSTANCES = new ConfigOption<>(SCHEMA_INIT_JSON,"force-close-other-instances",
+ String.format("Force closes other JanusGraph instances before schema initialization, regardless if they are active or not. " +
+ "This is a dangerous operation. This option exists to help people initialize schema who struggle with zombie JanusGraph instances. " +
+ "It's not recommended to be used unless you know what you are doing. Instead of this parameter, " +
+ "it's recommended to check `%s` and `%s` options to not create zombie instances in the cluster.",
+ UNIQUE_INSTANCE_ID.toStringWithoutRoot(),
+ REPLACE_INSTANCE_IF_EXISTS.toStringWithoutRoot()
+ ),
+ ConfigOption.Type.LOCAL, false);
+
+ public static final ConfigOption SCHEMA_INIT_JSON_AWAIT_INDEX_STATUS_TIMEOUT = new ConfigOption<>(SCHEMA_INIT_JSON,"await-index-status-timeout",
+ "Timeout for awaiting index status operation defined in milliseconds. If the status await timeouts the exception will be thrown during schema initialization process.",
+ ConfigOption.Type.LOCAL, TimeUnit.MINUTES.toMillis(3));
+
// ################ CACHE #######################
// ################################################
@@ -1371,6 +1444,15 @@ public boolean apply(@Nullable String s) {
private MultiQueryHasStepStrategyMode hasStepStrategyMode;
private MultiQueryPropertiesStrategyMode propertiesStrategyMode;
private MultiQueryLabelStepStrategyMode labelStepStrategyMode;
+ private String schemaInitStrategy;
+ private boolean dropSchemaBeforeInit;
+ private String schemaInitJsonFile;
+ private String schemaInitJsonString;
+ private boolean schemaInitJsonSkipElements;
+ private boolean schemaInitJsonSkipIndices;
+ private IndicesActivationType schemaInitJsonIndicesActivationType;
+ private boolean schemaInitJsonForceCloseOtherInstances;
+ private long schemaInitJsonIndexStatusAwaitTimeout;
private StoreFeatures storeFeatures = null;
@@ -1585,6 +1667,42 @@ public org.apache.commons.configuration2.Configuration getConfigurationAtOpen()
return ReadConfigurationConverter.getInstance().convertToBaseConfiguration(configurationAtOpen);
}
+ public String getSchemaInitStrategy(){
+ return schemaInitStrategy;
+ }
+
+ public boolean isDropSchemaBeforeInit(){
+ return dropSchemaBeforeInit;
+ }
+
+ public String getSchemaInitJsonFile(){
+ return schemaInitJsonFile;
+ }
+
+ public String getSchemaInitJsonString(){
+ return schemaInitJsonString;
+ }
+
+ public boolean getSchemaInitJsonSkipElements(){
+ return schemaInitJsonSkipElements;
+ }
+
+ public boolean getSchemaInitJsonSkipIndices(){
+ return schemaInitJsonSkipIndices;
+ }
+
+ public IndicesActivationType getSchemaInitJsonIndicesActivationType(){
+ return schemaInitJsonIndicesActivationType;
+ }
+
+ public boolean getSchemaInitJsonForceCloseOtherInstances(){
+ return schemaInitJsonForceCloseOtherInstances;
+ }
+
+ public long getSchemaInitJsonIndexStatusAwaitTimeout(){
+ return schemaInitJsonIndexStatusAwaitTimeout;
+ }
+
private void preLoadConfiguration() {
readOnly = configuration.get(STORAGE_READONLY);
evalScript = configuration.get(SCRIPT_EVAL_ENABLED);
@@ -1646,6 +1764,16 @@ private void preLoadConfiguration() {
unknownIndexKeyName = configuration.get(IGNORE_UNKNOWN_INDEX_FIELD) ? UNKNOWN_FIELD_NAME : null;
+ schemaInitStrategy = configuration.get(SCHEMA_INIT_STRATEGY);
+ dropSchemaBeforeInit = configuration.get(SCHEMA_DROP_BEFORE_INIT);
+ schemaInitJsonFile = configuration.has(SCHEMA_INIT_JSON_FILE) ? configuration.get(SCHEMA_INIT_JSON_FILE) : null;
+ schemaInitJsonString = configuration.has(SCHEMA_INIT_JSON_STR) ? configuration.get(SCHEMA_INIT_JSON_STR) : null;
+ schemaInitJsonSkipElements = configuration.get(SCHEMA_INIT_JSON_SKIP_ELEMENTS);
+ schemaInitJsonSkipIndices = configuration.get(SCHEMA_INIT_JSON_SKIP_INDICES);
+ schemaInitJsonIndicesActivationType = selectExactConfig(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE, IndicesActivationType.values());
+ schemaInitJsonForceCloseOtherInstances = configuration.get(SCHEMA_INIT_JSON_FORCE_CLOSE_OTHER_INSTANCES);
+ schemaInitJsonIndexStatusAwaitTimeout = configuration.get(SCHEMA_INIT_JSON_AWAIT_INDEX_STATUS_TIMEOUT);
+
configureMetrics();
}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java
index 526d58c5fc..05692129c7 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java
@@ -113,6 +113,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.Closeable;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
@@ -359,6 +360,9 @@ private synchronized void closeInternal() {
IOUtils.closeQuietly(backend);
IOUtils.closeQuietly(queryCache);
IOUtils.closeQuietly(serializer);
+ if(config instanceof Closeable){
+ IOUtils.closeQuietly((Closeable)config);
+ }
} finally {
isOpen = false;
}
diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java
index 0708429909..302071428d 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java
@@ -585,6 +585,39 @@ private String printIndexes(boolean calledDirectly) {
return sb.toString();
}
+ public List getGraphIndices(SchemaStatus withoutStatusFilter){
+ List indices = filter(getGraphIndexes(Vertex.class), withoutStatusFilter);
+ indices.addAll(filter(getGraphIndexes(Edge.class), withoutStatusFilter));
+ return indices;
+ }
+
+ private List filter(Iterable graphIndices, SchemaStatus withoutStatusFilter){
+ List indicesWithStatus = new ArrayList<>();
+ for(JanusGraphIndex graphIndex : graphIndices){
+ for(PropertyKey propertyKey : graphIndex.getFieldKeys()){
+ if(!withoutStatusFilter.equals(graphIndex.getIndexStatus(propertyKey))){
+ indicesWithStatus.add(graphIndex);
+ break;
+ }
+ }
+ }
+ return indicesWithStatus;
+ }
+
+ public List getVertexCentricIndices(SchemaStatus withoutStatusFilter){
+ Iterable relationTypes = getRelationTypes(RelationType.class);
+ LinkedList relationIndexes = new LinkedList<>();
+ for (RelationType rt :relationTypes) {
+ Iterable rti = getRelationIndexes(rt);
+ rti.forEach(relationTypeIndex -> {
+ if(!withoutStatusFilter.equals(relationTypeIndex.getIndexStatus())){
+ relationIndexes.add(relationTypeIndex);
+ }
+ });
+ }
+ return relationIndexes;
+ }
+
private String iterateIndexes(String pattern, Iterable indexes) {
StringBuilder sb = new StringBuilder();
for (JanusGraphIndex index: indexes) {
diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/types/ParameterType.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/types/ParameterType.java
index 1e0743784b..b1c9ea3d30 100644
--- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/types/ParameterType.java
+++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/types/ParameterType.java
@@ -48,7 +48,7 @@ public enum ParameterType {
TEXT_ANALYZER("text-analyzer"),
;
- private static final String CUSTOM_PARAMETER_PREFIX = "%`custom%`";
+ public static final String CUSTOM_PARAMETER_PREFIX = "%`custom%`";
private final String name;
diff --git a/janusgraph-core/src/test/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParserTest.java b/janusgraph-core/src/test/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParserTest.java
new file mode 100644
index 0000000000..7dc186e3e8
--- /dev/null
+++ b/janusgraph-core/src/test/java/org/janusgraph/core/schema/json/parser/JsonParameterDefinitionParserTest.java
@@ -0,0 +1,94 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.core.schema.json.parser;
+
+import org.janusgraph.core.schema.Mapping;
+import org.janusgraph.core.schema.json.creator.SchemaCreationException;
+import org.janusgraph.core.schema.json.definition.JsonParameterDefinition;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class JsonParameterDefinitionParserTest {
+
+ private final JsonParameterDefinitionParser parser = new JsonParameterDefinitionParser();
+
+ @Test
+ void shouldParseDefinedShortcutParsers(){
+ JsonParameterDefinition definition = new JsonParameterDefinition();
+ definition.setKey("test");
+
+ definition.setParser(JsonParameterDefinitionParser.STRING_PARAMETER_PARSER_NAME);
+ definition.setValue("testStr");
+ Assertions.assertEquals("testStr", parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.ENUM_PARAMETER_PARSER_NAME);
+ definition.setValue("org.janusgraph.core.schema.Mapping.TEXTSTRING");
+ Assertions.assertEquals(Mapping.TEXTSTRING, parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.BOOLEAN_PARAMETER_PARSER_NAME);
+ definition.setValue("true");
+ Assertions.assertEquals(true, parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.BYTE_PARAMETER_PARSER_NAME);
+ definition.setValue("127");
+ Assertions.assertEquals(((byte) 127), parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.SHORT_PARAMETER_PARSER_NAME);
+ definition.setValue("12345");
+ Assertions.assertEquals(((short) 12345), parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.INTEGER_PARAMETER_PARSER_NAME);
+ definition.setValue("12345678");
+ Assertions.assertEquals(12345678, parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.LONG_PARAMETER_PARSER_NAME);
+ definition.setValue("123456789012");
+ Assertions.assertEquals(123456789012L, parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.FLOAT_PARAMETER_PARSER_NAME);
+ definition.setValue("123.45");
+ Assertions.assertEquals(123.45f, parser.parse(definition).value());
+
+ definition.setParser(JsonParameterDefinitionParser.DOUBLE_PARAMETER_PARSER_NAME);
+ definition.setValue("12345.6789012");
+ Assertions.assertEquals(12345.6789012d, parser.parse(definition).value());
+ }
+
+ @Test
+ void shouldParseByFullClassPathParser(){
+ JsonParameterDefinition definition = new JsonParameterDefinition();
+ definition.setKey("test");
+
+ definition.setParser(EnumJsonParameterParser.class.getName());
+ definition.setValue("org.janusgraph.core.schema.Mapping.TEXTSTRING");
+ Assertions.assertEquals(Mapping.TEXTSTRING, parser.parse(definition).value());
+ }
+
+ @Test
+ void shouldFailParsingOnWrongClass(){
+ JsonParameterDefinition definition = new JsonParameterDefinition();
+ definition.setKey("test");
+ definition.setValue("org.janusgraph.core.schema.Mapping.TEXTSTRING");
+
+ definition.setParser(Integer.class.getName());
+ Assertions.assertThrows(SchemaCreationException.class, () -> parser.parse(definition));
+
+ definition.setParser("Unknown class");
+ Assertions.assertThrows(SchemaCreationException.class, () -> parser.parse(definition));
+
+ definition.setParser("integer");
+ Assertions.assertThrows(NumberFormatException.class, () -> parser.parse(definition));
+ }
+}
diff --git a/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CQLElasticsearchTest.java b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CQLElasticsearchTest.java
index f38c2ae867..1ae24a2607 100644
--- a/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CQLElasticsearchTest.java
+++ b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CQLElasticsearchTest.java
@@ -14,12 +14,48 @@
package org.janusgraph.diskstorage.es;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.janusgraph.JanusGraphCassandraContainer;
+import org.janusgraph.core.Cardinality;
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.Multiplicity;
+import org.janusgraph.core.schema.IndicesActivationType;
+import org.janusgraph.core.schema.JanusGraphManagement;
+import org.janusgraph.core.schema.SchemaInitType;
+import org.janusgraph.core.schema.SchemaStatus;
+import org.janusgraph.core.schema.json.definition.JsonSchemaDefinition;
+import org.janusgraph.core.util.JsonUtil;
+import org.janusgraph.core.util.ManagementUtil;
import org.janusgraph.diskstorage.configuration.ModifiableConfiguration;
import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.Callable;
+
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_DROP_BEFORE_INIT;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_FILE;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_FORCE_CLOSE_OTHER_INSTANCES;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_SKIP_ELEMENTS;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_JSON_SKIP_INDICES;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.SCHEMA_INIT_STRATEGY;
+import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
@Testcontainers
public class CQLElasticsearchTest extends ElasticsearchJanusGraphIndexTest {
@@ -34,4 +70,302 @@ public ModifiableConfiguration getStorageConfiguration() {
@Override
@Disabled("CQL seems to not clear storage correctly")
public void testClearStorage() {}
+
+ @Test
+ public void testJsonFreshSchemaImport() {
+ String jsonFilePath = createJsonFileAndReturnPath();
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.getConfigName()
+ );
+ assertSchemaElements();
+ assertIndices(SchemaStatus.ENABLED);
+
+ // Check additional run doesn't change anything
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), false,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath
+ );
+ assertSchemaElements();
+ assertIndices(SchemaStatus.ENABLED);
+
+ // Check schema from documentation can be initialized
+ String jsonFromDocFilePath = createJsonFileAndReturnPath("test_schema_from_doc.json");
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFromDocFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.getConfigName()
+ );
+ }
+
+ @Test
+ public void testJsonGradualSchemaImport() {
+ String jsonFilePath = createJsonFileAndReturnPath();
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), false,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), true
+ );
+
+ createData();
+ assertDataFetch(false, true);
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), false,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), true,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), false
+ );
+
+ assertSchemaElements();
+ assertIndices(SchemaStatus.ENABLED);
+ assertDataFetch(true, true);
+ }
+
+ @Test
+ public void testJsonForceSchemaImport() {
+ String jsonFilePath = createJsonFileAndReturnPath();
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.FORCE_ENABLE_NON_ENABLED.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), false,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), true
+ );
+
+ createData();
+ assertDataFetch(false, true);
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), false,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.FORCE_ENABLE_UPDATED_ONLY.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), true,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), false
+ );
+
+ assertSchemaElements();
+ assertIndices(SchemaStatus.ENABLED);
+ assertDataFetch(true, false);
+ }
+
+ @Test
+ public void testJsonSchemaImportSkipIndicesActivation() {
+ String jsonFilePath = createJsonFileAndReturnPath();
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.FORCE_ENABLE_NON_ENABLED.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), false,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), true
+ );
+
+ createData();
+ assertDataFetch(false, true);
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), false,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.SKIP_ACTIVATION.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), true,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), false
+ );
+
+ assertSchemaElements();
+ assertIndices(SchemaStatus.INSTALLED);
+ assertDataFetch(false, true);
+ }
+
+ @Test
+ public void testJsonGradualSchemaImportForceClosingOtherInstances() {
+ String jsonFilePath = createJsonFileAndReturnPath();
+
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), true,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_NON_ENABLED.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), false,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), true,
+ option(UNIQUE_INSTANCE_ID), "graph1"
+
+ );
+
+ createData();
+ assertDataFetch(false, true);
+
+ setupConfig(option(SCHEMA_INIT_STRATEGY), SchemaInitType.JSON.getConfigName(),
+ option(SCHEMA_DROP_BEFORE_INIT), false,
+ option(SCHEMA_INIT_JSON_FILE), jsonFilePath,
+ option(SCHEMA_INIT_JSON_INDICES_ACTIVATION_TYPE), IndicesActivationType.REINDEX_AND_ENABLE_UPDATED_ONLY.getConfigName(),
+ option(SCHEMA_INIT_JSON_SKIP_ELEMENTS), true,
+ option(SCHEMA_INIT_JSON_SKIP_INDICES), false,
+ option(SCHEMA_INIT_JSON_FORCE_CLOSE_OTHER_INSTANCES), true,
+ option(UNIQUE_INSTANCE_ID), "graph2"
+ );
+
+ JanusGraph prevGraph = graph;
+ JanusGraphManagement prevMgmt = mgmt;
+ if(!prevMgmt.isOpen()){
+ prevMgmt = graph.openManagement();
+ }
+ open(config);
+
+ assertSchemaElements();
+ assertIndices(SchemaStatus.ENABLED);
+ assertDataFetch(true, true);
+
+ if(prevMgmt.isOpen()){
+ prevMgmt.rollback();
+ }
+ if(prevGraph.isOpen()){
+ prevGraph.close();
+ }
+ }
+
+ @Test
+ public void testCustomSchemaInitStrategy() {
+ assertFalse(CustomTestSchemaInitStrategy.initialized);
+ clopen(
+ option(SCHEMA_INIT_STRATEGY), CustomTestSchemaInitStrategy.class.getName()
+ );
+ assertTrue(CustomTestSchemaInitStrategy.initialized);
+ }
+
+ @Test
+ public void testJsonSchemaResourceParseToObject() throws IOException {
+ JsonSchemaDefinition jsonSchemaDefinition = JsonUtil
+ .jsonResourcePathToObject("test_schema_example.json", JsonSchemaDefinition.class);
+ assertNotNull(jsonSchemaDefinition);
+ }
+
+ private void assertDataFetch(boolean indicesUsed, boolean indexedDataShouldBeFound){
+ GraphTraversalSource g = graph.traversal();
+
+ try {
+ Callable> query = () -> g.V().hasLabel("organization").has("name", "test_org1");
+ assertEquals(indicesUsed, query.call().profile().next().toString().contains("index="));
+ assertEquals(indexedDataShouldBeFound, query.call().hasNext());
+
+ query = () -> g.V().hasLabel("organization").has("name", "test_org2");
+ assertEquals(indicesUsed, query.call().profile().next().toString().contains("index="));
+ assertEquals(indexedDataShouldBeFound, query.call().hasNext());
+
+ query = () -> g.V().hasLabel("device").has("name", "test_org3");
+ assertEquals(indicesUsed, query.call().profile().next().toString().contains("index="));
+ assertEquals(indexedDataShouldBeFound, query.call().hasNext());
+
+ if(indexedDataShouldBeFound){
+ Vertex vertex = g.V().hasLabel("organization").has("name", "test_org1").next();
+ query = () -> g.V(vertex).properties("longPropCardinalityList").has("time", P.lt(300));
+ assertEquals(indicesUsed, query.call().profile().next().toString().contains("longPropCardinalityListMetaPropertyVertexCentricIndexForTime"));
+ assertTrue(query.call().hasNext());
+ }
+
+ query = () -> g.E().hasLabel("connects").has("name", "connectsEdge1");
+ assertEquals(indicesUsed, query.call().profile().next().toString().contains("index="));
+ assertEquals(indexedDataShouldBeFound, query.call().hasNext());
+
+ query = () -> g.E().hasLabel("viewed").has("name", "connectsEdge4");
+ // No indices were defined for such query. Thus, it should always be `false`.
+ assertFalse(query.call().profile().next().toString().contains("index="));
+ assertTrue(query.call().profile().next().toString().contains("fullscan=true"));
+ assertTrue(query.call().hasNext());
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ g.tx().rollback();
+ }
+ }
+
+ private void assertSchemaElements(){
+ assertEquals(Long.class, mgmt.getPropertyKey("time").dataType());
+ assertEquals(Double.class, mgmt.getPropertyKey("doubleProp").dataType());
+ assertEquals(Cardinality.LIST, mgmt.getPropertyKey("longPropCardinalityList").cardinality());
+
+ assertEquals("organization", mgmt.getVertexLabel("organization").name());
+
+ assertEquals(Multiplicity.SIMPLE, mgmt.getEdgeLabel("connects").multiplicity());
+ assertTrue(mgmt.getEdgeLabel("connects").isDirected());
+ assertEquals(Multiplicity.MULTI, mgmt.getEdgeLabel("viewed").multiplicity());
+ assertTrue(mgmt.getEdgeLabel("viewed").isUnidirected());
+ }
+
+ private void assertIndices(SchemaStatus status){
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("nameCompositeIndex"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("timeForOrganizationsOnlyCompositeIndex"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("connectsOnlyEdgeCompositeIndex"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("uniqueCompositeIndexWithLocking"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("multiKeysCompositeIndex"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getGraphIndex("nameMixedIndex"), status));
+
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getRelationIndex(mgmt.getRelationType("connects"), "connectsTimeVertexCentricIndex"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getRelationIndex(mgmt.getRelationType("viewed"), "vertexCentricUnidirectedEdgeIndexWithTwoProps"), status));
+ assertTrue(ManagementUtil.isIndexHasStatus(mgmt.getRelationIndex(mgmt.getRelationType("longPropCardinalityList"), "longPropCardinalityListMetaPropertyVertexCentricIndexForTime"), status));
+ }
+
+ private String createJsonFileAndReturnPath() {
+ return createJsonFileAndReturnPath("test_schema_example.json");
+ }
+
+ private String createJsonFileAndReturnPath(String resourcePath) {
+ try{
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ InputStream resourceStream = loader.getResourceAsStream(resourcePath);
+ String jsonSchemaExample = IOUtils.toString(resourceStream, StandardCharsets.UTF_8);
+ File file = File.createTempFile( "janusgraph", "_"+resourcePath);
+ file.deleteOnExit();
+ FileUtils.writeStringToFile(file, jsonSchemaExample, StandardCharsets.UTF_8);
+ return file.getAbsolutePath();
+ } catch (IOException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void createData(){
+ GraphTraversalSource g = graph.traversal();
+
+ Vertex vertex1 = g.addV("organization").property("time", 12345L)
+ .property("name", "test_org1")
+ .property("longPropCardinalityList", 123L, "time", 321L)
+ .property("longPropCardinalityList", 123L, "time", 213L)
+ .property("longPropCardinalityList", 123L, "time", 231L).next();
+
+ Vertex vertex2 = g.addV("organization").property("time", 54321L)
+ .property("name", "test_org2")
+ .property("longPropCardinalityList", 123L, "time", 321L)
+ .property("longPropCardinalityList", 123L, "time", 213L)
+ .property("longPropCardinalityList", 123L, "time", 231L).next();
+
+ Vertex vertex3 = g.addV("device").property("time", 1L)
+ .property("name", "test_org3")
+ .property("longPropCardinalityList", 123L, "time", 321L)
+ .property("longPropCardinalityList", 123L, "time", 213L)
+ .property("longPropCardinalityList", 123L, "time", 231L).next();
+
+ g.addE("connects").from(vertex1).to(vertex2).property("name", "connectsEdge1").property("time", 123L).next();
+ g.addE("connects").from(vertex1).to(vertex3).property("name", "connectsEdge2").property("time", 124L).next();
+ g.addE("connects").from(vertex3).to(vertex2).property("name", "connectsEdge3").property("time", 125L).next();
+ g.addE("viewed").from(vertex2).to(vertex3).property("name", "connectsEdge4").property("time", 100L).next();
+
+ g.tx().commit();
+ }
}
diff --git a/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomJsonStringParameterParser.java b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomJsonStringParameterParser.java
new file mode 100644
index 0000000000..f67313b5a7
--- /dev/null
+++ b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomJsonStringParameterParser.java
@@ -0,0 +1,27 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.diskstorage.es;
+
+import org.janusgraph.core.schema.Parameter;
+import org.janusgraph.core.schema.json.parser.JsonParameterParser;
+
+public class CustomJsonStringParameterParser implements JsonParameterParser {
+
+ @Override
+ public Parameter parse(String key, String value) {
+ return Parameter.of(key, value);
+ }
+
+}
diff --git a/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomTestSchemaInitStrategy.java b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomTestSchemaInitStrategy.java
new file mode 100644
index 0000000000..68d0a7cc42
--- /dev/null
+++ b/janusgraph-es/src/test/java/org/janusgraph/diskstorage/es/CustomTestSchemaInitStrategy.java
@@ -0,0 +1,30 @@
+// Copyright 2024 JanusGraph Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.janusgraph.diskstorage.es;
+
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.schema.SchemaInitStrategy;
+import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
+
+public class CustomTestSchemaInitStrategy implements SchemaInitStrategy {
+
+ public static volatile boolean initialized = false;
+
+ @Override
+ public JanusGraph initializeSchemaAndStart(GraphDatabaseConfiguration graphDatabaseConfiguration) {
+ initialized = true;
+ return null;
+ }
+}
diff --git a/janusgraph-es/src/test/resources/test_schema_example.json b/janusgraph-es/src/test/resources/test_schema_example.json
new file mode 100644
index 0000000000..8b1a7427a0
--- /dev/null
+++ b/janusgraph-es/src/test/resources/test_schema_example.json
@@ -0,0 +1,232 @@
+{
+ "vertexLabels": [
+ {
+ "label": "organization"
+ },
+ {
+ "label": "device",
+ "staticVertex": false,
+ "partition": false
+ },
+ {
+ "label": "unmodifiableVertex",
+ "staticVertex": true
+ },
+ {
+ "label": "temporaryVertexForTwoHours",
+ "staticVertex": true,
+ "ttl": 7200000
+ }
+ ],
+ "edgeLabels": [
+ {
+ "label": "connects",
+ "multiplicity": "SIMPLE",
+ "unidirected": false
+ },
+ {
+ "label": "viewed",
+ "multiplicity": "MULTI",
+ "unidirected": true
+ },
+ {
+ "label": "temporaryEdgeForOneHour",
+ "ttl": 3600000
+ },
+ {
+ "label": "edgeWhichUsesLocksInEventualConsistentDBs",
+ "consistency": "LOCK"
+ },
+ {
+ "label": "edgeWhichUsesForkingInEventualConsistentDBs",
+ "consistency": "FORK"
+ }
+ ],
+ "propertyKeys": [
+ {
+ "key": "time",
+ "className": "java.lang.Long"
+ },
+ {
+ "key": "doubleProp",
+ "className": "java.lang.Double"
+ },
+ {
+ "key": "integerProp",
+ "className": "java.lang.Integer"
+ },
+ {
+ "key": "longPropCardinalityList",
+ "className": "java.lang.Long",
+ "cardinality": "LIST"
+ },
+ {
+ "key": "name",
+ "className": "java.lang.String",
+ "cardinality": "SINGLE"
+ },
+ {
+ "key": "geoshape",
+ "className": "org.janusgraph.core.attribute.Geoshape"
+ },
+ {
+ "key": "type",
+ "className": "java.lang.String",
+ "cardinality": "SET"
+ },
+ {
+ "key": "propertyWhichUsesLocksInEventualConsistentDBs",
+ "className": "java.lang.Long",
+ "cardinality": "SET",
+ "consistency": "LOCK"
+ },
+ {
+ "key": "temporaryPropertyForOneHour",
+ "className": "java.lang.Long",
+ "ttl": 3600000
+ },
+ {
+ "key": "anotherName",
+ "className": "java.lang.String"
+ }
+ ],
+ "compositeIndexes": [
+ {
+ "name": "nameCompositeIndex",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "keys": [
+ {
+ "propertyKey": "name"
+ }
+ ]
+ },
+ {
+ "name": "timeForOrganizationsOnlyCompositeIndex",
+ "indexOnly": "organization",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "keys": [
+ {
+ "propertyKey": "time"
+ }
+ ]
+ },
+ {
+ "name": "connectsOnlyEdgeCompositeIndex",
+ "indexOnly": "connects",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Edge",
+ "keys": [
+ {
+ "propertyKey": "name"
+ }
+ ]
+ },
+ {
+ "name": "uniqueCompositeIndexWithLocking",
+ "indexOnly": "unmodifiableVertex",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "unique": true,
+ "consistency": "LOCK",
+ "keys": [
+ {
+ "propertyKey": "integerProp"
+ }
+ ]
+ },
+ {
+ "name": "multiKeysCompositeIndex",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "unique": true,
+ "consistency": "LOCK",
+ "keys": [
+ {
+ "propertyKey": "doubleProp"
+ },
+ {
+ "propertyKey": "integerProp"
+ }
+ ]
+ }
+ ],
+ "vertexCentricEdgeIndexes": [
+ {
+ "name": "connectsTimeVertexCentricIndex",
+ "indexedEdgeLabel": "connects",
+ "direction": "BOTH",
+ "propertyKeys": [
+ "time"
+ ],
+ "order": "asc"
+ },
+ {
+ "name": "vertexCentricUnidirectedEdgeIndexWithTwoProps",
+ "indexedEdgeLabel": "viewed",
+ "direction": "OUT",
+ "propertyKeys": [
+ "time",
+ "integerProp"
+ ],
+ "order": "asc"
+ }
+ ],
+ "vertexCentricPropertyIndexes": [
+ {
+ "name": "longPropCardinalityListMetaPropertyVertexCentricIndexForTime",
+ "indexedPropertyKey": "longPropCardinalityList",
+ "propertyKeys": [
+ "time"
+ ],
+ "order": "desc"
+ }
+ ],
+ "mixedIndexes": [
+ {
+ "name": "nameMixedIndex",
+ "indexOnly": "organization",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "indexBackend": "search",
+ "keys": [
+ {
+ "propertyKey": "name",
+ "parameters": [
+ {
+ "key": "string-analyzer",
+ "value": "standard",
+ "parser": "string"
+ },
+ {
+ "key": "mapping",
+ "value": "org.janusgraph.core.schema.Mapping.STRING",
+ "parser": "enum"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "typeAndAnotherNameMixedIndex",
+ "indexOnly": "unmodifiableVertex",
+ "typeClass": "org.apache.tinkerpop.gremlin.structure.Vertex",
+ "indexBackend": "search",
+ "keys": [
+ {
+ "propertyKey": "type"
+ },
+ {
+ "propertyKey": "anotherName",
+ "parameters": [
+ {
+ "key": "string-analyzer",
+ "value": "standard",
+ "parser": "org.janusgraph.diskstorage.es.CustomJsonStringParameterParser"
+ },
+ {
+ "key": "mapping",
+ "value": "org.janusgraph.core.schema.Mapping.STRING",
+ "parser": "enum"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/janusgraph-es/src/test/resources/test_schema_from_doc.json b/janusgraph-es/src/test/resources/test_schema_from_doc.json
new file mode 100644
index 0000000000..2acd9aa471
--- /dev/null
+++ b/janusgraph-es/src/test/resources/test_schema_from_doc.json
@@ -0,0 +1,240 @@
+{
+ "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"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/mkdocs.yml b/mkdocs.yml
index acb8a978ae..fa39ff2d81 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -56,6 +56,7 @@ plugins:
interactions/connecting/dotnet.md: basics/connecting/dotnet.md
advanced-topics/bulk-loading.md: operations/bulk-loading.md
advanced-topics/advschema.md: schema/advschema.md
+ advanced-topics/schema-init-strategies.md: schema/schema-init-strategies.md
advanced-topics/monitoring.md: operations/monitoring.md
advanced-topics/migrating-titan.md: operations/migrating-titan.md
advanced-topics/migrating-thrift.md: operations/migrating-thrift.md
@@ -138,6 +139,7 @@ nav:
- Index Lifecycle: schema/index-management/index-lifecycle.md
- Reindexing: schema/index-management/index-reindexing.md
- Removal: schema/index-management/index-removal.md
+ - Schema Initialization: schema/schema-init-strategies.md
- Operations:
- JanusGraph Server: operations/server.md
- Deployment Scenarios: operations/deployment.md