entry : entity.getProperties().entrySet()) {
- if (null != entry.getValue()) {
- vertex.propertyWithoutUpdate(Cardinality.list, entry.getKey(), entry.getValue());
+ final GafferPopVertex vertex = new GafferPopVertex(
+ entity.getGroup(),
+ GafferCustomTypeFactory.parseForGraphSONv3(entity.getVertex()),
+ graph);
+
+ // Add the properties
+ entity.getProperties().forEach((k, v) -> {
+ if (v != null) {
+ vertex.propertyWithoutUpdate(Cardinality.list, k, GafferCustomTypeFactory.parseForGraphSONv3(v));
}
- }
+ });
+
if (gafferPopReadOnly) {
vertex.setReadOnly();
}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
index 3268c35a522..fafee9b8148 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
@@ -52,12 +52,11 @@
/**
* Custom GafferPop GraphStep provides Gaffer specific optimisations
- * for the initial GraphStep in a query. Also responsible for parsing
- * any options passed via a 'with()' call on the query.
+ * for the initial GraphStep in a query.
+ * Also responsible for parsing any options passed via a 'with()' step
+ * on the query.
*
*
- * g.with("userId", "user").V() // userId extracted to be used in the operation executions
- * g.with("dataAuths", "write-access,read-access").V() // user access controls to apply on the user
* g.with("operationOptions", ["graphId:graph1", "opt1:val1"]).V() // operation options extracted and applied
*
*/
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
index 2d2c088b95b..1b98260cc5e 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
@@ -20,6 +20,7 @@
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Property;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferPredicateFactory;
import java.util.function.Predicate;
@@ -45,7 +46,7 @@ public Predicate getGafferPredicate() {
@Override
protected boolean testId(final Element element) {
- return gafferPredicate.test(element.id());
+ return gafferPredicate.test(GafferCustomTypeFactory.parseAsCustomTypeIfValid(element.id()));
}
@Override
@@ -60,7 +61,7 @@ protected boolean testLabel(final Element element) {
@Override
protected boolean testValue(final Property property) {
- return gafferPredicate.test(property.value());
+ return gafferPredicate.test(GafferCustomTypeFactory.parseAsCustomTypeIfValid(property.value()));
}
@Override
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
index dccbd3a46f5..497eae8a485 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
@@ -31,16 +31,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.step.GafferPopGraphStep;
+import java.util.Map;
import java.util.Optional;
/**
* The {@link GraphStep} strategy for GafferPop, this will replace the default
* {@link GraphStep} of a query to add Gaffer optimisations. Such as gathering
* any {@link HasStep}s so that a Gaffer View can be constructed for the query.
- * Will also handle the translation any Cypher queries passed in a with() step
- * into a Gremlin traversal.
+ * Will also handle the translation of Cypher queries passed via a with()
+ * step in the Gremlin traversal.
*
*
* g.V().hasLabel() // replaced by GafferPopGraphStep
@@ -52,27 +54,25 @@ public final class GafferPopGraphStepStrategy extends AbstractTraversalStrategy<
private static final Logger LOGGER = LoggerFactory.getLogger(GafferPopGraphStepStrategy.class);
private static final GafferPopGraphStepStrategy INSTANCE = new GafferPopGraphStepStrategy();
- /**
- * Key used in a with step to include a opencypher query traversal
- */
- public static final String CYPHER_KEY = "cypher";
-
private GafferPopGraphStepStrategy() {
}
@Override
public void apply(final Admin, ?> traversal) {
- // Check for any options on the traversal
+ // Parse any options on the traversal
Optional optionsStrategy = traversal.getStrategies().getStrategy(OptionsStrategy.class);
- // Translate and add a cypher traversal in if that key has been set
- if (optionsStrategy.isPresent() && optionsStrategy.get().getOptions().containsKey(CYPHER_KEY)) {
- LOGGER.info("Replacing traversal with translated Cypher query");
- CypherAst ast = CypherAst.parse((String) optionsStrategy.get().getOptions().get(CYPHER_KEY));
- Admin, ?> translatedCypher = ast.buildTranslation(Translator.builder().traversal().enableCypherExtensions().build()).asAdmin();
+ if (optionsStrategy.isPresent()) {
+ Map options = optionsStrategy.get().getOptions();
+ // Translate and add a cypher traversal in if that key has been set
+ if (options.containsKey(GafferPopGraphVariables.CYPHER_KEY)) {
+ LOGGER.info("Replacing traversal with translated Cypher query");
+ CypherAst ast = CypherAst.parse((String) options.get(GafferPopGraphVariables.CYPHER_KEY));
+ Admin, ?> translatedCypher = ast.buildTranslation(Translator.builder().traversal().enableCypherExtensions().build()).asAdmin();
- // Add the cypher traversal
- TraversalHelper.insertTraversal(0, translatedCypher, traversal);
- LOGGER.debug("New traversal is: {}", traversal);
+ // Add the cypher traversal
+ TraversalHelper.insertTraversal(0, translatedCypher, traversal);
+ LOGGER.debug("New traversal is: {}", traversal);
+ }
}
TraversalHelper.getStepsOfClass(GraphStep.class, traversal).forEach(originalGraphStep -> {
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java
new file mode 100644
index 00000000000..e13817615ee
--- /dev/null
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class that helps the conversion between Gaffer custom types and
+ * standard types that Tinkerpop supports.
+ *
+ * Tinkerpop only supports a limited number of types so any custom Gaffer
+ * ones can only be passed as String representations via Gremlin so need
+ * conversion before using in Gaffer Operation chains.
+ */
+public final class GafferCustomTypeFactory {
+ /*
+ * List of supported standard types that GraphSON v3 can serialise
+ */
+ public static final List> GRAPHSONV3_TYPES = Collections.unmodifiableList(
+ Arrays.asList(
+ Boolean.class,
+ Byte[].class,
+ Date.class,
+ Double.class,
+ Float.class,
+ Integer.class,
+ List.class,
+ Long.class,
+ Map.class,
+ Set.class,
+ String.class,
+ Timestamp.class,
+ UUID.class));
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GafferCustomTypeFactory.class);
+ private static final Pattern TSTV_REGEX = Pattern.compile(".*\\[type=(?.*),\\s*subType=(?.*),\\s*value=(?.*)\\]$");
+
+ private GafferCustomTypeFactory() {
+ // Utility class
+ }
+
+ /**
+ * Returns a the relevant Object e.g. {@link TypeSubTypeValue} from the
+ * supplied value or ID, usually by parsing a specifically formatted string.
+ * As a fallback will give back the original value if no relevant type
+ * was found.
+ *
+ *
+ * "TypeSubTypeValue[type=alpha,subType=beta,value=gamma]" // returns TypeSubTypeValue object
+ * "[type=alpha,subType=beta,value=gamma]" // returns TypeSubTypeValue object
+ * "normalvalue" // returns itself
+ *
+ *
+ * @param value The value.
+ * @return The value as its relevant type.
+ */
+ public static Object parseAsCustomTypeIfValid(final Object value) {
+ if (value instanceof String) {
+ Matcher tstvMatcher = TSTV_REGEX.matcher((String) value);
+ if (tstvMatcher.matches()) {
+ // Split into a TSTV via matcher
+ LOGGER.debug("Parsing string as a TSTV: {}", value);
+ return new TypeSubTypeValue(
+ tstvMatcher.group("type"),
+ tstvMatcher.group("stype"),
+ tstvMatcher.group("val"));
+ }
+ }
+
+ // If value is collection e.g. list or set then check the values inside it
+ if (value instanceof Collection>) {
+ List