property = persistentEntity.getPersistentProperty(fieldName);
+
+ return property != null ? accessor.getProperty(property) : null;
+ }
+
+ public Object getIdFieldValue(@NonNull Object domain) {
+ return this.getFieldValue(domain, this.getIdFieldName(domain));
+ }
+}
+
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java
new file mode 100644
index 000000000000..9b57d88161f7
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/AbstractGremlinResultReader.java
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.result;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+// TODO: seems only for Vertex.
+public abstract class AbstractGremlinResultReader implements GremlinResultsReader {
+
+ /**
+ * properties's organization is a little complicated.
+ *
+ * properties is {@link LinkedHashMap}<K, V>
+ * K is {@link String}
+ * V is {@link ArrayList}<T>
+ * T is {@link LinkedHashMap}<{@link String}, {@link String}>
+ */
+ private Object readProperty(@NonNull Object value) {
+ Assert.isInstanceOf(ArrayList.class, value, "should be instance of ArrayList");
+
+ @SuppressWarnings("unchecked") final ArrayList> mapList
+ = (ArrayList>) value;
+
+ Assert.isTrue(mapList.size() == 1, "should be only 1 element in ArrayList");
+
+ return mapList.get(0).get(Constants.PROPERTY_VALUE);
+ }
+
+ protected void readResultProperties(@NonNull Map properties, @NonNull GremlinSource source) {
+ source.getProperties().clear();
+ properties.forEach((key, value) -> source.setProperty(key, this.readProperty(value)));
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java
new file mode 100644
index 000000000000..be85f5d503ac
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultEdgeReader.java
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.result;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.common.GremlinUtils;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Map;
+
+public class GremlinResultEdgeReader extends AbstractGremlinResultReader {
+
+ private void readProperties(@NonNull GremlinSource source, @Nullable Map properties) {
+ Assert.notNull(source, "source should not be null");
+ if (properties != null) {
+ properties.forEach(source::setProperty);
+ }
+ }
+
+ private void validate(List results, GremlinSource source) {
+ Assert.notNull(results, "Results should not be null.");
+ Assert.notNull(source, "GremlinSource should not be null.");
+ Assert.isTrue(results.size() == 1, "Edge should contain only one result.");
+
+ final Result result = results.get(0);
+
+ Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map");
+
+ @SuppressWarnings("unchecked") final Map map = (Map) result.getObject();
+
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_ID), "should contain id property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_LABEL), "should contain label property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain type property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_INV), "should contain inV property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_OUTV), "should contain outV property");
+ Assert.isTrue(map.get(Constants.PROPERTY_TYPE).equals(Constants.RESULT_TYPE_EDGE), "must be vertex type");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void read(@NonNull List results, @NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceEdge");
+ }
+
+ validate(results, source);
+
+ final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source;
+ final Map map = (Map) results.get(0).getObject();
+
+ this.readProperties(source, (Map) map.get(Constants.PROPERTY_PROPERTIES));
+
+ final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString();
+
+ sourceEdge.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className)));
+ sourceEdge.setId(map.get(Constants.PROPERTY_ID));
+ sourceEdge.setLabel(map.get(Constants.PROPERTY_LABEL).toString());
+ sourceEdge.setVertexIdFrom(map.get(Constants.PROPERTY_OUTV));
+ sourceEdge.setVertexIdTo(map.get(Constants.PROPERTY_INV));
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java
new file mode 100644
index 000000000000..60d047041462
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultVertexReader.java
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.result;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.common.GremlinUtils;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Map;
+
+public class GremlinResultVertexReader extends AbstractGremlinResultReader {
+
+ private void validate(List results, GremlinSource source) {
+ Assert.notNull(results, "Results should not be null.");
+ Assert.notNull(source, "GremlinSource should not be null.");
+ Assert.isTrue(results.size() == 1, "Vertex should contain only one result.");
+
+ final Result result = results.get(0);
+
+ Assert.isInstanceOf(Map.class, result.getObject(), "should be one instance of Map");
+
+ @SuppressWarnings("unchecked") final Map map = (Map) result.getObject();
+
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_ID), "should contain id property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_LABEL), "should contain label property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain type property");
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_PROPERTIES), "should contain properties property");
+ Assert.isTrue(map.get(Constants.PROPERTY_TYPE).equals(Constants.RESULT_TYPE_VERTEX), "must be vertex type");
+
+ Assert.isInstanceOf(Map.class, map.get(Constants.PROPERTY_PROPERTIES), "should be one instance of Map");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void read(@NonNull List results, @NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceVertex");
+ }
+
+ validate(results, source);
+
+ final Map map = (Map) results.get(0).getObject();
+ final Map properties = (Map) map.get(Constants.PROPERTY_PROPERTIES);
+
+ super.readResultProperties(properties, source);
+
+ final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString();
+
+ source.setIdField(GremlinUtils.getIdField(GremlinUtils.toEntityClass(className)));
+ source.setId(map.get(Constants.PROPERTY_ID));
+ source.setLabel(map.get(Constants.PROPERTY_LABEL).toString());
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java
new file mode 100644
index 000000000000..93ec56f8c92c
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsGraphReader.java
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.result;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Collections.singletonList;
+
+public class GremlinResultsGraphReader extends AbstractGremlinResultReader {
+
+ private final GremlinResultVertexReader vertexResultReader;
+ private final GremlinResultEdgeReader edgeResultReader;
+
+ public GremlinResultsGraphReader() {
+ vertexResultReader = new GremlinResultVertexReader();
+ edgeResultReader = new GremlinResultEdgeReader();
+ }
+
+ @Override
+ public void read(@NonNull List results, @NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceGraph)) {
+ throw new GremlinUnexpectedSourceTypeException("Should be instance of GremlinSourceGraph");
+ }
+
+ final GremlinSourceGraph graphSource = (GremlinSourceGraph) source;
+
+ graphSource.getVertexSet().clear();
+ graphSource.getEdgeSet().clear();
+
+ results.stream().map(this::processResult).forEach(graphSource::addGremlinSource);
+ }
+
+ private GremlinSource> processResult(Result result) {
+ final GremlinSource> source;
+ final Object obj = result.getObject();
+
+ Assert.isInstanceOf(Map.class, obj, "should be an instance of Map");
+ @SuppressWarnings("unchecked") final Map map = (Map) result.getObject();
+
+ Assert.isTrue(map.containsKey(Constants.PROPERTY_TYPE), "should contain a type property");
+ final String type = (String) map.get(Constants.PROPERTY_TYPE);
+
+ switch (type) {
+ case Constants.RESULT_TYPE_VERTEX:
+ source = new GremlinSourceVertex<>();
+ vertexResultReader.read(singletonList(result), source);
+ break;
+ case Constants.RESULT_TYPE_EDGE:
+ source = new GremlinSourceEdge<>();
+ edgeResultReader.read(singletonList(result), source);
+ break;
+ default:
+ throw new GremlinUnexpectedEntityTypeException("Unexpected result type: " + type);
+ }
+
+ return source;
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java
new file mode 100644
index 000000000000..c6def579ccaf
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/result/GremlinResultsReader.java
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.result;
+
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import org.apache.tinkerpop.gremlin.driver.Result;
+
+import java.util.List;
+
+public interface GremlinResultsReader {
+ /**
+ * Read the Gremlin returned Results to GremlinSource.
+ *
+ * @param The type of the source domain.
+ * @param results Results retrieved from the Gremlin server.
+ * @param source The GremlinSource of the results.
+ */
+ void read(List results, GremlinSource source);
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java
new file mode 100644
index 000000000000..1a54eb107eb3
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteral.java
@@ -0,0 +1,82 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.script;
+
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+
+import java.util.List;
+
+/**
+ * Provider interface to generate different query to gremlin server.
+ * The scripts return queries in steps, organized by List.
+ */
+public interface GremlinScriptLiteral {
+ /**
+ * Generate the insert query from source (Vertex, Edge or Graph).
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return insert script
+ */
+ List generateInsertScript(GremlinSource source);
+
+ /**
+ * Generate the deleteAll query from source (Vertex, Edge or Graph).
+ *
+ * @return deleteAll script
+ */
+ List generateDeleteAllScript();
+
+ /**
+ * Generate the deleteAll By Domain Class query from source (Vertex, Edge or Graph).
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return deleteAllByClass script
+ */
+ List generateDeleteAllByClassScript(GremlinSource source);
+
+ /**
+ * Generate the findById query from source (Vertex, Edge).
+ *
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return findById script script
+ */
+ List generateFindByIdScript(GremlinSource source);
+
+ /**
+ * Generate the update query from source (Vertex, Edge or Graph).
+ *
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return update script
+ */
+ List generateUpdateScript(GremlinSource source);
+
+ /**
+ * Generate the findAll query from source (Vertex, Edge or Graph).
+ *
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return findAll script
+ */
+ List generateFindAllScript(GremlinSource source);
+
+ /**
+ * Generate the DeleteById query from source (Vertex, Edge or Graph).
+ *
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return deleteById script
+ */
+ List generateDeleteByIdScript(GremlinSource source);
+
+ /**
+ * Generate the Count query from Source (Vertex, Edge)
+ *
+ * @param The type of domain.
+ * @param source the gremlin source
+ * @return count query
+ */
+ List generateCountScript(GremlinSource source);
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java
new file mode 100644
index 000000000000..29d76ec72f45
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralEdge.java
@@ -0,0 +1,156 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.script;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.common.GremlinEntityType;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceEdge;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class GremlinScriptLiteralEdge implements GremlinScriptLiteral {
+
+ private static final String FROM_ALIAS = "from";
+ private static final String TO_ALIAS = "to";
+
+ private String generateEdgeDirection(@NonNull String from, @NonNull String to) {
+ Assert.notNull(from, "from should not be null");
+ Assert.notNull(to, "to should not be null");
+ return String.format("from('%s').to('%s')", from, to);
+ }
+
+ @Override
+ public List generateInsertScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ final GremlinSourceEdge sourceEdge = (GremlinSourceEdge) source;
+ final List scriptList = new ArrayList<>();
+
+ scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g
+ scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(sourceEdge.getVertexIdFrom(), GremlinEntityType.VERTEX)); // V(id)
+ scriptList.add(GremlinScriptLiteralHelper.generateAsWithAlias(FROM_ALIAS)); // from('from')
+ scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(sourceEdge.getVertexIdTo(), GremlinEntityType.VERTEX)); // V(id)
+ scriptList.add(GremlinScriptLiteralHelper.generateAsWithAlias(TO_ALIAS)); // to('to')
+ scriptList.add(GremlinScriptLiteralHelper.generateAddEntityWithLabel(sourceEdge.getLabel(), GremlinEntityType.EDGE)); // addE(label)
+ scriptList.add(generateEdgeDirection(FROM_ALIAS, TO_ALIAS)); // from('from').to('to')
+
+ source.getId().ifPresent(id -> scriptList.add(GremlinScriptLiteralHelper.generatePropertyWithRequiredId(id))); // property(id, xxx)
+
+ scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties()));
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateDeleteAllScript() {
+ return Collections.singletonList(Constants.GREMLIN_SCRIPT_EDGE_DROP_ALL);
+ }
+
+ @Override
+ public List generateDeleteAllByClassScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E()
+ GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label')
+ Constants.GREMLIN_PRIMITIVE_DROP // drop()
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateFindByIdScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E()
+ GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()) // hasId(xxx)
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateUpdateScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ final List scriptList = new ArrayList<>();
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g
+ scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(source.getId().get(), GremlinEntityType.EDGE)); // E(id)
+
+ scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties()));
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateFindAllScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ final String className = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString();
+ Assert.notNull(className, "GremlinSource should contain predefined className");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E()
+ GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label')
+ GremlinScriptLiteralHelper.generateHas(Constants.GREMLIN_PROPERTY_CLASSNAME, className) // has(_classname, 'xxxxxx')
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateDeleteByIdScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_EDGE_ALL, // E()
+ GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()), // hasId(xxx)
+ Constants.GREMLIN_PRIMITIVE_DROP // drop()
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateCountScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceEdge)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceEdge");
+ }
+
+ return Collections.singletonList(Constants.GREMLIN_SCRIPT_EDGE_ALL);
+ }
+}
+
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java
new file mode 100644
index 000000000000..033cf2f0204c
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralGraph.java
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.script;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceGraph;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+public class GremlinScriptLiteralGraph implements GremlinScriptLiteral {
+
+ private final GremlinScriptLiteralVertex scriptVertex = new GremlinScriptLiteralVertex();
+
+ private final GremlinScriptLiteralEdge scriptEdge = new GremlinScriptLiteralEdge();
+
+ @Override
+ public List generateInsertScript(@NonNull GremlinSource source) {
+ return generateInsertUpdateScript(source,
+ scriptVertex::generateInsertScript,
+ scriptEdge::generateInsertScript);
+ }
+
+ @Override
+ public List generateDeleteAllScript() {
+ return Arrays.asList(Constants.GREMLIN_SCRIPT_EDGE_DROP_ALL, Constants.GREMLIN_QUERY_BARRIER, Constants.GREMLIN_SCRIPT_VERTEX_DROP_ALL);
+ }
+
+ @Override
+ public List generateDeleteAllByClassScript(@NonNull GremlinSource source) {
+ return generateDeleteAllScript();
+ }
+
+ @Override
+ public List generateFindByIdScript(@Nullable GremlinSource source) {
+ throw new UnsupportedOperationException("Gremlin graph cannot findById by single query.");
+ }
+
+ @Override
+ public List generateUpdateScript(@NonNull GremlinSource source) {
+ return generateInsertUpdateScript(source,
+ scriptVertex::generateUpdateScript,
+ scriptEdge::generateUpdateScript);
+ }
+
+ private List generateInsertUpdateScript(@NonNull GremlinSource source,
+ @NonNull Function, List> vertexHandler,
+ @NonNull Function, List> edgeHandler) {
+ if (!(source instanceof GremlinSourceGraph)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceGraph");
+ }
+
+ final List scriptList = new ArrayList<>();
+ final GremlinSourceGraph sourceGraph = (GremlinSourceGraph) source;
+ final List> vertexes = sourceGraph.getVertexSet();
+ final List> edges = sourceGraph.getEdgeSet();
+
+ vertexes.forEach(vertex -> scriptList.addAll(vertexHandler.apply(vertex)));
+ scriptList.add(Constants.GREMLIN_QUERY_BARRIER);
+ edges.forEach(edge -> scriptList.addAll(edgeHandler.apply(edge)));
+
+ return scriptList;
+ }
+
+ @Override
+ public List generateDeleteByIdScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceGraph)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceGraph");
+ }
+
+ return this.generateDeleteAllScript();
+ }
+
+ @Override
+ public List generateFindAllScript(@NonNull GremlinSource source) {
+ throw new UnsupportedOperationException("Gremlin graph cannot be findAll.");
+ }
+
+ public List generateIsEmptyScript() {
+ final List scriptList = Arrays.asList(Constants.GREMLIN_PRIMITIVE_GRAPH, Constants.GREMLIN_PRIMITIVE_VERTEX_ALL);
+ final String query = String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList);
+
+ return Collections.singletonList(query);
+ }
+
+ @Override
+ public List generateCountScript(@NonNull GremlinSource source) {
+ throw new UnsupportedOperationException("Gremlin graph counting is not available.");
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java
new file mode 100644
index 000000000000..b9f94ae98412
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralHelper.java
@@ -0,0 +1,186 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.script;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.common.GremlinEntityType;
+import com.microsoft.spring.data.gremlin.common.GremlinUtils;
+import com.microsoft.spring.data.gremlin.annotation.GeneratedValue;
+import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException;
+import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class GremlinScriptLiteralHelper {
+
+ static String generateEntityWithRequiredId(@NonNull Object id, GremlinEntityType type) {
+ Assert.notNull(id, "id should not be null");
+ Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type");
+
+ final String prefix = (type == GremlinEntityType.VERTEX) ? "V" : "E";
+
+ if (id instanceof String) {
+ return prefix + String.format("('%s')", id);
+ } else if (id instanceof Integer) {
+ return prefix + String.format("(%d)", id);
+ } else if (id instanceof Long) {
+ return prefix + String.format("(%d)", id);
+ }
+
+ throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported");
+ }
+
+ static String generatePropertyWithRequiredId(@NonNull Object id) {
+ if (id instanceof String) {
+ return String.format("property(id, '%s')", id);
+ } else if (id instanceof Integer) {
+ return String.format("property(id, %d)", id);
+ } else if (id instanceof Long) {
+ return String.format("property(id, %d)", id);
+ }
+
+ throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported");
+ }
+
+ static String generateAsWithAlias(@NonNull String alias) {
+ return String.format("as('%s')", alias);
+ }
+
+ static String generateAddEntityWithLabel(@NonNull String label, GremlinEntityType type) {
+ Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type");
+
+ final String prefix = (type == GremlinEntityType.VERTEX) ? "addV" : "addE";
+
+ return prefix + String.format("('%s')", label);
+ }
+
+ static List completeScript(@NonNull List scriptList) {
+ return Collections.singletonList(String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList));
+ }
+
+ public static String generateHasLabel(@NonNull String label) {
+ return String.format("has(label, '%s')", label);
+ }
+
+ public static String generateHasId(@NonNull Object id) {
+ if (id instanceof String) {
+ return String.format("hasId('%s')", id);
+ } else if (id instanceof Integer) {
+ return String.format("hasId(%d)", id);
+ } else if (id instanceof Long) {
+ return String.format("hasId(%d)", id);
+ } else {
+ throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long");
+ }
+ }
+
+ public static String generateHasId(@NonNull Object id, @NonNull Field idFiled) {
+ if (!idFiled.isAnnotationPresent(GeneratedValue.class)) {
+ return generateHasId(id);
+ } else if (id instanceof String) {
+ return String.format("hasId('%s')", id);
+ } else if (id instanceof Integer) {
+ return String.format("hasId(%dL)", id);
+ } else if (id instanceof Long) {
+ return String.format("hasId(%dL)", id);
+ } else {
+ throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long");
+ }
+ }
+
+ private static String generateProperty(@NonNull String name, @NonNull String value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_STRING, name, value);
+ }
+
+ private static String generateProperty(@NonNull String name, @NonNull Integer value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value);
+ }
+
+ private static String generateProperty(@NonNull String name, @NonNull Boolean value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_BOOLEAN, name, value);
+ }
+
+ private static String generateProperty(@NonNull String name, @NonNull Long value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value);
+ }
+
+ private static String generateProperty(@NonNull String name, @NonNull Object value) {
+ if (value instanceof Integer) {
+ return generateProperty(name, (Integer) value);
+ } else if (value instanceof Boolean) {
+ return generateProperty(name, (Boolean) value);
+ } else if (value instanceof String) {
+ return generateProperty(name, (String) value);
+ } else if (value instanceof Date) {
+ return generateProperty(name, GremlinUtils.timeToMilliSeconds(value));
+ } else {
+ final String propertyScript;
+
+ try {
+ propertyScript = generateProperty(name, GremlinUtils.getObjectMapper().writeValueAsString(value));
+ } catch (JsonProcessingException e) {
+ throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e);
+ }
+
+ return propertyScript;
+ }
+ }
+
+ protected static List generateProperties(@NonNull final Map properties) {
+ final List scripts = new ArrayList<>();
+
+ properties.entrySet().stream().filter(e -> e.getValue() != null)
+ .forEach(e -> scripts.add(generateProperty(e.getKey(), e.getValue())));
+
+ return scripts;
+ }
+
+ private static String generateHas(@NonNull String name, @NonNull Integer value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value);
+ }
+
+ private static String generateHas(@NonNull String name, @NonNull Boolean value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_HAS_BOOLEAN, name, value);
+ }
+
+ private static String generateHas(@NonNull String name, @NonNull String value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_HAS_STRING, name, value);
+ }
+
+ private static String generateHas(@NonNull String name, @NonNull Long value) {
+ return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value);
+ }
+
+ // TODO: should move to query method part.
+ public static String generateHas(@NonNull String name, @NonNull Object value) {
+
+ if (value instanceof Integer) {
+ return generateHas(name, (Integer) value);
+ } else if (value instanceof Boolean) {
+ return generateHas(name, (Boolean) value);
+ } else if (value instanceof String) {
+ return generateHas(name, (String) value);
+ } else if (value instanceof Date) {
+ return generateHas(name, GremlinUtils.timeToMilliSeconds(value));
+ } else {
+ final String hasScript;
+
+ try {
+ hasScript = generateHas(name, GremlinUtils.getObjectMapper().writeValueAsString(value));
+ } catch (JsonProcessingException e) {
+ throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e);
+ }
+
+ return hasScript;
+ }
+ }
+}
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java
new file mode 100644
index 000000000000..44dedb456946
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/script/GremlinScriptLiteralVertex.java
@@ -0,0 +1,141 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.script;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.common.GremlinEntityType;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSource;
+import com.microsoft.spring.data.gremlin.conversion.source.GremlinSourceVertex;
+import com.microsoft.spring.data.gremlin.exception.GremlinUnexpectedSourceTypeException;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class GremlinScriptLiteralVertex implements GremlinScriptLiteral {
+
+ @Override
+ public List generateInsertScript(@NonNull GremlinSource source) {
+ Assert.notNull(source, "source should not be null");
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ final List scriptList = new ArrayList<>();
+
+ scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g
+ scriptList.add(GremlinScriptLiteralHelper.generateAddEntityWithLabel(source.getLabel(), GremlinEntityType.VERTEX)); // addV('label')
+
+ source.getId().ifPresent(id -> scriptList.add(GremlinScriptLiteralHelper.generatePropertyWithRequiredId(id))); // property(id, xxx)
+
+ scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties()));
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateDeleteAllScript() {
+ return Collections.singletonList(Constants.GREMLIN_SCRIPT_VERTEX_DROP_ALL);
+ }
+
+ @Override
+ public List generateDeleteAllByClassScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V()
+ GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label')
+ Constants.GREMLIN_PRIMITIVE_DROP // drop()
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateFindByIdScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V()
+ GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()) // hasId(xxx)
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateUpdateScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ final List scriptList = new ArrayList<>();
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ scriptList.add(Constants.GREMLIN_PRIMITIVE_GRAPH); // g
+ scriptList.add(GremlinScriptLiteralHelper.generateEntityWithRequiredId(source.getId().get(), GremlinEntityType.VERTEX)); // V(id)
+ scriptList.addAll(GremlinScriptLiteralHelper.generateProperties(source.getProperties()));
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateFindAllScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ final String classname = source.getProperties().get(Constants.GREMLIN_PROPERTY_CLASSNAME).toString();
+ Assert.notNull(classname, "GremlinSource should contain predefined classname");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // V()
+ GremlinScriptLiteralHelper.generateHasLabel(source.getLabel()), // has(label, 'label')
+ GremlinScriptLiteralHelper.generateHas(Constants.GREMLIN_PROPERTY_CLASSNAME, classname) // has(_classname, 'xxxxxx')
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateDeleteByIdScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ Assert.isTrue(source.getId().isPresent(), "GremlinSource should contain id.");
+
+ final List scriptList = Arrays.asList(
+ Constants.GREMLIN_PRIMITIVE_GRAPH, // g
+ Constants.GREMLIN_PRIMITIVE_VERTEX_ALL, // E()
+ GremlinScriptLiteralHelper.generateHasId(source.getId().get(), source.getIdField()), // hasId(xxx)
+ Constants.GREMLIN_PRIMITIVE_DROP // drop()
+ );
+
+ return GremlinScriptLiteralHelper.completeScript(scriptList);
+ }
+
+ @Override
+ public List generateCountScript(@NonNull GremlinSource source) {
+ if (!(source instanceof GremlinSourceVertex)) {
+ throw new GremlinUnexpectedSourceTypeException("should be the instance of GremlinSourceVertex");
+ }
+
+ return Collections.singletonList(Constants.GREMLIN_SCRIPT_VERTEX_ALL);
+ }
+}
+
diff --git a/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java
new file mode 100644
index 000000000000..602b04aa5b4b
--- /dev/null
+++ b/sdk/spring/azure-spring-data-gremlin/src/main/java/com/microsoft/spring/data/gremlin/conversion/source/AbstractGremlinSource.java
@@ -0,0 +1,175 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.spring.data.gremlin.conversion.source;
+
+import com.microsoft.spring.data.gremlin.common.Constants;
+import com.microsoft.spring.data.gremlin.annotation.GeneratedValue;
+import com.microsoft.spring.data.gremlin.conversion.MappingGremlinConverter;
+import com.microsoft.spring.data.gremlin.conversion.result.GremlinResultsReader;
+import com.microsoft.spring.data.gremlin.conversion.script.GremlinScriptLiteral;
+import com.microsoft.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class AbstractGremlinSource implements GremlinSource {
+
+ private Object id;
+
+ private String label;
+
+ private Field idField;
+
+ private Class domainClass;
+
+ private Map properties;
+
+ private GremlinScriptLiteral scriptLiteral;
+
+ private GremlinSourceWriter sourceWriter;
+
+ private GremlinSourceReader sourceReader;
+
+ private GremlinResultsReader resultReader;
+
+ protected AbstractGremlinSource() {
+ this.properties = new HashMap<>();
+ }
+
+ protected AbstractGremlinSource(Class