From 031c2291554b228d924ea031714b414c0593b52f Mon Sep 17 00:00:00 2001 From: shirly121 Date: Wed, 21 Jun 2023 17:15:42 +0800 Subject: [PATCH] [GIE Compiler] convert label id to name in label() step --- interactive_engine/compiler/Makefile | 1 + .../gremlin/InterOpCollectionBuilder.java | 8 +- .../suite/standard/IrGremlinQueryTest.java | 90 +++++++++- .../gremlin/result/GremlinResultAnalyzer.java | 4 +- .../result/GremlinResultParserFactory.java | 151 +--------------- .../gremlin/result/LabelParser.java | 166 ++++++++++++++++++ .../gremlin/result/ProjectResultParser.java | 137 +++++++++++++++ .../gremlin/result/UnionResultParser.java | 59 +++++++ .../transform/StepTransformFactory.java | 22 --- .../transform/TraversalParentTransform.java | 7 + .../graphscope/gremlin/IdLabelTest.java | 4 +- 11 files changed, 467 insertions(+), 182 deletions(-) create mode 100644 interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/LabelParser.java create mode 100644 interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/ProjectResultParser.java create mode 100644 interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/UnionResultParser.java diff --git a/interactive_engine/compiler/Makefile b/interactive_engine/compiler/Makefile index e26c14678a5f..f3eee656f3cf 100644 --- a/interactive_engine/compiler/Makefile +++ b/interactive_engine/compiler/Makefile @@ -53,6 +53,7 @@ submit: run: cd $(CUR_DIR) && $(java) \ + -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 \ -cp ".:./target/libs/*:./target/compiler-0.0.1-SNAPSHOT.jar" \ -Djna.library.path=../executor/ir/target/release \ -Dgraph.schema=${graph.schema} \ diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/InterOpCollectionBuilder.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/InterOpCollectionBuilder.java index d76023d2446c..c61a8fc210d7 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/InterOpCollectionBuilder.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/InterOpCollectionBuilder.java @@ -73,7 +73,9 @@ public InterOpCollection build() throws OpArgIllegalException, UnsupportedStepEx || Utils.equalClass(step, MeanGlobalStep.class)) { opList.add(StepTransformFactory.AGGREGATE_STEP.apply(step)); } else if (Utils.equalClass(step, PropertiesStep.class) - || Utils.equalClass(step, PropertyMapStep.class)) { + || Utils.equalClass(step, PropertyMapStep.class) + || Utils.equalClass(step, LabelStep.class) + || Utils.equalClass(step, IdStep.class)) { opList.add(StepTransformFactory.VALUES_STEP.apply(step)); } else if (Utils.equalClass(step, IsStep.class)) { opList.add(StepTransformFactory.IS_STEP.apply(step)); @@ -135,10 +137,6 @@ public InterOpCollection build() throws OpArgIllegalException, UnsupportedStepEx opList.add(StepTransformFactory.SUBGRAPH_STEP.apply(step)); } else if (Utils.equalClass(step, IdentityStep.class)) { opList.add(StepTransformFactory.IDENTITY_STEP.apply(step)); - } else if (Utils.equalClass(step, IdStep.class)) { - opList.add(StepTransformFactory.ID_STEP.apply(step)); - } else if (Utils.equalClass(step, LabelStep.class)) { - opList.add(StepTransformFactory.LABEL_STEP.apply(step)); } else if (Utils.equalClass(step, ConstantStep.class)) { opList.add(StepTransformFactory.CONSTANT_STEP.apply(step)); } else { diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java index 632d97cacb20..1df4449ab059 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java @@ -26,11 +26,16 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.structure.Column; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.junit.Assert; import org.junit.Test; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; public abstract class IrGremlinQueryTest extends AbstractGremlinProcessTest { @@ -55,6 +60,16 @@ public abstract class IrGremlinQueryTest extends AbstractGremlinProcessTest { public abstract Traversal get_g_V_matchXa_out_b__b_in_cX_select_c_out_dedup_values(); + public abstract Traversal get_g_V_has_name_marko_label(); + + public abstract Traversal get_g_V_has_name_marko_select_by_T_label(); + + public abstract Traversal get_g_V_has_name_marko_select_by_label(); + + public abstract Traversal get_g_E_has_weight_0_5_f_label(); + + public abstract Traversal> get_g_V_a_out_b_select_a_b_by_label_id(); + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) @Test public void g_V_group_by_by_dedup_count_test() { @@ -211,6 +226,47 @@ public void g_V_matchXa_out_b__b_in_cX_select_c_out_dedup_values() { Assert.assertEquals(2, counter); } + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_has_name_marko_label() { + final Traversal traversal = get_g_V_has_name_marko_label(); + printTraversalForm(traversal); + Assert.assertEquals("person", traversal.next()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_E_has_weight_0_5_f_label() { + final Traversal traversal = get_g_E_has_weight_0_5_f_label(); + printTraversalForm(traversal); + Assert.assertEquals("knows", traversal.next()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_has_name_marko_select_by_T_label() { + final Traversal traversal = get_g_V_has_name_marko_select_by_T_label(); + printTraversalForm(traversal); + Assert.assertEquals("person", traversal.next()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_has_name_marko_select_by_label() { + final Traversal traversal = get_g_V_has_name_marko_select_by_label(); + printTraversalForm(traversal); + Assert.assertEquals("person", traversal.next()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_a_out_b_select_a_b_by_label_id() { + final Traversal> traversal = + get_g_V_a_out_b_select_a_b_by_label_id(); + printTraversalForm(traversal); + Assert.assertEquals("{a=person, b=lop}", traversal.next().toString()); + } + public static class Traversals extends IrGremlinQueryTest { @Override @@ -271,5 +327,37 @@ public Traversal get_g_V_matchXa_knows_b__b_created_cX_select_c_ .dedup() .values("name"); } + + @Override + public Traversal get_g_V_has_name_marko_label() { + return g.V().has("name", "marko").label(); + } + + @Override + public Traversal get_g_V_has_name_marko_select_by_T_label() { + return g.V().has("name", "marko").as("a").select("a").by(T.label); + } + + @Override + public Traversal get_g_V_has_name_marko_select_by_label() { + return g.V().has("name", "marko").as("a").select("a").by(__.label()); + } + + @Override + public Traversal get_g_E_has_weight_0_5_f_label() { + return g.E().has("weight", 0.5f).label(); + } + + @Override + public Traversal> get_g_V_a_out_b_select_a_b_by_label_id() { + return g.V().has("name", "marko") + .as("a") + .out() + .has("name", "lop") + .as("b") + .select("a", "b") + .by(label()) + .by("name"); + } } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultAnalyzer.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultAnalyzer.java index 1aba6c0a5142..e507c2bb2168 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultAnalyzer.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultAnalyzer.java @@ -59,12 +59,12 @@ public static GremlinResultParser analyze(Traversal traversal) { || Utils.equalClass(step, IdStep.class) || Utils.equalClass(step, LabelStep.class) || Utils.equalClass(step, ConstantStep.class)) { - parserType = GremlinResultParserFactory.PROJECT_VALUE; + parserType = ProjectResultParser.create(step); } else if (Utils.equalClass(step, GroupCountStep.class) || Utils.equalClass(step, GroupStep.class)) { parserType = GroupResultParser.create(step); } else if (Utils.equalClass(step, UnionStep.class)) { - parserType = GremlinResultParserFactory.UNION; + parserType = UnionResultParser.create(step); } else if (Utils.equalClass(step, SubgraphStep.class)) { parserType = GremlinResultParserFactory.SUBGRAPH; } else if (Utils.equalClass(step, HasStep.class) diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultParserFactory.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultParserFactory.java index 59ef7d294ad6..ea69363a46e0 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultParserFactory.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/GremlinResultParserFactory.java @@ -16,20 +16,14 @@ package com.alibaba.graphscope.gremlin.result; -import com.alibaba.graphscope.common.jna.type.FfiKeyType; -import com.alibaba.graphscope.gaia.proto.Common; import com.alibaba.graphscope.gaia.proto.IrResult; import com.alibaba.graphscope.gremlin.exception.GremlinResultParserException; -import com.alibaba.graphscope.gremlin.transform.alias.AliasManager; import org.apache.tinkerpop.gremlin.structure.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; public enum GremlinResultParserFactory implements GremlinResultParser { GRAPH_ELEMENT { @@ -44,6 +38,7 @@ public Object parseFrom(IrResult.Results results) { return graphElement; } }, + SINGLE_VALUE { @Override public Object parseFrom(IrResult.Results results) { @@ -51,151 +46,7 @@ public Object parseFrom(IrResult.Results results) { return ParserUtils.parseEntry(entry); } }, - PROJECT_VALUE { - // values("name") -> key: head, value: "marko" - // valueMap("name") -> key: head, value: {name, "marko"} - // select("a").by("name") -> key: head, value: "marko" - // select("a", "b").by("name") -> key: a, value: "marko"; key: b, value: "josh" - // select("a", "b").by(valueMap("name")) -> key: a, value: {name, "marko"}; key: b, value: - // {name, "josh"} - @Override - public Object parseFrom(IrResult.Results results) { - logger.debug("{}", results); - IrResult.Record record = results.getRecord(); - logger.debug("{}", record); - Map projectResult = new HashMap<>(); - record.getColumnsList() - .forEach( - column -> { - String tag = getColumnKeyAsResultKey(column.getNameOrId()); - Object parseEntry = ParserUtils.parseEntry(column.getEntry()); - if (parseEntry instanceof Map) { - Map projectTags = (Map) parseEntry; - // return empty Map if none properties - Map tagEntry = - (Map) - projectResult.computeIfAbsent( - tag, k1 -> new HashMap<>()); - projectTags.forEach( - (k, v) -> { - if (!(v instanceof EmptyValue)) { - String nameOrId = null; - if (k - instanceof - List) { // valueMap("name") -> Map<["", - // "name"], value> - nameOrId = (String) ((List) k).get(1); - } else if (k - instanceof - String) { // valueMap() -> Map<"name", - // value> - nameOrId = (String) k; - } else if (k - instanceof - Number) { // valueMap() -> Map<1, value> - nameOrId = String.valueOf(k); - } - if (nameOrId == null || nameOrId.isEmpty()) { - throw new GremlinResultParserException( - "map value should have property" - + " key"); - } - String property = getPropertyName(nameOrId); - tagEntry.put( - property, Collections.singletonList(v)); - } - }); - } else { - if (!(parseEntry instanceof EmptyValue)) { - projectResult.put(tag, parseEntry); - } - } - }); - if (projectResult.isEmpty()) { - return EmptyValue.INSTANCE; - } else if (projectResult.size() == 1) { - return projectResult.entrySet().iterator().next().getValue(); - } else { - return projectResult; - } - } - // a_1 -> a, i.e. g.V().as("a").select("a") - // name_1 -> name, i.e. g.V().values("name") - // a_name_1 -> a, i.e. g.V().as("a").select("a").by("name") - private String getColumnKeyAsResultKey(Common.NameOrId columnKey) { - if (columnKey.getItemCase() == Common.NameOrId.ItemCase.ITEM_NOT_SET) { - return ""; - } - switch (columnKey.getItemCase()) { - case ITEM_NOT_SET: - return ""; - case NAME: - String key = columnKey.getName(); - return AliasManager.getPrefix(key); - case ID: - return String.valueOf(columnKey.getId()); - default: - throw new GremlinResultParserException(columnKey.getItemCase() + " is invalid"); - } - } - - // propertyId is in String format, i.e. "1" - private String getPropertyName(String nameOrId) { - Common.NameOrId.Builder builder = Common.NameOrId.newBuilder(); - if (nameOrId.matches("^[0-9]+$")) { - builder.setId(Integer.valueOf(nameOrId)); - } else { - builder.setName(nameOrId); - } - return ParserUtils.getKeyName(builder.build(), FfiKeyType.Column); - } - }, - UNION { - @Override - public Object parseFrom(IrResult.Results results) { - GremlinResultParser resultParser = inferFromIrResults(results); - return resultParser.parseFrom(results); - } - - // try to infer from the results - private GremlinResultParser inferFromIrResults(IrResult.Results results) { - int columns = results.getRecord().getColumnsList().size(); - logger.debug("result is {}", results); - if (columns == 1) { - IrResult.Entry entry = ParserUtils.getHeadEntry(results); - switch (entry.getInnerCase()) { - case ELEMENT: - IrResult.Element element = entry.getElement(); - if (element.getInnerCase() == IrResult.Element.InnerCase.VERTEX - || element.getInnerCase() == IrResult.Element.InnerCase.EDGE - || element.getInnerCase() - == IrResult.Element.InnerCase.GRAPH_PATH) { - return GRAPH_ELEMENT; - } else if (element.getInnerCase() == IrResult.Element.InnerCase.OBJECT) { - Common.Value value = element.getObject(); - if (value.getItemCase() - == Common.Value.ItemCase.PAIR_ARRAY) { // project - return PROJECT_VALUE; - } else { // simple type - return SINGLE_VALUE; - } - } else { - throw new GremlinResultParserException( - element.getInnerCase() + " is invalid"); - } - case COLLECTION: // path() - default: - throw new GremlinResultParserException( - entry.getInnerCase() + " is unsupported yet"); - } - } else if (columns > 1) { // project or group - return PROJECT_VALUE; - } else { - throw new GremlinResultParserException("columns should not be empty"); - } - } - }, SUBGRAPH { @Override public Object parseFrom(IrResult.Results results) { diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/LabelParser.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/LabelParser.java new file mode 100644 index 000000000000..f9fe470637d2 --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/LabelParser.java @@ -0,0 +1,166 @@ +/* + * Copyright 2020 Alibaba Group Holding Limited. + * + * 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 com.alibaba.graphscope.gremlin.result; + +import com.alibaba.graphscope.common.jna.type.FfiKeyType; +import com.alibaba.graphscope.gaia.proto.Common; +import com.alibaba.graphscope.gremlin.transform.TraversalParentTransformFactory; +import com.google.common.collect.Lists; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.TokenTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.*; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.structure.T; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public abstract class LabelParser { + public Object parseLabelInProjectResults(Map originalValues, Step step) { + return parseLabel(originalValues, StringUtils.EMPTY, step, EmptyStep.instance()); + } + + /** + * parse label id to name for each value in original values + * @param values original values before parsing + * @param tag with stepOrTraversal help to decide the {@link LabelType} + * @param stepOrTraversal + * @param parent parent of the stepOrTraversal + * @return + */ + public Object parseLabel(Object values, String tag, Object stepOrTraversal, Step parent) { + if (stepOrTraversal instanceof SelectOneStep || stepOrTraversal instanceof SelectStep) { + Iterator> projectIterator = + TraversalParentTransformFactory.PROJECT_BY_STEP + .getProjectTraversals((TraversalParent) stepOrTraversal) + .entrySet() + .iterator(); + Iterator valuesIterator = ((Map) values).entrySet().iterator(); + while (valuesIterator.hasNext() && projectIterator.hasNext()) { + Map.Entry valuesEntry = valuesIterator.next(); + Map.Entry projectEntry = projectIterator.next(); + Traversal.Admin admin = projectEntry.getValue(); + valuesEntry.setValue( + parseLabel( + valuesEntry.getValue(), + projectEntry.getKey(), + (admin == null || admin.getSteps().isEmpty()) + ? admin + : admin.getEndStep(), + (Step) stepOrTraversal)); + } + return values; + } else if (values instanceof Map) { + Iterator valuesIterator = ((Map) values).entrySet().iterator(); + while (valuesIterator.hasNext()) { + Map.Entry valuesEntry = valuesIterator.next(); + if (!(stepOrTraversal instanceof ElementMapStep) + || valuesEntry.getKey().equals(T.label.getAccessor())) { + valuesEntry.setValue( + parseLabelByType( + valuesEntry.getValue(), + getLabelType(tag, stepOrTraversal, parent))); + } + } + return values; + } else { + return parseLabelByType(values, getLabelType(tag, stepOrTraversal, parent)); + } + } + + private Object parseLabelByType(Object original, LabelType type) { + switch (type) { + case VERTEX_LABEL: + return parseLabelByFfiType(original, FfiKeyType.Entity); + case EDGE_LABEL: + return parseLabelByFfiType(original, FfiKeyType.Relation); + default: + return original; + } + } + + private Object parseLabelByFfiType(Object label, FfiKeyType ffiKeyType) { + if (label instanceof Number) { + return ParserUtils.getKeyName( + Common.NameOrId.newBuilder().setId(((Number) label).intValue()).build(), + ffiKeyType); + } else if (label instanceof List) { + List parseLabels = Lists.newArrayList(); + for (Object o : (List) label) { + parseLabels.add(parseLabelByFfiType(o, ffiKeyType)); + } + return parseLabels; + } else { + return label; + } + } + + private LabelType getLabelType(String tag, Object traversalOrStep, Step parent) { + if (!containsLabel(traversalOrStep)) { + return LabelType.NONE; + } + Step tagStep = EmptyStep.instance(); // step aliased as 'tag' + if (traversalOrStep instanceof Traversal) { + tagStep = parent; + } else if (traversalOrStep instanceof Step) { + tagStep = + (parent instanceof SelectStep || parent instanceof SelectOneStep) + ? parent + : (Step) traversalOrStep; + } + do { + tagStep = tagStep.getPreviousStep(); + } while (tagStep != EmptyStep.instance() + && !ObjectUtils.isEmpty(tag) + && !tagStep.getLabels().contains(tag)); + if (tagStep instanceof GraphStep) { + return ((GraphStep) tagStep).returnsVertex() + ? LabelType.VERTEX_LABEL + : LabelType.EDGE_LABEL; + } else if (tagStep instanceof VertexStep) { + return ((VertexStep) tagStep).returnsVertex() + ? LabelType.VERTEX_LABEL + : LabelType.EDGE_LABEL; + } else if (tagStep instanceof EdgeVertexStep || tagStep instanceof EdgeOtherVertexStep) { + return LabelType.VERTEX_LABEL; + } else { + return LabelType.NONE; + } + } + + // check if output of the step or traversal contains label id + private boolean containsLabel(Object traversalOrStep) { + return (traversalOrStep instanceof TokenTraversal + && ((TokenTraversal) traversalOrStep) + .getToken() + .equals(T.label)) // select("a").by(T.label) + || traversalOrStep instanceof LabelStep // g.V().label() + || traversalOrStep instanceof ElementMapStep; // g.V().elementMap() + } + + private enum LabelType { + VERTEX_LABEL, + EDGE_LABEL, + NONE + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/ProjectResultParser.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/ProjectResultParser.java new file mode 100644 index 000000000000..708a1585cacd --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/ProjectResultParser.java @@ -0,0 +1,137 @@ +/* + * Copyright 2020 Alibaba Group Holding Limited. + * + * 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 com.alibaba.graphscope.gremlin.result; + +import com.alibaba.graphscope.common.jna.type.FfiKeyType; +import com.alibaba.graphscope.gaia.proto.Common; +import com.alibaba.graphscope.gaia.proto.IrResult; +import com.alibaba.graphscope.gremlin.exception.GremlinResultParserException; +import com.alibaba.graphscope.gremlin.transform.alias.AliasManager; + +import org.apache.tinkerpop.gremlin.process.traversal.Step; + +import java.util.*; + +// values("name") -> key: head, value: "marko" +// valueMap("name") -> key: head, value: {name, "marko"} +// select("a").by("name") -> key: head, value: "marko" +// select("a", "b").by("name") -> key: a, value: "marko"; key: b, value: "josh" +// select("a", "b").by(valueMap("name")) -> key: a, value: {name, "marko"}; key: b, value: +// {name, "josh"} +public class ProjectResultParser extends LabelParser implements GremlinResultParser { + private final Step step; + + private ProjectResultParser(Step step) { + this.step = step; + } + + public static ProjectResultParser create(Step step) { + return new ProjectResultParser(step); + } + + @Override + public Object parseFrom(IrResult.Results results) { + IrResult.Record record = results.getRecord(); + Map projectResult = new LinkedHashMap<>(); + record.getColumnsList() + .forEach( + column -> { + String tag = getColumnKeyAsResultKey(column.getNameOrId()); + Object parseEntry = ParserUtils.parseEntry(column.getEntry()); + if (parseEntry instanceof Map) { + Map projectTags = (Map) parseEntry; + // return empty Map if none properties + Map tagEntry = + (Map) + projectResult.computeIfAbsent( + tag, k1 -> new HashMap<>()); + projectTags.forEach( + (k, v) -> { + if (!(v instanceof EmptyValue)) { + String nameOrId = null; + if (k + instanceof + List) { // valueMap("name") -> Map<["", + // "name"], value> + nameOrId = (String) ((List) k).get(1); + } else if (k + instanceof + String) { // valueMap() -> Map<"name", + // value> + nameOrId = (String) k; + } else if (k + instanceof + Number) { // valueMap() -> Map<1, value> + nameOrId = String.valueOf(k); + } + if (nameOrId == null || nameOrId.isEmpty()) { + throw new GremlinResultParserException( + "map value should have property" + + " key"); + } + String property = getPropertyName(nameOrId); + tagEntry.put( + property, Collections.singletonList(v)); + } + }); + } else { + if (!(parseEntry instanceof EmptyValue)) { + projectResult.put(tag, parseEntry); + } + } + }); + Map parseLabel = (Map) parseLabelInProjectResults(projectResult, step); + if (parseLabel.isEmpty()) { + return EmptyValue.INSTANCE; + } else if (parseLabel.size() == 1) { + return parseLabel.entrySet().iterator().next().getValue(); + } else { + return parseLabel; + } + } + + // a_1 -> a, i.e. g.V().as("a").select("a") + // name_1 -> name, i.e. g.V().values("name") + // a_name_1 -> a, i.e. g.V().as("a").select("a").by("name") + private String getColumnKeyAsResultKey(Common.NameOrId columnKey) { + if (columnKey.getItemCase() == Common.NameOrId.ItemCase.ITEM_NOT_SET) { + return ""; + } + switch (columnKey.getItemCase()) { + case ITEM_NOT_SET: + return ""; + case NAME: + String key = columnKey.getName(); + return AliasManager.getPrefix(key); + case ID: + return String.valueOf(columnKey.getId()); + default: + throw new GremlinResultParserException(columnKey.getItemCase() + " is invalid"); + } + } + + // propertyId is in String format, i.e. "1" + private String getPropertyName(String nameOrId) { + Common.NameOrId.Builder builder = Common.NameOrId.newBuilder(); + if (nameOrId.matches("^[0-9]+$")) { + builder.setId(Integer.valueOf(nameOrId)); + } else { + builder.setName(nameOrId); + } + return ParserUtils.getKeyName(builder.build(), FfiKeyType.Column); + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/UnionResultParser.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/UnionResultParser.java new file mode 100644 index 000000000000..3b11ab1c069c --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/result/UnionResultParser.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Alibaba Group Holding Limited. + * + * 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 com.alibaba.graphscope.gremlin.result; + +import com.alibaba.graphscope.gaia.proto.IrResult; +import com.google.common.base.Preconditions; + +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep; + +import java.util.Iterator; + +public class UnionResultParser implements GremlinResultParser { + private final Step step; + + private UnionResultParser(Step step) { + this.step = step; + } + + public static UnionResultParser create(Step step) { + return new UnionResultParser(step); + } + + @Override + public Object parseFrom(IrResult.Results results) { + GremlinResultParser resultParser = getResultParser(); + return resultParser.parseFrom(results); + } + + private GremlinResultParser getResultParser() { + UnionStep unionStep = (UnionStep) step; + Iterator subIterator = unionStep.getGlobalChildren().iterator(); + Preconditions.checkArgument( + subIterator.hasNext(), "union step must have at least one branch traversal"); + GremlinResultParser resultParser = GremlinResultAnalyzer.analyze(subIterator.next()); + while (subIterator.hasNext()) { + GremlinResultParser subParser = GremlinResultAnalyzer.analyze(subIterator.next()); + Preconditions.checkArgument( + resultParser.getClass().equals(subParser.getClass()), + "union step must have same branch traversal result type"); + } + return resultParser; + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/StepTransformFactory.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/StepTransformFactory.java index a6516d3768b7..1ff6e54f7de5 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/StepTransformFactory.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/StepTransformFactory.java @@ -606,28 +606,6 @@ public InterOpBase apply(Step step) { } }, - ID_STEP { - @Override - public InterOpBase apply(Step step) { - ProjectOp projectOp = new ProjectOp(); - String expr = "@." + ArgUtils.ID; - projectOp.setExprWithAlias( - new OpArg(Collections.singletonList(Pair.with(expr, ArgUtils.asNoneAlias())))); - return projectOp; - } - }, - - LABEL_STEP { - @Override - public InterOpBase apply(Step step) { - ProjectOp projectOp = new ProjectOp(); - String expr = "@." + ArgUtils.LABEL; - projectOp.setExprWithAlias( - new OpArg(Collections.singletonList(Pair.with(expr, ArgUtils.asNoneAlias())))); - return projectOp; - } - }, - CONSTANT_STEP { @Override public InterOpBase apply(Step step) { diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/TraversalParentTransform.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/TraversalParentTransform.java index 7b92fbbd9166..09d5db4aa82d 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/TraversalParentTransform.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/transform/TraversalParentTransform.java @@ -35,6 +35,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.*; +import org.apache.tinkerpop.gremlin.structure.T; import org.javatuples.Pair; import java.util.*; @@ -87,6 +88,12 @@ default ExprResult getSubTraversalAsExpr(ExprArg exprArg) { "use valueMap(..) instead if there are multiple project keys"); } return (new ExprResult()).addTagExpr("", Optional.of("@." + mapKeys[0])); + } else if (step instanceof LabelStep) { + return (new ExprResult()) + .addTagExpr("", Optional.of("@." + T.label.getAccessor())); // @.~label + } else if (step instanceof IdStep) { + return (new ExprResult()) + .addTagExpr("", Optional.of("@." + T.id.getAccessor())); // @.~id } else if (step instanceof SelectOneStep || step instanceof SelectStep) { // select('a'), select('a').by() // select('a').by('name'/values/valueMap) diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/IdLabelTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/IdLabelTest.java index 7e76d28adfd1..e3e7b79e0797 100644 --- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/IdLabelTest.java +++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/IdLabelTest.java @@ -24,7 +24,7 @@ public class IdLabelTest { public void g_V_id_test() { Traversal traversal = g.V().id(); Step step = traversal.asAdmin().getEndStep(); - ProjectOp op = (ProjectOp) StepTransformFactory.ID_STEP.apply(step); + ProjectOp op = (ProjectOp) StepTransformFactory.VALUES_STEP.apply(step); List exprWithAlias = (List) op.getExprWithAlias().get().applyArg(); Assert.assertEquals("@.~id", exprWithAlias.get(0).getValue0()); @@ -35,7 +35,7 @@ public void g_V_id_test() { public void g_V_label_test() { Traversal traversal = g.V().label(); Step step = traversal.asAdmin().getEndStep(); - ProjectOp op = (ProjectOp) StepTransformFactory.LABEL_STEP.apply(step); + ProjectOp op = (ProjectOp) StepTransformFactory.VALUES_STEP.apply(step); List exprWithAlias = (List) op.getExprWithAlias().get().applyArg(); Assert.assertEquals("@.~label", exprWithAlias.get(0).getValue0());