You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@asterixdb.apache.org by AsterixDB Code Review <do...@asterix-gerrit.ics.uci.edu> on 2022/01/09 21:30:49 UTC

Change in asterixdb-graph[master]: [NO-ISSUE][GRAPH] Refactoring metadata for Graph

From Glenn Galvizo <gg...@uci.edu>:

Glenn Galvizo has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/14764 )


Change subject: [NO-ISSUE][GRAPH] Refactoring metadata for Graph
......................................................................

[NO-ISSUE][GRAPH] Refactoring metadata for Graph

1. Definitions are now a list of objects for both vertices and edges.
2. We no longer store the "(" ")" for definition bodies.
3. Simplified the logic to obtain a normalized body for graph elements
   (to resolve dependencies).
4. Slight refactoring of the graph element classes.

Change-Id: I1686875b004451f3d7b4f1f45f1a854ccb5e8a04
---
M asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
R asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
D asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
M asterix-graphix/src/main/resources/lang-extension/lang.txt
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
24 files changed, 800 insertions(+), 632 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb-graph refs/changes/64/14764/1

diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
index 59889a3..24ce57c 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.asterix.graphix.app.translator;
 
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
@@ -35,11 +34,11 @@
 import org.apache.asterix.graphix.extension.GraphixMetadataExtension;
 import org.apache.asterix.graphix.lang.expression.GraphElementExpr;
 import org.apache.asterix.graphix.lang.rewrites.GraphixQueryRewriter;
-import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext;
 import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
 import org.apache.asterix.graphix.metadata.entities.Graph;
 import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.statement.DataverseDropStatement;
 import org.apache.asterix.lang.common.statement.DropDatasetStatement;
 import org.apache.asterix.lang.common.statement.FunctionDropStatement;
@@ -77,10 +76,9 @@
         Query query = ExpressionUtils.createWrappedQuery(functionCall, graphElementDecl.getSourceLocation());
 
         // We call our rewriter to set the normalized bodies of {@code graphElementDecl}.
-        GraphixRewritingContext graphixRewritingContext =
-                new GraphixRewritingContext(metadataProvider, declaredFunctions, null,
-                        Collections.singletonList(graphElementDecl), warningCollector, query.getVarCounter());
-        queryRewriter.loadNormalizedGraphElements(graphixRewritingContext, query);
+        LangRewritingContext rewriteContext = new LangRewritingContext(metadataProvider, declaredFunctions, null,
+                warningCollector, query.getVarCounter());
+        queryRewriter.loadNormalizedGraphElement(rewriteContext, query, graphElementDecl);
     }
 
     @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
index 8ba4cea..0a07239 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
@@ -21,7 +21,7 @@
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.lang.common.base.AbstractExpression;
 import org.apache.asterix.lang.common.base.AbstractLangExpression;
 import org.apache.asterix.lang.common.base.Expression;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
index b1337b7..c8d8723 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
@@ -19,24 +19,33 @@
 package org.apache.asterix.graphix.lang.parser;
 
 import java.io.StringReader;
-import java.util.Objects;
 
 import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
-import org.apache.asterix.graphix.metadata.entities.Graph;
+import org.apache.asterix.graphix.metadata.entities.Element;
 import org.apache.hyracks.api.exceptions.IWarningCollector;
 
 public final class GraphElementBodyParser {
     // Just a wrapper for the parseGraphElementBody method.
-    public static GraphElementDecl parse(Graph.Element element, GraphixParserFactory parserFactory,
+    public static GraphElementDecl parse(Element element, GraphixParserFactory parserFactory,
             IWarningCollector warningCollector) throws CompilationException {
         GraphElementDecl graphElementDecl = null;
-        for (String definition : element.getDefinitions()) {
-            if (Objects.equals(definition, "")) {
+        for (String definition : element.getDefinitionBodies()) {
+            if (definition == null) {
                 continue;
             }
+
+            // Parse our the definition.
             GraphixParser parser = (GraphixParser) parserFactory.createParser(new StringReader(definition));
-            GraphElementDecl parsedElementDecl = parser.parseGraphElementBody(element.getIdentifier());
+            GraphElementDecl parsedElementDecl;
+            try {
+                parsedElementDecl = parser.parseGraphElementBody(element.getIdentifier());
+
+            } catch (CompilationException e) {
+                throw new CompilationException(ErrorCode.COMPILATION_ERROR,
+                        "Bad definition for a graph element: " + e.getMessage());
+            }
 
             // Accumulate the element bodies.
             if (graphElementDecl == null) {
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
index 9e88251..c454773 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
@@ -22,25 +22,20 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
-import org.apache.asterix.graphix.extension.GraphixMetadataExtension;
 import org.apache.asterix.graphix.lang.expression.GraphElementExpr;
-import org.apache.asterix.graphix.lang.parser.GraphElementBodyParser;
 import org.apache.asterix.graphix.lang.parser.GraphixParserFactory;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
-import org.apache.asterix.graphix.metadata.entities.Graph;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.IParserFactory;
 import org.apache.asterix.lang.common.base.IReturningStatement;
 import org.apache.asterix.lang.common.expression.AbstractCallExpression;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.statement.Query;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
 import org.apache.asterix.lang.common.util.ExpressionUtils;
@@ -57,27 +52,19 @@
     public GraphixQueryRewriter(IParserFactory parserFactory) {
         super(parserFactory);
 
-        // We can safely downcast to our specific parser factory here.
+        // We safely downcast to our specific parser factory here.
         this.parserFactory = (GraphixParserFactory) parserFactory;
-        this.bodyRewriter = new SqlppQueryRewriter(parserFactory);
+        this.bodyRewriter = getFunctionAndViewBodyRewriter();
     }
 
-    public void rewrite(GraphixRewritingContext rewriteContext, IReturningStatement topStatement,
-            boolean allowNonStoredUdfCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars)
-            throws CompilationException {
-        // Get the graph elements in the top statement.
-        Map<GraphElementIdentifier, GraphElementDecl> graphElements =
-                loadNormalizedGraphElements(rewriteContext, topStatement);
-
-        // Perform the remainder of our rewrites in our parent.
-        super.rewrite(rewriteContext.getLangRewritingContext(), topStatement, allowNonStoredUdfCalls,
-                inlineUdfsAndViews, externalVars);
+    @Override
+    public void rewrite(LangRewritingContext context, IReturningStatement topStatement, boolean allowNonStoredUdfCalls,
+            boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) throws CompilationException {
+        super.rewrite(context, topStatement, allowNonStoredUdfCalls, inlineUdfsAndViews, externalVars);
     }
 
-    public Map<GraphElementIdentifier, GraphElementDecl> loadNormalizedGraphElements(
-            GraphixRewritingContext rewriteContext, IReturningStatement topExpr) throws CompilationException {
-        Map<GraphElementIdentifier, GraphElementDecl> graphElements = new HashMap<>();
-
+    public void loadNormalizedGraphElement(LangRewritingContext context, IReturningStatement topExpr,
+            GraphElementDecl graphElementDecl) throws CompilationException {
         // Gather all function calls.
         Deque<AbstractCallExpression> workQueue = new ArrayDeque<>();
         SqlppGatherFunctionCallsVisitor callVisitor = new SqlppGatherFunctionCallsVisitor(workQueue);
@@ -87,71 +74,31 @@
 
         AbstractCallExpression fnCall;
         while ((fnCall = workQueue.poll()) != null) {
-            // Load only the graph element declarations (we will load the rest of the elements in the parent).
-            if (!fnCall.getKind().equals(Expression.Kind.CALL_EXPRESSION)
-                    || !fnCall.getFunctionSignature().getName().equals(GraphElementExpr.GRAPH_ELEMENT_FUNCTION_NAME)) {
+            // We only care about graph element declarations.
+            if (!fnCall.getFunctionSignature().getName().equals(GraphElementExpr.GRAPH_ELEMENT_FUNCTION_NAME)) {
                 continue;
             }
-            GraphElementExpr graphElementExpr = (GraphElementExpr) fnCall;
-            GraphElementIdentifier identifier = graphElementExpr.getIdentifier();
-            if (!graphElements.containsKey(identifier)) {
 
-                // First, check if we have already loaded this graph element.
-                GraphElementDecl elementDecl = rewriteContext.getDeclaredGraphElements().get(identifier);
-
-                // If we cannot find the graph element in our context, search our metadata.
-                if (elementDecl == null) {
-                    GraphIdentifier graphIdentifier = identifier.getGraphIdentifier();
-                    Graph graph;
-                    try {
-                        graph = GraphixMetadataExtension.getGraph(
-                                rewriteContext.getMetadataProvider().getMetadataTxnContext(),
-                                graphIdentifier.getDataverseName(), graphIdentifier.getGraphName());
-
-                    } catch (AlgebricksException e) {
-                        throw new CompilationException(ErrorCode.COMPILATION_ERROR,
-                                graphElementExpr.getSourceLocation(),
-                                "Graph " + graphIdentifier.getGraphName() + " does not exist.");
-                    }
-
-                    // Parse our graph element.
-                    if (graph == null) {
-                        throw new CompilationException(ErrorCode.COMPILATION_ERROR,
-                                graphElementExpr.getSourceLocation(),
-                                "Graph " + graphIdentifier.getGraphName() + " does not exist.");
-                    }
-                    Graph.Element element = graph.getGraphSchema().getElement(identifier);
-                    elementDecl = GraphElementBodyParser.parse(element, parserFactory,
-                            rewriteContext.getLangRewritingContext().getWarningCollector());
-                }
-
-                // Get our normalized element bodies.
-                List<Expression> normalizedBodies = elementDecl.getNormalizedBodies();
-                if (normalizedBodies.size() != elementDecl.getBodies().size()) {
-                    GraphIdentifier graphIdentifier = elementDecl.getGraphIdentifier();
-                    for (Expression body : elementDecl.getBodies()) {
-                        Expression normalizedBody =
-                                rewriteGraphElementBody(rewriteContext, graphIdentifier.getDataverseName(), body,
-                                        Collections.emptyList(), elementDecl.getSourceLocation());
-                        normalizedBodies.add(normalizedBody);
-                    }
-                }
-
-                // Store the element declaration in our map, and iterate over this body.
-                graphElements.put(identifier, elementDecl);
-                for (Expression e : elementDecl.getNormalizedBodies()) {
-                    e.accept(callVisitor, null);
+            // Get our normalized element bodies, by calling our rewriter.
+            List<Expression> normalizedBodies = graphElementDecl.getNormalizedBodies();
+            if (normalizedBodies.size() != graphElementDecl.getBodies().size()) {
+                GraphIdentifier graphIdentifier = graphElementDecl.getGraphIdentifier();
+                for (Expression body : graphElementDecl.getBodies()) {
+                    Expression normalizedBody = rewriteGraphElementBody(context, graphIdentifier.getDataverseName(),
+                            body, Collections.emptyList(), graphElementDecl.getSourceLocation());
+                    normalizedBodies.add(normalizedBody);
                 }
             }
-        }
 
-        return graphElements;
+            // We should only have one element. We can safely break here.
+            break;
+        }
     }
 
-    private Expression rewriteGraphElementBody(GraphixRewritingContext rewriteContext, DataverseName elementDataverse,
+    private Expression rewriteGraphElementBody(LangRewritingContext context, DataverseName elementDataverse,
             Expression bodyExpr, List<VarIdentifier> externalVars, SourceLocation sourceLoc)
             throws CompilationException {
-        Dataverse defaultDataverse = rewriteContext.getMetadataProvider().getDefaultDataverse();
+        Dataverse defaultDataverse = context.getMetadataProvider().getDefaultDataverse();
         Dataverse targetDataverse;
 
         // We might need to change our dataverse, if the element definition requires a different one.
@@ -160,18 +107,18 @@
 
         } else {
             try {
-                targetDataverse = rewriteContext.getMetadataProvider().findDataverse(elementDataverse);
+                targetDataverse = context.getMetadataProvider().findDataverse(elementDataverse);
 
             } catch (AlgebricksException e) {
                 throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, e, sourceLoc, elementDataverse);
             }
         }
-        rewriteContext.getMetadataProvider().setDefaultDataverse(targetDataverse);
+        context.getMetadataProvider().setDefaultDataverse(targetDataverse);
 
         // Get the body of the rewritten query.
         try {
             Query wrappedQuery = ExpressionUtils.createWrappedQuery(bodyExpr, sourceLoc);
-            bodyRewriter.rewrite(rewriteContext.getLangRewritingContext(), wrappedQuery, false, false, externalVars);
+            bodyRewriter.rewrite(context, wrappedQuery, false, false, externalVars);
             return wrappedQuery.getBody();
 
         } catch (CompilationException e) {
@@ -180,7 +127,7 @@
 
         } finally {
             // Switch back to the working dataverse.
-            rewriteContext.getMetadataProvider().setDefaultDataverse(defaultDataverse);
+            context.getMetadataProvider().setDefaultDataverse(defaultDataverse);
         }
     }
 }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
similarity index 96%
rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java
rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
index 2b41511..fb70ae6 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.graphix.lang.rewrites.visitor;
+package org.apache.asterix.graphix.lang.rewrites.base;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.graphix.lang.expression.GraphConstructor;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
index 866e2f2..670cb9a 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
@@ -25,7 +25,7 @@
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.graphix.lang.expression.GraphConstructor;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
index e9e8255..31885e4 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
@@ -22,7 +22,7 @@
 import org.apache.asterix.app.translator.QueryTranslator;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
index c5e87de..51446dc 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
@@ -27,7 +27,7 @@
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
index 1d7d0da..344c02b 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
@@ -35,8 +35,10 @@
 import org.apache.asterix.graphix.lang.statement.CreateGraphStatement;
 import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
+import org.apache.asterix.graphix.metadata.entities.Edge;
 import org.apache.asterix.graphix.metadata.entities.Graph;
-import org.apache.asterix.graphix.metadata.entities.GraphDependencies;
+import org.apache.asterix.graphix.metadata.entities.Schema;
+import org.apache.asterix.graphix.metadata.entities.Vertex;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.metadata.MetadataManager;
 import org.apache.asterix.metadata.MetadataTransactionContext;
@@ -80,11 +82,10 @@
         }
 
         // Build the graph schema.
-        GraphIdentifier graphIdentifier = new GraphIdentifier(activeDataverseName, cgs.getGraphName());
-        Graph.Schema.Builder schemaBuilder = new Graph.Schema.Builder(graphIdentifier);
+        Schema.Builder schemaBuilder = new Schema.Builder(new GraphIdentifier(activeDataverseName, cgs.getGraphName()));
         Map<GraphElementIdentifier, GraphElementDecl> graphElementDecls = new LinkedHashMap<>();
         for (GraphConstructor.VertexElement vertex : cgs.getVertexElements()) {
-            Graph.Vertex schemaVertex =
+            Vertex schemaVertex =
                     schemaBuilder.addVertex(vertex.getLabel(), vertex.getPrimaryKeyFields(), vertex.getDefinition());
             switch (schemaBuilder.getLastError()) {
                 case NO_ERROR:
@@ -109,7 +110,7 @@
             }
         }
         for (GraphConstructor.EdgeElement edge : cgs.getEdgeElements()) {
-            Graph.Edge schemaEdge;
+            Edge schemaEdge;
             if (edge.getDefinition() == null) {
                 schemaEdge = schemaBuilder.addEdge(edge.getEdgeLabel(), edge.getDestinationLabel(),
                         edge.getSourceLabel(), edge.getDestinationKeyFields());
@@ -166,7 +167,7 @@
         }
 
         // Build our dependencies (collected over all graph element bodies).
-        GraphDependencies graphDependencies = new GraphDependencies();
+        Graph.Dependencies graphDependencies = new Graph.Dependencies();
         for (GraphElementDecl graphElementDecl : graphElementDecls.values()) {
             if (graphElementDecl.getNormalizedBodies().size() != graphElementDecl.getBodies().size()) {
                 // We should have set the normalized body by calling {@code normalizeGraphElementAsQuery} beforehand.
@@ -179,7 +180,7 @@
         }
 
         // Add / upsert our graph to our metadata.
-        Graph newGraph = new Graph(graphIdentifier, schemaBuilder.build(), graphDependencies);
+        Graph newGraph = new Graph(schemaBuilder.build(), graphDependencies);
         if (existingGraph == null) {
             MetadataManager.INSTANCE.addEntity(mdTxnCtx, newGraph);
 
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
index 055056d..b748f7b 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
@@ -19,12 +19,14 @@
 package org.apache.asterix.graphix.metadata.bootstrap;
 
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DATAVERSE_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DEFINITION;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DEPENDENCIES;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_PRIMARY_KEY;
 
 import org.apache.asterix.metadata.bootstrap.MetadataRecordTypes;
 import org.apache.asterix.om.types.AOrderedListType;
 import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 
@@ -32,6 +34,8 @@
     public static final String RECORD_NAME_GRAPH = "GraphRecordType";
     public static final String RECORD_NAME_VERTICES = "VerticesRecordType";
     public static final String RECORD_NAME_EDGES = "EdgesRecordType";
+    public static final String RECORD_NAME_VERTEX_DEF = "VertexDefinitionRecordType";
+    public static final String RECORD_NAME_EDGE_DEF = "EdgeDefinitionRecordType";
 
     public static final String FIELD_NAME_DEFINITIONS = "Definitions";
     public static final String FIELD_NAME_DESTINATION_KEY = "DestinationKey";
@@ -43,31 +47,43 @@
     public static final String FIELD_NAME_SOURCE_LABEL = "SourceLabel";
     public static final String FIELD_NAME_VERTICES = "Vertices";
 
-    public static final int GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX = 0;
-    public static final int GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX = 1;
-    public static final int GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX = 2;
-    public static final ARecordType VERTICES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTICES,
-            new String[] { FIELD_NAME_LABEL, FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DEFINITIONS },
-            new IAType[] { BuiltinType.ASTRING,
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(BuiltinType.ASTRING, null) },
+    public static final int VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX = 0;
+    public static final int VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX = 1;
+    public static final ARecordType VERTEX_DEF_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTEX_DEF,
+            new String[] { FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DEFINITION }, new IAType[] {
+                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null), BuiltinType.ASTRING },
             true);
 
-    public static final int GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX = 0;
-    public static final int GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX = 1;
-    public static final int GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX = 2;
-    public static final int GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX = 3;
-    public static final int GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX = 4;
-    public static final int GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX = 5;
-    public static final int GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX = 6;
+    public static final int VERTICES_ARECORD_LABEL_FIELD_INDEX = 0;
+    public static final int VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX = 1;
+    public static final ARecordType VERTICES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTICES,
+            new String[] { FIELD_NAME_LABEL, FIELD_NAME_DEFINITIONS },
+            new IAType[] { BuiltinType.ASTRING, new AOrderedListType(VERTEX_DEF_RECORDTYPE, null) }, true);
+
+    public static final int EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX = 0;
+    public static final int EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX = 1;
+    public static final int EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX = 2;
+    public static final int EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX = 3;
+    public static final ARecordType EDGE_DEF_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_EDGE_DEF,
+            new String[] { FIELD_NAME_DESTINATION_KEY, FIELD_NAME_SOURCE_KEY, FIELD_NAME_PRIMARY_KEY,
+                    FIELD_NAME_DEFINITION },
+            new IAType[] { new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
+                    AUnionType.createMissableType(
+                            new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null)),
+                    AUnionType.createMissableType(
+                            new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null)),
+                    AUnionType.createMissableType(BuiltinType.ASTRING) },
+            true);
+
+    public static final int EDGES_ARECORD_LABEL_FIELD_INDEX = 0;
+    public static final int EDGES_ARECORD_DEST_LABEL_FIELD_INDEX = 1;
+    public static final int EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX = 2;
+    public static final int EDGES_ARECORD_DEFINITIONS_FIELD_INDEX = 3;
     public static final ARecordType EDGES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_EDGES,
             new String[] { FIELD_NAME_LABEL, FIELD_NAME_DESTINATION_LABEL, FIELD_NAME_SOURCE_LABEL,
-                    FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DESTINATION_KEY, FIELD_NAME_SOURCE_KEY, FIELD_NAME_DEFINITIONS },
+                    FIELD_NAME_DEFINITIONS },
             new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING, BuiltinType.ASTRING,
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(BuiltinType.ASTRING, null) },
+                    new AOrderedListType(EDGE_DEF_RECORDTYPE, null) },
             true);
 
     public static final int GRAPH_ARECORD_DATAVERSENAME_FIELD_INDEX = 0;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
new file mode 100644
index 0000000..ea2624f
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata representation of an edge. An edge consists of the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the edge across other graph elements.
+ * 2. A source vertex ({@code Vertex}) reference.
+ * 3. A destination vertex (@code Vertex}) reference.
+ * 4. A collection of edge definitions, which contains either a destination key OR a source key, destination key,
+ *    primary key, and a SQL++ string.
+ */
+public class Edge implements Element {
+    private static final long serialVersionUID = 1L;
+
+    private final GraphElementIdentifier identifier;
+    private final Vertex destinationVertex;
+    private final Vertex sourceVertex;
+    private final List<Definition> definitions;
+
+    // Use {@code Schema.Builder} to build Edge instances instead of this constructor.
+    Edge(GraphElementIdentifier identifier, Vertex destinationVertex, Vertex sourceVertex, Definition edgeDefinition) {
+        this.destinationVertex = Objects.requireNonNull(destinationVertex);
+        this.sourceVertex = Objects.requireNonNull(sourceVertex);
+        this.identifier = Objects.requireNonNull(identifier);
+        this.definitions = new ArrayList<>();
+        this.definitions.add(edgeDefinition);
+    }
+
+    public static class Definition {
+        private final List<List<String>> primaryKeyFieldNames;
+        private final List<List<String>> destinationKeyFieldNames;
+        private final List<List<String>> sourceKeyFieldNames;
+        private final String definition;
+
+        Definition(List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
+                List<List<String>> sourceKeyFieldNames, String definition) {
+            this.primaryKeyFieldNames = primaryKeyFieldNames;
+            this.destinationKeyFieldNames = destinationKeyFieldNames;
+            this.sourceKeyFieldNames = sourceKeyFieldNames;
+            this.definition = definition;
+        }
+
+        Definition(List<List<String>> destinationKeyFieldNames) {
+            this.primaryKeyFieldNames = null;
+            this.destinationKeyFieldNames = destinationKeyFieldNames;
+            this.sourceKeyFieldNames = null;
+            this.definition = null;
+        }
+
+        public List<List<String>> getPrimaryKeyFieldNames() {
+            return primaryKeyFieldNames;
+        }
+
+        public List<List<String>> getDestinationKeyFieldNames() {
+            return destinationKeyFieldNames;
+        }
+
+        public List<List<String>> getSourceKeyFieldNames() {
+            return sourceKeyFieldNames;
+        }
+
+        public String getDefinition() {
+            return definition;
+        }
+
+        public boolean isSimpleDefinition() {
+            return definition == null;
+        }
+    }
+
+    public String getDestinationLabelName() {
+        return destinationVertex.getLabelName();
+    }
+
+    public String getSourceLabelName() {
+        return sourceVertex.getLabelName();
+    }
+
+    public Vertex getDestinationVertex() {
+        return destinationVertex;
+    }
+
+    public Vertex getSourceVertex() {
+        return sourceVertex;
+    }
+
+    public List<Definition> getDefinitions() {
+        return definitions;
+    }
+
+    @Override
+    public GraphElementIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public String getLabelName() {
+        return identifier.getLabelName();
+    }
+
+    @Override
+    public List<String> getDefinitionBodies() {
+        return definitions.stream().map(Definition::getDefinition).collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        String edgeBodyPattern = "[:" + getLabelName() + "]";
+        String sourceNodePattern = "(:" + getSourceLabelName() + ")";
+        String destinationNodePattern = "(:" + getDestinationLabelName() + ")";
+        String edgePattern = sourceNodePattern + "-" + edgeBodyPattern + "->" + destinationNodePattern;
+        return edgePattern + " AS " + getDefinitionBodies().stream().map(s -> (s == null) ? "(no definition)" : s)
+                .collect(Collectors.joining(",\n"));
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
new file mode 100644
index 0000000..1454a9a
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata interface for a graph element (i.e. edge or vertex). An element has the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the element across other graph elements.
+ * 2. A label name, unique amongst the element classes (e.g. an edge label is unique amongst all graph edges).
+ * 3. A non-empty list of SQL++ strings, each of which represents a definition.
+ */
+public interface Element extends Serializable {
+    GraphElementIdentifier getIdentifier();
+
+    String getLabelName();
+
+    List<String> getDefinitionBodies();
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
index 21afc2d..0f7659f 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
@@ -18,53 +18,55 @@
  */
 package org.apache.asterix.graphix.metadata.entities;
 
-import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 
+import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataIndexes;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.IQueryRewriter;
+import org.apache.asterix.lang.common.util.ExpressionUtils;
 import org.apache.asterix.metadata.api.ExtensionMetadataDatasetId;
 import org.apache.asterix.metadata.api.IExtensionMetadataEntity;
+import org.apache.asterix.metadata.entities.DependencyKind;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Triple;
 
 /**
- * Metadata describing a graph view, composed of vertices and edges.
+ * Metadata describing a graph view, composed of a graph {@code Schema} and a collection of dependencies.
  */
 public class Graph implements IExtensionMetadataEntity {
     private static final long serialVersionUID = 1L;
 
-    private final GraphIdentifier identifier;
-    private final GraphDependencies dependencies;
+    private final Dependencies dependencies;
     private final Schema graphSchema;
 
-    public Graph(GraphIdentifier identifier, Schema graphSchema, GraphDependencies dependencies) {
-        this.identifier = Objects.requireNonNull(identifier);
-        this.dependencies = dependencies;
-        this.graphSchema = graphSchema;
+    public Graph(Schema graphSchema, Dependencies dependencies) {
+        this.graphSchema = Objects.requireNonNull(graphSchema);
+        this.dependencies = Objects.requireNonNull(dependencies);
     }
 
     public DataverseName getDataverseName() {
-        return identifier.getDataverseName();
+        return graphSchema.getGraphIdentifier().getDataverseName();
     }
 
     public String getGraphName() {
-        return identifier.getGraphName();
+        return graphSchema.getGraphIdentifier().getGraphName();
     }
 
     public GraphIdentifier getIdentifier() {
-        return identifier;
+        return graphSchema.getGraphIdentifier();
     }
 
     public Schema getGraphSchema() {
         return graphSchema;
     }
 
-    public GraphDependencies getDependencies() {
+    public Dependencies getDependencies() {
         return dependencies;
     }
 
@@ -73,276 +75,44 @@
         return GraphixMetadataIndexes.GRAPH_METADATA_DATASET_EXTENSION_ID;
     }
 
-    public static class Schema implements Serializable {
-        private static final long serialVersionUID = 1L;
+    public static class Dependencies {
+        private final List<Triple<DataverseName, String, String>> datasetDependencies;
+        private final List<Triple<DataverseName, String, String>> synonymDependencies;
+        private final List<Triple<DataverseName, String, String>> functionDependencies;
 
-        // The element map is composed of the vertices and edges.
-        private final Map<GraphElementIdentifier, Element> elementMap = new HashMap<>();
-        private final List<Vertex> vertexList = new ArrayList<>();
-        private final List<Edge> edgeList = new ArrayList<>();
-
-        public List<Vertex> getVertices() {
-            return vertexList;
+        public Dependencies(List<List<Triple<DataverseName, String, String>>> listRepresentation) {
+            datasetDependencies = listRepresentation.get(0);
+            synonymDependencies = listRepresentation.get(1);
+            functionDependencies = listRepresentation.get(2);
         }
 
-        public List<Edge> getEdges() {
-            return edgeList;
+        public Dependencies() {
+            datasetDependencies = new ArrayList<>();
+            synonymDependencies = new ArrayList<>();
+            functionDependencies = new ArrayList<>();
         }
 
-        public Element getElement(GraphElementIdentifier identifier) {
-            return elementMap.get(identifier);
+        public List<List<Triple<DataverseName, String, String>>> getListRepresentation() {
+            return List.of(datasetDependencies, synonymDependencies, functionDependencies);
         }
 
-        private Schema() {
-        }
-
-        public static class Builder {
-            private final Map<String, Vertex> vertexLabelMap = new HashMap<>();
-            private final Map<String, Edge> edgeLabelMap = new HashMap<>();
-
-            // We aim to populate the schema object below.
-            private final Schema workingSchema = new Schema();
-            private final GraphIdentifier graphIdentifier;
-            private Error lastError = Error.NO_ERROR;
-
-            public Builder(GraphIdentifier graphIdentifier) {
-                this.graphIdentifier = graphIdentifier;
+        public Iterator<Pair<DependencyKind, Triple<DataverseName, String, String>>> getIterator() {
+            List<Pair<DependencyKind, Triple<DataverseName, String, String>>> resultant = new ArrayList<>();
+            for (Triple<DataverseName, String, String> datasetDependency : datasetDependencies) {
+                resultant.add(new Pair<>(DependencyKind.DATASET, datasetDependency));
             }
-
-            /**
-             * @return Null if the primary keys of an existing vertex conflict with the vertex to-be-added. The vertex
-             * to-be-added otherwise.
-             */
-            public Vertex addVertex(String labelName, List<List<String>> primaryKeyFieldNames, String definition) {
-                if (!vertexLabelMap.containsKey(labelName)) {
-                    GraphElementIdentifier identifier =
-                            new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.VERTEX, labelName);
-                    Vertex newVertex = new Vertex(identifier, primaryKeyFieldNames, definition);
-                    workingSchema.vertexList.add(newVertex);
-                    vertexLabelMap.put(labelName, newVertex);
-                    return newVertex;
-
-                } else {
-                    Vertex existingVertex = vertexLabelMap.get(labelName);
-                    if (!existingVertex.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
-                        lastError = Error.CONFLICTING_PRIMARY_KEY;
-                        return null;
-                    }
-                    existingVertex.getDefinitions().add(definition);
-                    return existingVertex;
-                }
+            for (Triple<DataverseName, String, String> synonymDependency : synonymDependencies) {
+                resultant.add(new Pair<>(DependencyKind.SYNONYM, synonymDependency));
             }
-
-            /**
-             * @return Null if there exists no vertex with the given source label or destination label, OR if the
-             * primary key / source vertex / destination vertex of an existing edge conflict with the edge to-be-added.
-             */
-            public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
-                    List<List<String>> destinationKeyFieldNames) {
-                if (!vertexLabelMap.containsKey(sourceLabelName)) {
-                    lastError = Error.SOURCE_VERTEX_NOT_FOUND;
-                    return null;
-                }
-
-                Vertex representativeSourceVertex = vertexLabelMap.get(sourceLabelName);
-                return addEdge(edgeLabelName, destinationLabelName, sourceLabelName,
-                        representativeSourceVertex.getPrimaryKeyFieldNames(), destinationKeyFieldNames,
-                        representativeSourceVertex.getPrimaryKeyFieldNames(), "");
+            for (Triple<DataverseName, String, String> functionDependency : functionDependencies) {
+                resultant.add(new Pair<>(DependencyKind.FUNCTION, functionDependency));
             }
-
-            /**
-             * @return Null if there exists no vertex with the given source label or destination label, OR if the
-             * primary key / source vertex / destination vertex of an existing edge conflict with the edge to-be-added.
-             */
-            public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
-                    List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
-                    List<List<String>> sourceKeyFieldNames, String definition) {
-                if (!vertexLabelMap.containsKey(sourceLabelName)) {
-                    lastError = Error.SOURCE_VERTEX_NOT_FOUND;
-                    return null;
-
-                } else if (!vertexLabelMap.containsKey(destinationLabelName)) {
-                    lastError = Error.DESTINATION_VERTEX_NOT_FOUND;
-                    return null;
-
-                } else if (edgeLabelMap.containsKey(edgeLabelName)) {
-                    Edge existingEdge = edgeLabelMap.get(edgeLabelName);
-                    if (!existingEdge.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
-                        lastError = Error.CONFLICTING_PRIMARY_KEY;
-                        return null;
-
-                    } else if (!existingEdge.getSourceLabelName().equals(sourceLabelName)) {
-                        // This also covers any source-key conflicts.
-                        lastError = Error.CONFLICTING_SOURCE_VERTEX;
-                        return null;
-
-                    } else if (!existingEdge.getDestinationLabelName().equals(destinationLabelName)) {
-                        // This also covers any destination-key conflicts.
-                        lastError = Error.CONFLICTING_DESTINATION_VERTEX;
-                        return null;
-                    }
-                    existingEdge.getDefinitions().add(definition);
-                    return existingEdge;
-
-                } else {
-                    GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier,
-                            GraphElementIdentifier.Kind.EDGE, edgeLabelName);
-                    Edge newEdge = new Edge(identifier, primaryKeyFieldNames, destinationKeyFieldNames,
-                            sourceKeyFieldNames, vertexLabelMap.get(destinationLabelName),
-                            vertexLabelMap.get(sourceLabelName), definition);
-                    workingSchema.edgeList.add(newEdge);
-                    edgeLabelMap.put(edgeLabelName, newEdge);
-                    return newEdge;
-                }
-            }
-
-            public Schema build() {
-                // Build the element map, composed of our vertices and edges.
-                workingSchema.elementMap.clear();
-                workingSchema.getVertices().forEach(v -> workingSchema.elementMap.put(v.identifier, v));
-                workingSchema.getEdges().forEach(e -> workingSchema.elementMap.put(e.identifier, e));
-                return workingSchema;
-            }
-
-            public Error getLastError() {
-                return lastError;
-            }
-
-            public enum Error {
-                NO_ERROR,
-
-                CONFLICTING_PRIMARY_KEY,
-                CONFLICTING_SOURCE_VERTEX,
-                CONFLICTING_DESTINATION_VERTEX,
-
-                SOURCE_VERTEX_NOT_FOUND,
-                DESTINATION_VERTEX_NOT_FOUND
-            }
-        }
-    }
-
-    public static final class Vertex implements Element {
-        private static final long serialVersionUID = 1L;
-
-        private final GraphElementIdentifier identifier;
-        private final List<List<String>> primaryKeyFieldNames;
-        private final List<String> definitions;
-
-        private Vertex(GraphElementIdentifier identifier, List<List<String>> primaryKeyFieldNames, String definition) {
-            this.identifier = Objects.requireNonNull(identifier);
-            this.primaryKeyFieldNames = Objects.requireNonNull(primaryKeyFieldNames);
-            this.definitions = new ArrayList<>();
-            this.definitions.add(Objects.requireNonNull(definition));
+            return resultant.listIterator();
         }
 
-        public List<List<String>> getPrimaryKeyFieldNames() {
-            return primaryKeyFieldNames;
+        public void collectDependencies(Expression expr, IQueryRewriter queryRewriter) throws CompilationException {
+            ExpressionUtils.collectDependencies(expr, queryRewriter, datasetDependencies, synonymDependencies,
+                    functionDependencies);
         }
-
-        @Override
-        public GraphElementIdentifier getIdentifier() {
-            return identifier;
-        }
-
-        @Override
-        public String getLabelName() {
-            return identifier.getLabelName();
-        }
-
-        @Override
-        public List<String> getDefinitions() {
-            return definitions;
-        }
-
-        @Override
-        public String toString() {
-            return "(:" + getLabelName() + ") AS " + String.join(",\n", definitions);
-        }
-    }
-
-    public static final class Edge implements Element {
-        private static final long serialVersionUID = 1L;
-
-        private final List<List<String>> primaryKeyFieldNames;
-        private final List<List<String>> destinationKeyFieldNames;
-        private final List<List<String>> sourceKeyFieldNames;
-
-        private final GraphElementIdentifier identifier;
-        private final Vertex destinationVertex;
-        private final Vertex sourceVertex;
-        private final List<String> definitions;
-
-        private Edge(GraphElementIdentifier identifier, List<List<String>> primaryKeyFieldNames,
-                List<List<String>> destinationKeyFieldNames, List<List<String>> sourceKeyFieldNames,
-                Vertex destinationVertex, Vertex sourceVertex, String edgeDefinition) {
-            this.primaryKeyFieldNames = Objects.requireNonNull(primaryKeyFieldNames);
-            this.destinationKeyFieldNames = Objects.requireNonNull(destinationKeyFieldNames);
-            this.sourceKeyFieldNames = Objects.requireNonNull(sourceKeyFieldNames);
-            this.destinationVertex = Objects.requireNonNull(destinationVertex);
-            this.sourceVertex = Objects.requireNonNull(sourceVertex);
-            this.identifier = Objects.requireNonNull(identifier);
-            this.definitions = new ArrayList<>();
-            this.definitions.add(Objects.requireNonNull(edgeDefinition));
-        }
-
-        public String getDestinationLabelName() {
-            return destinationVertex.getLabelName();
-        }
-
-        public String getSourceLabelName() {
-            return sourceVertex.getLabelName();
-        }
-
-        public List<List<String>> getPrimaryKeyFieldNames() {
-            return primaryKeyFieldNames;
-        }
-
-        public List<List<String>> getDestinationKeyFieldNames() {
-            return destinationKeyFieldNames;
-        }
-
-        public List<List<String>> getSourceKeyFieldNames() {
-            return sourceKeyFieldNames;
-        }
-
-        public Vertex getDestinationVertex() {
-            return destinationVertex;
-        }
-
-        public Vertex getSourceVertex() {
-            return sourceVertex;
-        }
-
-        @Override
-        public GraphElementIdentifier getIdentifier() {
-            return identifier;
-        }
-
-        @Override
-        public String getLabelName() {
-            return identifier.getLabelName();
-        }
-
-        @Override
-        public List<String> getDefinitions() {
-            return definitions;
-        }
-
-        @Override
-        public String toString() {
-            String edgeBodyPattern = "[:" + getLabelName() + "]";
-            String sourceNodePattern = "(:" + getSourceLabelName() + ")";
-            String destinationNodePattern = "(:" + getDestinationLabelName() + ")";
-            String edgePattern = sourceNodePattern + "-" + edgeBodyPattern + "->" + destinationNodePattern;
-            return edgePattern + " AS " + String.join(",\n", getDefinitions());
-        }
-    }
-
-    public interface Element extends Serializable {
-        GraphElementIdentifier getIdentifier();
-
-        String getLabelName();
-
-        List<String> getDefinitions();
     }
 }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
deleted file mode 100644
index 0e7aa5a..0000000
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.apache.asterix.graphix.metadata.entities;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.lang.common.base.Expression;
-import org.apache.asterix.lang.common.base.IQueryRewriter;
-import org.apache.asterix.lang.common.util.ExpressionUtils;
-import org.apache.asterix.metadata.entities.DependencyKind;
-import org.apache.hyracks.algebricks.common.utils.Pair;
-import org.apache.hyracks.algebricks.common.utils.Triple;
-
-/**
- * Helper class to manage dependencies for a graph metadata entity.
- */
-public class GraphDependencies {
-    private final List<Triple<DataverseName, String, String>> datasetDependencies;
-    private final List<Triple<DataverseName, String, String>> synonymDependencies;
-    private final List<Triple<DataverseName, String, String>> functionDependencies;
-
-    public GraphDependencies(List<List<Triple<DataverseName, String, String>>> listRepresentation) {
-        datasetDependencies = listRepresentation.get(0);
-        synonymDependencies = listRepresentation.get(1);
-        functionDependencies = listRepresentation.get(2);
-    }
-
-    public GraphDependencies() {
-        datasetDependencies = new ArrayList<>();
-        synonymDependencies = new ArrayList<>();
-        functionDependencies = new ArrayList<>();
-    }
-
-    public List<List<Triple<DataverseName, String, String>>> getListRepresentation() {
-        return List.of(datasetDependencies, synonymDependencies, functionDependencies);
-    }
-
-    public Iterator<Pair<DependencyKind, Triple<DataverseName, String, String>>> getIterator() {
-        List<Pair<DependencyKind, Triple<DataverseName, String, String>>> resultant = new ArrayList<>();
-        for (Triple<DataverseName, String, String> datasetDependency : datasetDependencies) {
-            resultant.add(new Pair<>(DependencyKind.DATASET, datasetDependency));
-        }
-        for (Triple<DataverseName, String, String> synonymDependency : synonymDependencies) {
-            resultant.add(new Pair<>(DependencyKind.SYNONYM, synonymDependency));
-        }
-        for (Triple<DataverseName, String, String> functionDependency : functionDependencies) {
-            resultant.add(new Pair<>(DependencyKind.FUNCTION, functionDependency));
-        }
-        return resultant.listIterator();
-    }
-
-    public void collectDependencies(Expression expression, IQueryRewriter queryRewriter) throws CompilationException {
-        ExpressionUtils.collectDependencies(expression, queryRewriter, datasetDependencies, synonymDependencies,
-                functionDependencies);
-    }
-}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
new file mode 100644
index 0000000..75c696b
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
+
+/**
+ * Metadata representation of a graph schema. A graph schema consists of:
+ * 1. A list of {@code Vertex} instances.
+ * 2. A list of {@code Edge} instances, which link the aforementioned vertices.
+ * 3. A graph identifier ({@code GraphIdentifier}
+ */
+public class Schema implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    // The element map is composed of the vertices and edges.
+    private final List<Vertex> vertexList = new ArrayList<>();
+    private final List<Edge> edgeList = new ArrayList<>();
+    private final GraphIdentifier graphIdentifier;
+
+    public List<Vertex> getVertices() {
+        return vertexList;
+    }
+
+    public List<Edge> getEdges() {
+        return edgeList;
+    }
+
+    public GraphIdentifier getGraphIdentifier() {
+        return graphIdentifier;
+    }
+
+    // Use the {@code Builder} class to create {@code Schema} instances.
+    private Schema(GraphIdentifier graphIdentifier) {
+        this.graphIdentifier = graphIdentifier;
+    }
+
+    public static class Builder {
+        private final Map<String, Vertex> vertexLabelMap = new HashMap<>();
+        private final Map<String, Edge> edgeLabelMap = new HashMap<>();
+
+        // We aim to populate the schema object below.
+        private final Schema workingSchema;
+        private final GraphIdentifier graphIdentifier;
+        private Error lastError = Error.NO_ERROR;
+
+        public Builder(GraphIdentifier graphIdentifier) {
+            this.workingSchema = new Schema(graphIdentifier);
+            this.graphIdentifier = graphIdentifier;
+        }
+
+        /**
+         * @return Null if the primary keys of an existing vertex conflict with the vertex to-be-added. The vertex
+         * to-be-added otherwise.
+         */
+        public Vertex addVertex(String labelName, List<List<String>> primaryKeyFieldNames, String definition) {
+            if (!vertexLabelMap.containsKey(labelName)) {
+                GraphElementIdentifier identifier =
+                        new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.VERTEX, labelName);
+                Vertex newVertex = new Vertex(identifier, new Vertex.Definition(primaryKeyFieldNames, definition));
+                workingSchema.vertexList.add(newVertex);
+                vertexLabelMap.put(labelName, newVertex);
+                return newVertex;
+
+            } else {
+                Vertex existingVertex = vertexLabelMap.get(labelName);
+                if (!existingVertex.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
+                    lastError = Error.CONFLICTING_PRIMARY_KEY;
+                    return null;
+                }
+                existingVertex.getDefinitions().add(new Vertex.Definition(primaryKeyFieldNames, definition));
+                return existingVertex;
+            }
+        }
+
+        /**
+         * @return Null if there exists no vertex with the given source label or destination label, OR if the
+         * source vertex / destination vertex of an existing edge conflict with the edge to-be-added. Otherwise, the
+         * edge to-be-added.
+         */
+        public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                List<List<String>> destinationKeyFieldNames) {
+            Edge.Definition definition = new Edge.Definition(destinationKeyFieldNames);
+            return addEdge(edgeLabelName, destinationLabelName, sourceLabelName, definition);
+        }
+
+        /**
+         * @return Null if there exists no vertex with the given source label or destination label, OR if the
+         * source vertex / destination vertex of an existing edge conflict with the edge to-be-added. Otherwise, the
+         * edge to-be-added.
+         */
+        public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
+                List<List<String>> sourceKeyFieldNames, String definitionBody) {
+            Edge.Definition definition = new Edge.Definition(primaryKeyFieldNames, destinationKeyFieldNames,
+                    sourceKeyFieldNames, definitionBody);
+            return addEdge(edgeLabelName, destinationLabelName, sourceLabelName, definition);
+        }
+
+        private Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                Edge.Definition definition) {
+            if (!vertexLabelMap.containsKey(sourceLabelName)) {
+                lastError = Error.SOURCE_VERTEX_NOT_FOUND;
+                return null;
+
+            } else if (!vertexLabelMap.containsKey(destinationLabelName)) {
+                lastError = Error.DESTINATION_VERTEX_NOT_FOUND;
+                return null;
+
+            }
+
+            if (edgeLabelMap.containsKey(edgeLabelName)) {
+                Edge existingEdge = edgeLabelMap.get(edgeLabelName);
+                if (!existingEdge.getSourceLabelName().equals(sourceLabelName)) {
+                    // This also covers any source-key conflicts.
+                    lastError = Error.CONFLICTING_SOURCE_VERTEX;
+                    return null;
+
+                } else if (!existingEdge.getDestinationLabelName().equals(destinationLabelName)) {
+                    // This also covers any destination-key conflicts.
+                    lastError = Error.CONFLICTING_DESTINATION_VERTEX;
+                    return null;
+                }
+                existingEdge.getDefinitions().add(definition);
+                return existingEdge;
+
+            } else {
+                GraphElementIdentifier identifier =
+                        new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.EDGE, edgeLabelName);
+                Edge newEdge = new Edge(identifier, vertexLabelMap.get(destinationLabelName),
+                        vertexLabelMap.get(sourceLabelName), definition);
+                workingSchema.edgeList.add(newEdge);
+                edgeLabelMap.put(edgeLabelName, newEdge);
+                return newEdge;
+            }
+        }
+
+        public Schema build() {
+            return workingSchema;
+        }
+
+        public Error getLastError() {
+            return lastError;
+        }
+
+        public enum Error {
+            NO_ERROR,
+
+            CONFLICTING_PRIMARY_KEY,
+            CONFLICTING_SOURCE_VERTEX,
+            CONFLICTING_DESTINATION_VERTEX,
+
+            SOURCE_VERTEX_NOT_FOUND,
+            DESTINATION_VERTEX_NOT_FOUND
+        }
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
new file mode 100644
index 0000000..09c28d4
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata representation of a vertex. A vertex consists of the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the vertex across other graph elements.
+ * 2. A collection of vertex definitions, each of which consists of a primary key and a SQL++ string.
+ */
+public class Vertex implements Element {
+    private static final long serialVersionUID = 1L;
+
+    private final GraphElementIdentifier identifier;
+    private final List<Definition> definitions;
+
+    // Use {@code Schema.Builder} to build Vertex instances instead of this constructor.
+    Vertex(GraphElementIdentifier identifier, Definition definition) {
+        this.identifier = Objects.requireNonNull(identifier);
+        this.definitions = new ArrayList<>();
+        this.definitions.add(Objects.requireNonNull(definition));
+    }
+
+    public static class Definition {
+        private final List<List<String>> primaryKeyFieldNames;
+        private final String definition;
+
+        Definition(List<List<String>> primaryKeyFieldNames, String definition) {
+            this.primaryKeyFieldNames = primaryKeyFieldNames;
+            this.definition = definition;
+        }
+
+        public List<List<String>> getPrimaryKeyFieldNames() {
+            return primaryKeyFieldNames;
+        }
+
+        public String getDefinition() {
+            return definition;
+        }
+
+        @Override
+        public String toString() {
+            return definition;
+        }
+    }
+
+    // A primary key is the same across all vertex definitions.
+    public List<List<String>> getPrimaryKeyFieldNames() {
+        return definitions.get(0).getPrimaryKeyFieldNames();
+    }
+
+    public List<Definition> getDefinitions() {
+        return definitions;
+    }
+
+    @Override
+    public GraphElementIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public String getLabelName() {
+        return identifier.getLabelName();
+    }
+
+    @Override
+    public List<String> getDefinitionBodies() {
+        return definitions.stream().map(Definition::getDefinition).collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        return "(:" + getLabelName() + ") AS " + String.join(",\n", getDefinitionBodies());
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
index 9c83184..544c462 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
@@ -31,14 +31,17 @@
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataIndexes;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataRecordTypes;
+import org.apache.asterix.graphix.metadata.entities.Edge;
 import org.apache.asterix.graphix.metadata.entities.Graph;
-import org.apache.asterix.graphix.metadata.entities.GraphDependencies;
+import org.apache.asterix.graphix.metadata.entities.Schema;
+import org.apache.asterix.graphix.metadata.entities.Vertex;
 import org.apache.asterix.metadata.entitytupletranslators.AbstractTupleTranslator;
 import org.apache.asterix.om.base.AOrderedList;
 import org.apache.asterix.om.base.ARecord;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.base.IACursor;
 import org.apache.asterix.om.types.AOrderedListType;
+import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Triple;
@@ -52,9 +55,11 @@
 
     // For constructing our dependency, edge, and vertex lists.
     protected OrderedListBuilder listBuilder;
+    protected OrderedListBuilder defListBuilder;
     protected OrderedListBuilder innerListBuilder;
     protected OrderedListBuilder nameListBuilder;
-    protected IARecordBuilder subRecordBuilder;
+    protected IARecordBuilder elemRecordBuilder;
+    protected IARecordBuilder defRecordBuilder;
     protected AOrderedListType stringListList;
     protected AOrderedListType stringList;
 
@@ -62,9 +67,13 @@
         super(getTuple, GraphixMetadataIndexes.GRAPH_DATASET, GRAPH_PAYLOAD_TUPLE_FIELD_INDEX);
         if (getTuple) {
             listBuilder = new OrderedListBuilder();
+            defListBuilder = new OrderedListBuilder();
             innerListBuilder = new OrderedListBuilder();
             nameListBuilder = new OrderedListBuilder();
-            subRecordBuilder = new RecordBuilder();
+            elemRecordBuilder = new RecordBuilder();
+            defRecordBuilder = new RecordBuilder();
+
+            // Avoid having to create the string list types multiple times.
             stringListList = new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null);
             stringList = new AOrderedListType(BuiltinType.ASTRING, null);
         }
@@ -97,13 +106,12 @@
         }
 
         // Read in the vertex and edge lists.
-        GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName);
-        Graph.Schema graphSchema = readGraphSchema(graphRecord, graphIdentifier);
-        return new Graph(graphIdentifier, graphSchema, new GraphDependencies(graphDependencies));
+        Schema graphSchema = readGraphSchema(graphRecord, new GraphIdentifier(dataverseName, graphName));
+        return new Graph(graphSchema, new Graph.Dependencies(graphDependencies));
     }
 
-    private Graph.Schema readGraphSchema(ARecord graphRecord, GraphIdentifier graphIdentifier) throws AsterixException {
-        Graph.Schema.Builder schemaBuilder = new Graph.Schema.Builder(graphIdentifier);
+    private Schema readGraphSchema(ARecord graphRecord, GraphIdentifier graphIdentifier) throws AsterixException {
+        Schema.Builder schemaBuilder = new Schema.Builder(graphIdentifier);
 
         // Read in the vertex list.
         IACursor verticesCursor = ((AOrderedList) graphRecord
@@ -112,27 +120,33 @@
             ARecord vertex = (ARecord) verticesCursor.get();
 
             // Read in the label name.
-            String labelName = ((AString) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX))
+            String labelName =
+                    ((AString) vertex.getValueByPos(GraphixMetadataRecordTypes.VERTICES_ARECORD_LABEL_FIELD_INDEX))
                             .getStringValue();
 
-            // Read in the primary key fields.
-            List<List<String>> primaryKeyFields = new ArrayList<>();
-            IACursor primaryKeyCursor = ((AOrderedList) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX))
-                            .getCursor();
-            while (primaryKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
-                primaryKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the vertex definition(s). Validate each definition.
+            // Read in our vertex definitions.
             IACursor definitionsCursor = ((AOrderedList) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX))
-                            .getCursor();
+                    .getValueByPos(GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
             while (definitionsCursor.next()) {
-                String definition = ((AString) definitionsCursor.get()).getStringValue();
-                schemaBuilder.addVertex(labelName, primaryKeyFields, definition);
+                ARecord definition = (ARecord) definitionsCursor.get();
+
+                // Read in the primary key fields.
+                List<List<String>> primaryKeyFields = new ArrayList<>();
+                IACursor primaryKeyCursor = ((AOrderedList) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX))
+                                .getCursor();
+                while (primaryKeyCursor.next()) {
+                    IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
+                    primaryKeyFields.add(readNameList(nameCursor));
+                }
+
+                // Read in the definition body.
+                String definitionBody = ((AString) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX))
+                                .getStringValue();
+
+                // Read in the vertex definition, and perform validation of the metadata record.
+                schemaBuilder.addVertex(labelName, primaryKeyFields, definitionBody);
                 switch (schemaBuilder.getLastError()) {
                     case NO_ERROR:
                         break;
@@ -157,53 +171,70 @@
 
             // Read in the label name.
             String labelName =
-                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX))
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_LABEL_FIELD_INDEX))
                             .getStringValue();
 
             // Read in the destination label name.
-            String destinationLabelName = ((AString) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX))
+            String destinationLabelName =
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_DEST_LABEL_FIELD_INDEX))
                             .getStringValue();
 
             // Read in the source label name.
-            String sourceLabelName = ((AString) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX))
+            String sourceLabelName =
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX))
                             .getStringValue();
 
-            // Read in the primary key fields.
-            List<List<String>> primaryKeyFields = new ArrayList<>();
-            IACursor primaryKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX)).getCursor();
-            while (primaryKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
-                primaryKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the destination key fields.
-            List<List<String>> destinationKeyFields = new ArrayList<>();
-            IACursor destinationKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX)).getCursor();
-            while (destinationKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) destinationKeyCursor.get()).getCursor();
-                destinationKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the source key fields.
-            List<List<String>> sourceKeyFields = new ArrayList<>();
-            IACursor sourceKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX)).getCursor();
-            while (sourceKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) sourceKeyCursor.get()).getCursor();
-                sourceKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the edge definition(s). Validate each definition.
+            // Read in our edge definitions.
             IACursor definitionsCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
+                    .getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
             while (definitionsCursor.next()) {
-                String definition = ((AString) definitionsCursor.get()).getStringValue();
-                schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, primaryKeyFields,
-                        destinationKeyFields, sourceKeyFields, definition);
+                ARecord definition = (ARecord) definitionsCursor.get();
+
+                // Read in the destination key fields (this is common to all edge definition records).
+                List<List<String>> destinationKeyFields = new ArrayList<>();
+                IACursor destinationKeyCursor = ((AOrderedList) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX)).getCursor();
+                while (destinationKeyCursor.next()) {
+                    IACursor nameCursor = ((AOrderedList) destinationKeyCursor.get()).getCursor();
+                    destinationKeyFields.add(readNameList(nameCursor));
+                }
+
+                if (definition.getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX)
+                        .getType().getTypeTag().equals(ATypeTag.MISSING)) {
+                    // We are finished. Read in the edge definition, and perform validation of the metadata record.
+                    schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, destinationKeyFields);
+
+                } else {
+                    // Read in the source key fields.
+                    List<List<String>> sourceKeyFields = new ArrayList<>();
+                    IACursor sourceKeyCursor = ((AOrderedList) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX))
+                                    .getCursor();
+                    while (sourceKeyCursor.next()) {
+                        IACursor nameCursor = ((AOrderedList) sourceKeyCursor.get()).getCursor();
+                        sourceKeyFields.add(readNameList(nameCursor));
+                    }
+
+                    // Read in the primary key fields.
+                    List<List<String>> primaryKeyFields = new ArrayList<>();
+                    IACursor primaryKeyCursor = ((AOrderedList) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX))
+                                    .getCursor();
+                    while (primaryKeyCursor.next()) {
+                        IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
+                        primaryKeyFields.add(readNameList(nameCursor));
+                    }
+
+                    // Read in the definition body.
+                    String definitionBody = ((AString) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX))
+                                    .getStringValue();
+
+                    // Finally, read in the edge definition and perform validation of the metadata record.
+                    schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, primaryKeyFields,
+                            destinationKeyFields, sourceKeyFields, definitionBody);
+                }
+
                 switch (schemaBuilder.getLastError()) {
                     case NO_ERROR:
                         break;
@@ -293,7 +324,7 @@
         // Write our vertex set.
         listBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.GRAPH_RECORDTYPE
                 .getFieldTypes()[GraphixMetadataRecordTypes.GRAPH_ARECORD_VERTICES_FIELD_INDEX]);
-        for (Graph.Vertex vertex : graph.getGraphSchema().getVertices()) {
+        for (Vertex vertex : graph.getGraphSchema().getVertices()) {
             writeVertexRecord(vertex, itemValue);
             listBuilder.addItem(itemValue);
         }
@@ -304,7 +335,7 @@
         // Write our edge set.
         listBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.GRAPH_RECORDTYPE
                 .getFieldTypes()[GraphixMetadataRecordTypes.GRAPH_ARECORD_EDGES_FIELD_INDEX]);
-        for (Graph.Edge edge : graph.getGraphSchema().getEdges()) {
+        for (Edge edge : graph.getGraphSchema().getEdges()) {
             writeEdgeRecord(edge, itemValue);
             listBuilder.addItem(itemValue);
         }
@@ -319,108 +350,128 @@
         return tuple;
     }
 
-    private void writeVertexRecord(Graph.Vertex vertex, ArrayBackedValueStorage itemValue) throws HyracksDataException {
-        subRecordBuilder.reset(GraphixMetadataRecordTypes.VERTICES_RECORDTYPE);
+    private void writeVertexRecord(Vertex vertex, ArrayBackedValueStorage itemValue) throws HyracksDataException {
+        elemRecordBuilder.reset(GraphixMetadataRecordTypes.VERTICES_RECORDTYPE);
 
         // Write the label name.
         fieldValue.reset();
         aString.setValue(vertex.getLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
-
-        // Write the primary key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : vertex.getPrimaryKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX,
-                fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.VERTICES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the vertex definition(s).
-        fieldValue.reset();
-        innerListBuilder.reset(stringList);
-        for (String definition : vertex.getDefinitions()) {
-            itemValue.reset();
-            aString.setValue(definition);
-            stringSerde.serialize(aString, itemValue.getDataOutput());
-            innerListBuilder.addItem(itemValue);
+        defListBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.VERTICES_RECORDTYPE
+                .getFieldTypes()[GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX]);
+        for (Vertex.Definition definition : vertex.getDefinitions()) {
+            defRecordBuilder.reset(GraphixMetadataRecordTypes.VERTEX_DEF_RECORDTYPE);
+
+            // Write the primary key fields.
+            fieldValue.reset();
+            innerListBuilder.reset(new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null));
+            for (List<String> keyField : definition.getPrimaryKeyFieldNames()) {
+                writeNameList(keyField, itemValue);
+                innerListBuilder.addItem(itemValue);
+            }
+            innerListBuilder.write(fieldValue.getDataOutput(), true);
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX,
+                    fieldValue);
+
+            // Write the definition body.
+            fieldValue.reset();
+            aString.setValue(definition.getDefinition());
+            stringSerde.serialize(aString, fieldValue.getDataOutput());
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX, fieldValue);
+
+            fieldValue.reset();
+            defRecordBuilder.write(fieldValue.getDataOutput(), true);
+            defListBuilder.addItem(fieldValue);
         }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX,
-                fieldValue);
+        fieldValue.reset();
+        defListBuilder.write(fieldValue.getDataOutput(), true);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
 
         itemValue.reset();
-        subRecordBuilder.write(itemValue.getDataOutput(), true);
+        elemRecordBuilder.write(itemValue.getDataOutput(), true);
     }
 
-    private void writeEdgeRecord(Graph.Edge edge, ArrayBackedValueStorage itemValue) throws HyracksDataException {
-        subRecordBuilder.reset(GraphixMetadataRecordTypes.EDGES_RECORDTYPE);
+    private void writeEdgeRecord(Edge edge, ArrayBackedValueStorage itemValue) throws HyracksDataException {
+        elemRecordBuilder.reset(GraphixMetadataRecordTypes.EDGES_RECORDTYPE);
 
         // Write the label name.
         fieldValue.reset();
         aString.setValue(edge.getLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the destination label name.
         fieldValue.reset();
         aString.setValue(edge.getDestinationLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_DEST_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the source label name.
         fieldValue.reset();
         aString.setValue(edge.getSourceLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX, fieldValue);
-
-        // Write the primary key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getPrimaryKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX, fieldValue);
-
-        // Write the destination key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getDestinationKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX, fieldValue);
-
-        // Write the source key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getSourceKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the edge definition(s).
-        fieldValue.reset();
-        innerListBuilder.reset(stringList);
-        for (String definition : edge.getDefinitions()) {
-            itemValue.reset();
-            aString.setValue(definition);
-            stringSerde.serialize(aString, itemValue.getDataOutput());
-            innerListBuilder.addItem(itemValue);
+        defListBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.EDGES_RECORDTYPE
+                .getFieldTypes()[GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX]);
+        for (Edge.Definition definition : edge.getDefinitions()) {
+            defRecordBuilder.reset(GraphixMetadataRecordTypes.EDGE_DEF_RECORDTYPE);
+
+            // Write the destination key fields.
+            fieldValue.reset();
+            innerListBuilder.reset(stringListList);
+            for (List<String> keyField : definition.getDestinationKeyFieldNames()) {
+                writeNameList(keyField, itemValue);
+                innerListBuilder.addItem(itemValue);
+            }
+            innerListBuilder.write(fieldValue.getDataOutput(), true);
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX, fieldValue);
+
+            if (!definition.isSimpleDefinition()) {
+                // Write the source key fields.
+                fieldValue.reset();
+                innerListBuilder.reset(stringListList);
+                for (List<String> keyField : definition.getSourceKeyFieldNames()) {
+                    writeNameList(keyField, itemValue);
+                    innerListBuilder.addItem(itemValue);
+                }
+                innerListBuilder.write(fieldValue.getDataOutput(), true);
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX,
+                        fieldValue);
+
+                // Write the primary key fields.
+                fieldValue.reset();
+                innerListBuilder.reset(stringListList);
+                for (List<String> keyField : definition.getPrimaryKeyFieldNames()) {
+                    writeNameList(keyField, itemValue);
+                    innerListBuilder.addItem(itemValue);
+                }
+                innerListBuilder.write(fieldValue.getDataOutput(), true);
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX,
+                        fieldValue);
+
+                // Write the definition body.
+                fieldValue.reset();
+                aString.setValue(definition.getDefinition());
+                stringSerde.serialize(aString, fieldValue.getDataOutput());
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX,
+                        fieldValue);
+            }
+
+            fieldValue.reset();
+            defRecordBuilder.write(fieldValue.getDataOutput(), true);
+            defListBuilder.addItem(fieldValue);
         }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
+        fieldValue.reset();
+        defListBuilder.write(fieldValue.getDataOutput(), true);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
 
         itemValue.reset();
-        subRecordBuilder.write(itemValue.getDataOutput(), true);
+        elemRecordBuilder.write(itemValue.getDataOutput(), true);
     }
 
     private void writeNameList(List<String> name, ArrayBackedValueStorage itemValue) throws HyracksDataException {
diff --git a/asterix-graphix/src/main/resources/lang-extension/lang.txt b/asterix-graphix/src/main/resources/lang-extension/lang.txt
index 6d215f0..18967a4 100644
--- a/asterix-graphix/src/main/resources/lang-extension/lang.txt
+++ b/asterix-graphix/src/main/resources/lang-extension/lang.txt
@@ -213,11 +213,10 @@
     createNewScope();
   }
   (
-    vertexDefinitionExpr = ViewBody()
-    | <LEFTPAREN> vertexDefinitionExpr = ViewBody() <RIGHTPAREN>
+    vertexDefinitionExpr = ViewBody() { endPos = token; }
+    | <LEFTPAREN> { beginPos = token; } vertexDefinitionExpr = ViewBody() { endPos = token; } <RIGHTPAREN>
   )
   {
-    endPos = token;
     String vDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
     removeCurrentScope();
     GraphConstructor.VertexElement vertexElement = new GraphConstructor.VertexElement(vertexName,
@@ -279,8 +278,8 @@
         createNewScope();
       }
       (
-        edgeDefinitionExpr = ViewBody()
-        | <LEFTPAREN> edgeDefinitionExpr = ViewBody() <RIGHTPAREN>
+        edgeDefinitionExpr = ViewBody() { endPos = token; }
+        | <LEFTPAREN> { beginPos = token; } edgeDefinitionExpr = ViewBody() { endPos = token; } <RIGHTPAREN>
       )
     )
     |
@@ -306,7 +305,6 @@
 
     String eDef = null;
     if (edgeDefinitionExpr != null) {
-      endPos = token;
       eDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
       removeCurrentScope();
     }
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
index 0820e29..7d5380b 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
@@ -26,7 +26,7 @@
 import org.apache.asterix.test.runtime.ExecutionTestUtil;
 import org.apache.asterix.testframework.context.TestCaseContext;
 import org.apache.asterix.testframework.xml.TestGroup;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -36,10 +36,9 @@
 
 @RunWith(Parameterized.class)
 public class GraphixExecutionTest {
-    protected static final String PATH_ACTUAL = "target/rttest" + File.separator;
-    protected static final String PATH_BASE =
-            StringUtils.join(new String[] { "src", "test", "resources", "runtimets" }, File.separator);
-    protected static final String TEST_CONFIG_FILE_NAME = "src/main/resources/cc.conf";
+    protected static final String PATH_ACTUAL = FileUtil.joinPath("target", "rttest");
+    protected static final String PATH_BASE = FileUtil.joinPath("src", "test", "resources", "runtimets");
+    protected static final String FILE_TEST_CONFIG = FileUtil.joinPath("src", "main", "resources", "cc.conf");
     private static final String TEST_SUITE_FILE = "testsuite.xml";
     private static final String ONLY_SUITE_FILE = "only.xml";
 
@@ -57,7 +56,7 @@
     public static void setUp() throws Exception {
         //noinspection ResultOfMethodCallIgnored
         new File(PATH_ACTUAL).mkdirs();
-        ExecutionTestUtil.setUp(true, TEST_CONFIG_FILE_NAME, integrationUtil, false, null);
+        ExecutionTestUtil.setUp(true, FILE_TEST_CONFIG, integrationUtil, false, null);
     }
 
     @AfterClass
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
index e3ea8e9..72485f1 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
@@ -16,12 +16,14 @@
 
 import org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;
 import org.apache.hyracks.test.support.TestUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class GraphixIntegrationUtil extends AsterixHyracksIntegrationUtil {
-    private static final String DEFAULT_CONF_FILE = "asterixdb/asterix-opt/asterix-graphix/src/main/resources/cc.conf";
+    private static final String FILE_DEFAULT_CONF =
+            FileUtil.joinPath("asterixdb", "asterix-opt", "asterix-graphix", "src", "main", "resources", "cc.conf");
 
     public static void main(String[] args) {
         TestUtils.redirectLoggingToConsole();
@@ -29,7 +31,7 @@
         try {
             boolean cleanupOnStart = Boolean.getBoolean("cleanup.start");
             boolean cleanupOnShutdown = Boolean.getBoolean("cleanup.shutdown");
-            String confFile = System.getProperty("external.lib", DEFAULT_CONF_FILE);
+            String confFile = System.getProperty("external.lib", FILE_DEFAULT_CONF);
             graphixIntegrationUtil.run(cleanupOnStart, cleanupOnShutdown, confFile);
         } catch (Exception e) {
             e.printStackTrace();
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
index bf2c756..70331b1 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
@@ -22,7 +22,7 @@
 
 import org.apache.asterix.test.common.TestExecutor;
 import org.apache.asterix.testframework.context.TestCaseContext;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -32,10 +32,9 @@
 
 @RunWith(Parameterized.class)
 public class GraphixMetadataTest {
-    private static final String PATH_ACTUAL = "target" + File.separator + "mdtest" + File.separator;
-    private static final String PATH_BASE =
-            StringUtils.join(new String[] { "src", "test", "resources", "metadata" + File.separator }, File.separator);
-    private static final String TEST_CONFIG_FILE_NAME = "src/main/resources/cc.conf";
+    private static final String PATH_ACTUAL = FileUtil.joinPath("target", "mdtest");
+    private static final String PATH_BASE = FileUtil.joinPath("src", "test", "resources", "metadata");
+    private static final String FILE_TEST_CONFIG = FileUtil.joinPath("src", "main", "resources", "cc.conf");
 
     private static final TestExecutor testExecutor = new TestExecutor();
     private static final GraphixIntegrationUtil integrationUtil = new GraphixIntegrationUtil();
@@ -50,7 +49,7 @@
     public static void setUp() throws Exception {
         //noinspection ResultOfMethodCallIgnored
         new File(PATH_ACTUAL).mkdirs();
-        integrationUtil.init(true, TEST_CONFIG_FILE_NAME);
+        integrationUtil.init(true, FILE_TEST_CONFIG);
     }
 
     @AfterClass
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
index 31c330c..35bc5e2 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
@@ -1,2 +1,2 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_1", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "PrimaryKey": [ [ "user_id" ] ], "Definitions": [ "Yelp_B.Yelpers" ] }, { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "Definitions": [ "( FROM    Yelp_B.Users U\n                       UNNEST  U.friends F\n                       SELECT  U.user_id, F AS friend )", "Yelp_B.Friends" ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "user_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
\ No newline at end of file
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_1", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "Definitions": [ { "PrimaryKey": [ [ "user_id" ] ], "Definition": "Yelp_B.Yelpers" } ] }, { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "Definitions": [ { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "FROM    Yelp_B.Users U\n                       UNNEST  U.friends F\n                       SELECT  U.user_id, F AS friend" }, { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "Yelp_B.Friends" } ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "user_id" ] ] } ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
index a749b72..90ebd8b 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
@@ -1 +1 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
index 80af6cb..b723764 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
@@ -1 +1 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "PrimaryKey": [ [ "user_id" ] ], "Definitions": [ "Yelp_B.Yelpers" ] }, { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                               SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                               SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "Definitions": [ "( FROM    Yelp_B.Users U\n                               UNNEST  U.friends F\n                               SELECT  U.user_id, F AS friend )", "Yelp_B.Friends" ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "user_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "Definitions": [ { "PrimaryKey": [ [ "user_id" ] ], "Definition": "Yelp_B.Yelpers" } ] }, { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                               SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                               SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "Definitions": [ { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "FROM    Yelp_B.Users U\n                               UNNEST  U.friends F\n                               SELECT  U.user_id, F AS friend" }, { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "Yelp_B.Friends" } ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "user_id" ] ] } ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/14764
To unsubscribe, or for help writing mail filters, visit https://asterix-gerrit.ics.uci.edu/settings

Gerrit-Project: asterixdb-graph
Gerrit-Branch: master
Gerrit-Change-Id: I1686875b004451f3d7b4f1f45f1a854ccb5e8a04
Gerrit-Change-Number: 14764
Gerrit-PatchSet: 1
Gerrit-Owner: Glenn Galvizo <gg...@uci.edu>
Gerrit-MessageType: newchange

Change in asterixdb-graph[master]: [NO-ISSUE][GRAPH] Refactoring metadata for Graph

Posted by AsterixDB Code Review <do...@asterix-gerrit.ics.uci.edu>.
From Glenn Galvizo <gg...@uci.edu>:

Glenn Galvizo has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/14764 )


Change subject: [NO-ISSUE][GRAPH] Refactoring metadata for Graph
......................................................................

[NO-ISSUE][GRAPH] Refactoring metadata for Graph

1. Definitions are now a list of objects for both vertices and edges.
2. We no longer store the "(" ")" for definition bodies.
3. Simplified the logic to obtain a normalized body for graph elements
   (to resolve dependencies).
4. Slight refactoring of the graph element classes.

Change-Id: I1686875b004451f3d7b4f1f45f1a854ccb5e8a04
---
M asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
R asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
D asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
A asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
M asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
M asterix-graphix/src/main/resources/lang-extension/lang.txt
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
M asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
M asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
24 files changed, 800 insertions(+), 632 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb-graph refs/changes/64/14764/1

diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
index 59889a3..24ce57c 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.asterix.graphix.app.translator;
 
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
@@ -35,11 +34,11 @@
 import org.apache.asterix.graphix.extension.GraphixMetadataExtension;
 import org.apache.asterix.graphix.lang.expression.GraphElementExpr;
 import org.apache.asterix.graphix.lang.rewrites.GraphixQueryRewriter;
-import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext;
 import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
 import org.apache.asterix.graphix.metadata.entities.Graph;
 import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.statement.DataverseDropStatement;
 import org.apache.asterix.lang.common.statement.DropDatasetStatement;
 import org.apache.asterix.lang.common.statement.FunctionDropStatement;
@@ -77,10 +76,9 @@
         Query query = ExpressionUtils.createWrappedQuery(functionCall, graphElementDecl.getSourceLocation());
 
         // We call our rewriter to set the normalized bodies of {@code graphElementDecl}.
-        GraphixRewritingContext graphixRewritingContext =
-                new GraphixRewritingContext(metadataProvider, declaredFunctions, null,
-                        Collections.singletonList(graphElementDecl), warningCollector, query.getVarCounter());
-        queryRewriter.loadNormalizedGraphElements(graphixRewritingContext, query);
+        LangRewritingContext rewriteContext = new LangRewritingContext(metadataProvider, declaredFunctions, null,
+                warningCollector, query.getVarCounter());
+        queryRewriter.loadNormalizedGraphElement(rewriteContext, query, graphElementDecl);
     }
 
     @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
index 8ba4cea..0a07239 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
@@ -21,7 +21,7 @@
 import java.util.List;
 
 import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.lang.common.base.AbstractExpression;
 import org.apache.asterix.lang.common.base.AbstractLangExpression;
 import org.apache.asterix.lang.common.base.Expression;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
index b1337b7..c8d8723 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphElementBodyParser.java
@@ -19,24 +19,33 @@
 package org.apache.asterix.graphix.lang.parser;
 
 import java.io.StringReader;
-import java.util.Objects;
 
 import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
-import org.apache.asterix.graphix.metadata.entities.Graph;
+import org.apache.asterix.graphix.metadata.entities.Element;
 import org.apache.hyracks.api.exceptions.IWarningCollector;
 
 public final class GraphElementBodyParser {
     // Just a wrapper for the parseGraphElementBody method.
-    public static GraphElementDecl parse(Graph.Element element, GraphixParserFactory parserFactory,
+    public static GraphElementDecl parse(Element element, GraphixParserFactory parserFactory,
             IWarningCollector warningCollector) throws CompilationException {
         GraphElementDecl graphElementDecl = null;
-        for (String definition : element.getDefinitions()) {
-            if (Objects.equals(definition, "")) {
+        for (String definition : element.getDefinitionBodies()) {
+            if (definition == null) {
                 continue;
             }
+
+            // Parse our the definition.
             GraphixParser parser = (GraphixParser) parserFactory.createParser(new StringReader(definition));
-            GraphElementDecl parsedElementDecl = parser.parseGraphElementBody(element.getIdentifier());
+            GraphElementDecl parsedElementDecl;
+            try {
+                parsedElementDecl = parser.parseGraphElementBody(element.getIdentifier());
+
+            } catch (CompilationException e) {
+                throw new CompilationException(ErrorCode.COMPILATION_ERROR,
+                        "Bad definition for a graph element: " + e.getMessage());
+            }
 
             // Accumulate the element bodies.
             if (graphElementDecl == null) {
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
index 9e88251..c454773 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java
@@ -22,25 +22,20 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
-import org.apache.asterix.graphix.extension.GraphixMetadataExtension;
 import org.apache.asterix.graphix.lang.expression.GraphElementExpr;
-import org.apache.asterix.graphix.lang.parser.GraphElementBodyParser;
 import org.apache.asterix.graphix.lang.parser.GraphixParserFactory;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
-import org.apache.asterix.graphix.metadata.entities.Graph;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.IParserFactory;
 import org.apache.asterix.lang.common.base.IReturningStatement;
 import org.apache.asterix.lang.common.expression.AbstractCallExpression;
+import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.statement.Query;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
 import org.apache.asterix.lang.common.util.ExpressionUtils;
@@ -57,27 +52,19 @@
     public GraphixQueryRewriter(IParserFactory parserFactory) {
         super(parserFactory);
 
-        // We can safely downcast to our specific parser factory here.
+        // We safely downcast to our specific parser factory here.
         this.parserFactory = (GraphixParserFactory) parserFactory;
-        this.bodyRewriter = new SqlppQueryRewriter(parserFactory);
+        this.bodyRewriter = getFunctionAndViewBodyRewriter();
     }
 
-    public void rewrite(GraphixRewritingContext rewriteContext, IReturningStatement topStatement,
-            boolean allowNonStoredUdfCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars)
-            throws CompilationException {
-        // Get the graph elements in the top statement.
-        Map<GraphElementIdentifier, GraphElementDecl> graphElements =
-                loadNormalizedGraphElements(rewriteContext, topStatement);
-
-        // Perform the remainder of our rewrites in our parent.
-        super.rewrite(rewriteContext.getLangRewritingContext(), topStatement, allowNonStoredUdfCalls,
-                inlineUdfsAndViews, externalVars);
+    @Override
+    public void rewrite(LangRewritingContext context, IReturningStatement topStatement, boolean allowNonStoredUdfCalls,
+            boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) throws CompilationException {
+        super.rewrite(context, topStatement, allowNonStoredUdfCalls, inlineUdfsAndViews, externalVars);
     }
 
-    public Map<GraphElementIdentifier, GraphElementDecl> loadNormalizedGraphElements(
-            GraphixRewritingContext rewriteContext, IReturningStatement topExpr) throws CompilationException {
-        Map<GraphElementIdentifier, GraphElementDecl> graphElements = new HashMap<>();
-
+    public void loadNormalizedGraphElement(LangRewritingContext context, IReturningStatement topExpr,
+            GraphElementDecl graphElementDecl) throws CompilationException {
         // Gather all function calls.
         Deque<AbstractCallExpression> workQueue = new ArrayDeque<>();
         SqlppGatherFunctionCallsVisitor callVisitor = new SqlppGatherFunctionCallsVisitor(workQueue);
@@ -87,71 +74,31 @@
 
         AbstractCallExpression fnCall;
         while ((fnCall = workQueue.poll()) != null) {
-            // Load only the graph element declarations (we will load the rest of the elements in the parent).
-            if (!fnCall.getKind().equals(Expression.Kind.CALL_EXPRESSION)
-                    || !fnCall.getFunctionSignature().getName().equals(GraphElementExpr.GRAPH_ELEMENT_FUNCTION_NAME)) {
+            // We only care about graph element declarations.
+            if (!fnCall.getFunctionSignature().getName().equals(GraphElementExpr.GRAPH_ELEMENT_FUNCTION_NAME)) {
                 continue;
             }
-            GraphElementExpr graphElementExpr = (GraphElementExpr) fnCall;
-            GraphElementIdentifier identifier = graphElementExpr.getIdentifier();
-            if (!graphElements.containsKey(identifier)) {
 
-                // First, check if we have already loaded this graph element.
-                GraphElementDecl elementDecl = rewriteContext.getDeclaredGraphElements().get(identifier);
-
-                // If we cannot find the graph element in our context, search our metadata.
-                if (elementDecl == null) {
-                    GraphIdentifier graphIdentifier = identifier.getGraphIdentifier();
-                    Graph graph;
-                    try {
-                        graph = GraphixMetadataExtension.getGraph(
-                                rewriteContext.getMetadataProvider().getMetadataTxnContext(),
-                                graphIdentifier.getDataverseName(), graphIdentifier.getGraphName());
-
-                    } catch (AlgebricksException e) {
-                        throw new CompilationException(ErrorCode.COMPILATION_ERROR,
-                                graphElementExpr.getSourceLocation(),
-                                "Graph " + graphIdentifier.getGraphName() + " does not exist.");
-                    }
-
-                    // Parse our graph element.
-                    if (graph == null) {
-                        throw new CompilationException(ErrorCode.COMPILATION_ERROR,
-                                graphElementExpr.getSourceLocation(),
-                                "Graph " + graphIdentifier.getGraphName() + " does not exist.");
-                    }
-                    Graph.Element element = graph.getGraphSchema().getElement(identifier);
-                    elementDecl = GraphElementBodyParser.parse(element, parserFactory,
-                            rewriteContext.getLangRewritingContext().getWarningCollector());
-                }
-
-                // Get our normalized element bodies.
-                List<Expression> normalizedBodies = elementDecl.getNormalizedBodies();
-                if (normalizedBodies.size() != elementDecl.getBodies().size()) {
-                    GraphIdentifier graphIdentifier = elementDecl.getGraphIdentifier();
-                    for (Expression body : elementDecl.getBodies()) {
-                        Expression normalizedBody =
-                                rewriteGraphElementBody(rewriteContext, graphIdentifier.getDataverseName(), body,
-                                        Collections.emptyList(), elementDecl.getSourceLocation());
-                        normalizedBodies.add(normalizedBody);
-                    }
-                }
-
-                // Store the element declaration in our map, and iterate over this body.
-                graphElements.put(identifier, elementDecl);
-                for (Expression e : elementDecl.getNormalizedBodies()) {
-                    e.accept(callVisitor, null);
+            // Get our normalized element bodies, by calling our rewriter.
+            List<Expression> normalizedBodies = graphElementDecl.getNormalizedBodies();
+            if (normalizedBodies.size() != graphElementDecl.getBodies().size()) {
+                GraphIdentifier graphIdentifier = graphElementDecl.getGraphIdentifier();
+                for (Expression body : graphElementDecl.getBodies()) {
+                    Expression normalizedBody = rewriteGraphElementBody(context, graphIdentifier.getDataverseName(),
+                            body, Collections.emptyList(), graphElementDecl.getSourceLocation());
+                    normalizedBodies.add(normalizedBody);
                 }
             }
-        }
 
-        return graphElements;
+            // We should only have one element. We can safely break here.
+            break;
+        }
     }
 
-    private Expression rewriteGraphElementBody(GraphixRewritingContext rewriteContext, DataverseName elementDataverse,
+    private Expression rewriteGraphElementBody(LangRewritingContext context, DataverseName elementDataverse,
             Expression bodyExpr, List<VarIdentifier> externalVars, SourceLocation sourceLoc)
             throws CompilationException {
-        Dataverse defaultDataverse = rewriteContext.getMetadataProvider().getDefaultDataverse();
+        Dataverse defaultDataverse = context.getMetadataProvider().getDefaultDataverse();
         Dataverse targetDataverse;
 
         // We might need to change our dataverse, if the element definition requires a different one.
@@ -160,18 +107,18 @@
 
         } else {
             try {
-                targetDataverse = rewriteContext.getMetadataProvider().findDataverse(elementDataverse);
+                targetDataverse = context.getMetadataProvider().findDataverse(elementDataverse);
 
             } catch (AlgebricksException e) {
                 throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, e, sourceLoc, elementDataverse);
             }
         }
-        rewriteContext.getMetadataProvider().setDefaultDataverse(targetDataverse);
+        context.getMetadataProvider().setDefaultDataverse(targetDataverse);
 
         // Get the body of the rewritten query.
         try {
             Query wrappedQuery = ExpressionUtils.createWrappedQuery(bodyExpr, sourceLoc);
-            bodyRewriter.rewrite(rewriteContext.getLangRewritingContext(), wrappedQuery, false, false, externalVars);
+            bodyRewriter.rewrite(context, wrappedQuery, false, false, externalVars);
             return wrappedQuery.getBody();
 
         } catch (CompilationException e) {
@@ -180,7 +127,7 @@
 
         } finally {
             // Switch back to the working dataverse.
-            rewriteContext.getMetadataProvider().setDefaultDataverse(defaultDataverse);
+            context.getMetadataProvider().setDefaultDataverse(defaultDataverse);
         }
     }
 }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
similarity index 96%
rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java
rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
index 2b41511..fb70ae6 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/base/IGraphixLangVisitor.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.graphix.lang.rewrites.visitor;
+package org.apache.asterix.graphix.lang.rewrites.base;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.graphix.lang.expression.GraphConstructor;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
index 866e2f2..670cb9a 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
@@ -25,7 +25,7 @@
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.graphix.lang.expression.GraphConstructor;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
index e9e8255..31885e4 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
@@ -22,7 +22,7 @@
 import org.apache.asterix.app.translator.QueryTranslator;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
index c5e87de..51446dc 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDecl.java
@@ -27,7 +27,7 @@
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
-import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor;
+import org.apache.asterix.graphix.lang.rewrites.base.IGraphixLangVisitor;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 import org.apache.asterix.metadata.declared.MetadataProvider;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
index 1d7d0da..344c02b 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
@@ -35,8 +35,10 @@
 import org.apache.asterix.graphix.lang.statement.CreateGraphStatement;
 import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
 import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
+import org.apache.asterix.graphix.metadata.entities.Edge;
 import org.apache.asterix.graphix.metadata.entities.Graph;
-import org.apache.asterix.graphix.metadata.entities.GraphDependencies;
+import org.apache.asterix.graphix.metadata.entities.Schema;
+import org.apache.asterix.graphix.metadata.entities.Vertex;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.metadata.MetadataManager;
 import org.apache.asterix.metadata.MetadataTransactionContext;
@@ -80,11 +82,10 @@
         }
 
         // Build the graph schema.
-        GraphIdentifier graphIdentifier = new GraphIdentifier(activeDataverseName, cgs.getGraphName());
-        Graph.Schema.Builder schemaBuilder = new Graph.Schema.Builder(graphIdentifier);
+        Schema.Builder schemaBuilder = new Schema.Builder(new GraphIdentifier(activeDataverseName, cgs.getGraphName()));
         Map<GraphElementIdentifier, GraphElementDecl> graphElementDecls = new LinkedHashMap<>();
         for (GraphConstructor.VertexElement vertex : cgs.getVertexElements()) {
-            Graph.Vertex schemaVertex =
+            Vertex schemaVertex =
                     schemaBuilder.addVertex(vertex.getLabel(), vertex.getPrimaryKeyFields(), vertex.getDefinition());
             switch (schemaBuilder.getLastError()) {
                 case NO_ERROR:
@@ -109,7 +110,7 @@
             }
         }
         for (GraphConstructor.EdgeElement edge : cgs.getEdgeElements()) {
-            Graph.Edge schemaEdge;
+            Edge schemaEdge;
             if (edge.getDefinition() == null) {
                 schemaEdge = schemaBuilder.addEdge(edge.getEdgeLabel(), edge.getDestinationLabel(),
                         edge.getSourceLabel(), edge.getDestinationKeyFields());
@@ -166,7 +167,7 @@
         }
 
         // Build our dependencies (collected over all graph element bodies).
-        GraphDependencies graphDependencies = new GraphDependencies();
+        Graph.Dependencies graphDependencies = new Graph.Dependencies();
         for (GraphElementDecl graphElementDecl : graphElementDecls.values()) {
             if (graphElementDecl.getNormalizedBodies().size() != graphElementDecl.getBodies().size()) {
                 // We should have set the normalized body by calling {@code normalizeGraphElementAsQuery} beforehand.
@@ -179,7 +180,7 @@
         }
 
         // Add / upsert our graph to our metadata.
-        Graph newGraph = new Graph(graphIdentifier, schemaBuilder.build(), graphDependencies);
+        Graph newGraph = new Graph(schemaBuilder.build(), graphDependencies);
         if (existingGraph == null) {
             MetadataManager.INSTANCE.addEntity(mdTxnCtx, newGraph);
 
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
index 055056d..b748f7b 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/bootstrap/GraphixMetadataRecordTypes.java
@@ -19,12 +19,14 @@
 package org.apache.asterix.graphix.metadata.bootstrap;
 
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DATAVERSE_NAME;
+import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DEFINITION;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_DEPENDENCIES;
 import static org.apache.asterix.metadata.bootstrap.MetadataRecordTypes.FIELD_NAME_PRIMARY_KEY;
 
 import org.apache.asterix.metadata.bootstrap.MetadataRecordTypes;
 import org.apache.asterix.om.types.AOrderedListType;
 import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.AUnionType;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 
@@ -32,6 +34,8 @@
     public static final String RECORD_NAME_GRAPH = "GraphRecordType";
     public static final String RECORD_NAME_VERTICES = "VerticesRecordType";
     public static final String RECORD_NAME_EDGES = "EdgesRecordType";
+    public static final String RECORD_NAME_VERTEX_DEF = "VertexDefinitionRecordType";
+    public static final String RECORD_NAME_EDGE_DEF = "EdgeDefinitionRecordType";
 
     public static final String FIELD_NAME_DEFINITIONS = "Definitions";
     public static final String FIELD_NAME_DESTINATION_KEY = "DestinationKey";
@@ -43,31 +47,43 @@
     public static final String FIELD_NAME_SOURCE_LABEL = "SourceLabel";
     public static final String FIELD_NAME_VERTICES = "Vertices";
 
-    public static final int GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX = 0;
-    public static final int GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX = 1;
-    public static final int GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX = 2;
-    public static final ARecordType VERTICES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTICES,
-            new String[] { FIELD_NAME_LABEL, FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DEFINITIONS },
-            new IAType[] { BuiltinType.ASTRING,
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(BuiltinType.ASTRING, null) },
+    public static final int VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX = 0;
+    public static final int VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX = 1;
+    public static final ARecordType VERTEX_DEF_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTEX_DEF,
+            new String[] { FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DEFINITION }, new IAType[] {
+                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null), BuiltinType.ASTRING },
             true);
 
-    public static final int GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX = 0;
-    public static final int GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX = 1;
-    public static final int GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX = 2;
-    public static final int GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX = 3;
-    public static final int GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX = 4;
-    public static final int GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX = 5;
-    public static final int GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX = 6;
+    public static final int VERTICES_ARECORD_LABEL_FIELD_INDEX = 0;
+    public static final int VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX = 1;
+    public static final ARecordType VERTICES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_VERTICES,
+            new String[] { FIELD_NAME_LABEL, FIELD_NAME_DEFINITIONS },
+            new IAType[] { BuiltinType.ASTRING, new AOrderedListType(VERTEX_DEF_RECORDTYPE, null) }, true);
+
+    public static final int EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX = 0;
+    public static final int EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX = 1;
+    public static final int EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX = 2;
+    public static final int EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX = 3;
+    public static final ARecordType EDGE_DEF_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_EDGE_DEF,
+            new String[] { FIELD_NAME_DESTINATION_KEY, FIELD_NAME_SOURCE_KEY, FIELD_NAME_PRIMARY_KEY,
+                    FIELD_NAME_DEFINITION },
+            new IAType[] { new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
+                    AUnionType.createMissableType(
+                            new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null)),
+                    AUnionType.createMissableType(
+                            new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null)),
+                    AUnionType.createMissableType(BuiltinType.ASTRING) },
+            true);
+
+    public static final int EDGES_ARECORD_LABEL_FIELD_INDEX = 0;
+    public static final int EDGES_ARECORD_DEST_LABEL_FIELD_INDEX = 1;
+    public static final int EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX = 2;
+    public static final int EDGES_ARECORD_DEFINITIONS_FIELD_INDEX = 3;
     public static final ARecordType EDGES_RECORDTYPE = MetadataRecordTypes.createRecordType(RECORD_NAME_EDGES,
             new String[] { FIELD_NAME_LABEL, FIELD_NAME_DESTINATION_LABEL, FIELD_NAME_SOURCE_LABEL,
-                    FIELD_NAME_PRIMARY_KEY, FIELD_NAME_DESTINATION_KEY, FIELD_NAME_SOURCE_KEY, FIELD_NAME_DEFINITIONS },
+                    FIELD_NAME_DEFINITIONS },
             new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING, BuiltinType.ASTRING,
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null),
-                    new AOrderedListType(BuiltinType.ASTRING, null) },
+                    new AOrderedListType(EDGE_DEF_RECORDTYPE, null) },
             true);
 
     public static final int GRAPH_ARECORD_DATAVERSENAME_FIELD_INDEX = 0;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
new file mode 100644
index 0000000..ea2624f
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Edge.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata representation of an edge. An edge consists of the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the edge across other graph elements.
+ * 2. A source vertex ({@code Vertex}) reference.
+ * 3. A destination vertex (@code Vertex}) reference.
+ * 4. A collection of edge definitions, which contains either a destination key OR a source key, destination key,
+ *    primary key, and a SQL++ string.
+ */
+public class Edge implements Element {
+    private static final long serialVersionUID = 1L;
+
+    private final GraphElementIdentifier identifier;
+    private final Vertex destinationVertex;
+    private final Vertex sourceVertex;
+    private final List<Definition> definitions;
+
+    // Use {@code Schema.Builder} to build Edge instances instead of this constructor.
+    Edge(GraphElementIdentifier identifier, Vertex destinationVertex, Vertex sourceVertex, Definition edgeDefinition) {
+        this.destinationVertex = Objects.requireNonNull(destinationVertex);
+        this.sourceVertex = Objects.requireNonNull(sourceVertex);
+        this.identifier = Objects.requireNonNull(identifier);
+        this.definitions = new ArrayList<>();
+        this.definitions.add(edgeDefinition);
+    }
+
+    public static class Definition {
+        private final List<List<String>> primaryKeyFieldNames;
+        private final List<List<String>> destinationKeyFieldNames;
+        private final List<List<String>> sourceKeyFieldNames;
+        private final String definition;
+
+        Definition(List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
+                List<List<String>> sourceKeyFieldNames, String definition) {
+            this.primaryKeyFieldNames = primaryKeyFieldNames;
+            this.destinationKeyFieldNames = destinationKeyFieldNames;
+            this.sourceKeyFieldNames = sourceKeyFieldNames;
+            this.definition = definition;
+        }
+
+        Definition(List<List<String>> destinationKeyFieldNames) {
+            this.primaryKeyFieldNames = null;
+            this.destinationKeyFieldNames = destinationKeyFieldNames;
+            this.sourceKeyFieldNames = null;
+            this.definition = null;
+        }
+
+        public List<List<String>> getPrimaryKeyFieldNames() {
+            return primaryKeyFieldNames;
+        }
+
+        public List<List<String>> getDestinationKeyFieldNames() {
+            return destinationKeyFieldNames;
+        }
+
+        public List<List<String>> getSourceKeyFieldNames() {
+            return sourceKeyFieldNames;
+        }
+
+        public String getDefinition() {
+            return definition;
+        }
+
+        public boolean isSimpleDefinition() {
+            return definition == null;
+        }
+    }
+
+    public String getDestinationLabelName() {
+        return destinationVertex.getLabelName();
+    }
+
+    public String getSourceLabelName() {
+        return sourceVertex.getLabelName();
+    }
+
+    public Vertex getDestinationVertex() {
+        return destinationVertex;
+    }
+
+    public Vertex getSourceVertex() {
+        return sourceVertex;
+    }
+
+    public List<Definition> getDefinitions() {
+        return definitions;
+    }
+
+    @Override
+    public GraphElementIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public String getLabelName() {
+        return identifier.getLabelName();
+    }
+
+    @Override
+    public List<String> getDefinitionBodies() {
+        return definitions.stream().map(Definition::getDefinition).collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        String edgeBodyPattern = "[:" + getLabelName() + "]";
+        String sourceNodePattern = "(:" + getSourceLabelName() + ")";
+        String destinationNodePattern = "(:" + getDestinationLabelName() + ")";
+        String edgePattern = sourceNodePattern + "-" + edgeBodyPattern + "->" + destinationNodePattern;
+        return edgePattern + " AS " + getDefinitionBodies().stream().map(s -> (s == null) ? "(no definition)" : s)
+                .collect(Collectors.joining(",\n"));
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
new file mode 100644
index 0000000..1454a9a
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Element.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata interface for a graph element (i.e. edge or vertex). An element has the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the element across other graph elements.
+ * 2. A label name, unique amongst the element classes (e.g. an edge label is unique amongst all graph edges).
+ * 3. A non-empty list of SQL++ strings, each of which represents a definition.
+ */
+public interface Element extends Serializable {
+    GraphElementIdentifier getIdentifier();
+
+    String getLabelName();
+
+    List<String> getDefinitionBodies();
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
index 21afc2d..0f7659f 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Graph.java
@@ -18,53 +18,55 @@
  */
 package org.apache.asterix.graphix.metadata.entities;
 
-import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 
+import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataIndexes;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.IQueryRewriter;
+import org.apache.asterix.lang.common.util.ExpressionUtils;
 import org.apache.asterix.metadata.api.ExtensionMetadataDatasetId;
 import org.apache.asterix.metadata.api.IExtensionMetadataEntity;
+import org.apache.asterix.metadata.entities.DependencyKind;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Triple;
 
 /**
- * Metadata describing a graph view, composed of vertices and edges.
+ * Metadata describing a graph view, composed of a graph {@code Schema} and a collection of dependencies.
  */
 public class Graph implements IExtensionMetadataEntity {
     private static final long serialVersionUID = 1L;
 
-    private final GraphIdentifier identifier;
-    private final GraphDependencies dependencies;
+    private final Dependencies dependencies;
     private final Schema graphSchema;
 
-    public Graph(GraphIdentifier identifier, Schema graphSchema, GraphDependencies dependencies) {
-        this.identifier = Objects.requireNonNull(identifier);
-        this.dependencies = dependencies;
-        this.graphSchema = graphSchema;
+    public Graph(Schema graphSchema, Dependencies dependencies) {
+        this.graphSchema = Objects.requireNonNull(graphSchema);
+        this.dependencies = Objects.requireNonNull(dependencies);
     }
 
     public DataverseName getDataverseName() {
-        return identifier.getDataverseName();
+        return graphSchema.getGraphIdentifier().getDataverseName();
     }
 
     public String getGraphName() {
-        return identifier.getGraphName();
+        return graphSchema.getGraphIdentifier().getGraphName();
     }
 
     public GraphIdentifier getIdentifier() {
-        return identifier;
+        return graphSchema.getGraphIdentifier();
     }
 
     public Schema getGraphSchema() {
         return graphSchema;
     }
 
-    public GraphDependencies getDependencies() {
+    public Dependencies getDependencies() {
         return dependencies;
     }
 
@@ -73,276 +75,44 @@
         return GraphixMetadataIndexes.GRAPH_METADATA_DATASET_EXTENSION_ID;
     }
 
-    public static class Schema implements Serializable {
-        private static final long serialVersionUID = 1L;
+    public static class Dependencies {
+        private final List<Triple<DataverseName, String, String>> datasetDependencies;
+        private final List<Triple<DataverseName, String, String>> synonymDependencies;
+        private final List<Triple<DataverseName, String, String>> functionDependencies;
 
-        // The element map is composed of the vertices and edges.
-        private final Map<GraphElementIdentifier, Element> elementMap = new HashMap<>();
-        private final List<Vertex> vertexList = new ArrayList<>();
-        private final List<Edge> edgeList = new ArrayList<>();
-
-        public List<Vertex> getVertices() {
-            return vertexList;
+        public Dependencies(List<List<Triple<DataverseName, String, String>>> listRepresentation) {
+            datasetDependencies = listRepresentation.get(0);
+            synonymDependencies = listRepresentation.get(1);
+            functionDependencies = listRepresentation.get(2);
         }
 
-        public List<Edge> getEdges() {
-            return edgeList;
+        public Dependencies() {
+            datasetDependencies = new ArrayList<>();
+            synonymDependencies = new ArrayList<>();
+            functionDependencies = new ArrayList<>();
         }
 
-        public Element getElement(GraphElementIdentifier identifier) {
-            return elementMap.get(identifier);
+        public List<List<Triple<DataverseName, String, String>>> getListRepresentation() {
+            return List.of(datasetDependencies, synonymDependencies, functionDependencies);
         }
 
-        private Schema() {
-        }
-
-        public static class Builder {
-            private final Map<String, Vertex> vertexLabelMap = new HashMap<>();
-            private final Map<String, Edge> edgeLabelMap = new HashMap<>();
-
-            // We aim to populate the schema object below.
-            private final Schema workingSchema = new Schema();
-            private final GraphIdentifier graphIdentifier;
-            private Error lastError = Error.NO_ERROR;
-
-            public Builder(GraphIdentifier graphIdentifier) {
-                this.graphIdentifier = graphIdentifier;
+        public Iterator<Pair<DependencyKind, Triple<DataverseName, String, String>>> getIterator() {
+            List<Pair<DependencyKind, Triple<DataverseName, String, String>>> resultant = new ArrayList<>();
+            for (Triple<DataverseName, String, String> datasetDependency : datasetDependencies) {
+                resultant.add(new Pair<>(DependencyKind.DATASET, datasetDependency));
             }
-
-            /**
-             * @return Null if the primary keys of an existing vertex conflict with the vertex to-be-added. The vertex
-             * to-be-added otherwise.
-             */
-            public Vertex addVertex(String labelName, List<List<String>> primaryKeyFieldNames, String definition) {
-                if (!vertexLabelMap.containsKey(labelName)) {
-                    GraphElementIdentifier identifier =
-                            new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.VERTEX, labelName);
-                    Vertex newVertex = new Vertex(identifier, primaryKeyFieldNames, definition);
-                    workingSchema.vertexList.add(newVertex);
-                    vertexLabelMap.put(labelName, newVertex);
-                    return newVertex;
-
-                } else {
-                    Vertex existingVertex = vertexLabelMap.get(labelName);
-                    if (!existingVertex.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
-                        lastError = Error.CONFLICTING_PRIMARY_KEY;
-                        return null;
-                    }
-                    existingVertex.getDefinitions().add(definition);
-                    return existingVertex;
-                }
+            for (Triple<DataverseName, String, String> synonymDependency : synonymDependencies) {
+                resultant.add(new Pair<>(DependencyKind.SYNONYM, synonymDependency));
             }
-
-            /**
-             * @return Null if there exists no vertex with the given source label or destination label, OR if the
-             * primary key / source vertex / destination vertex of an existing edge conflict with the edge to-be-added.
-             */
-            public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
-                    List<List<String>> destinationKeyFieldNames) {
-                if (!vertexLabelMap.containsKey(sourceLabelName)) {
-                    lastError = Error.SOURCE_VERTEX_NOT_FOUND;
-                    return null;
-                }
-
-                Vertex representativeSourceVertex = vertexLabelMap.get(sourceLabelName);
-                return addEdge(edgeLabelName, destinationLabelName, sourceLabelName,
-                        representativeSourceVertex.getPrimaryKeyFieldNames(), destinationKeyFieldNames,
-                        representativeSourceVertex.getPrimaryKeyFieldNames(), "");
+            for (Triple<DataverseName, String, String> functionDependency : functionDependencies) {
+                resultant.add(new Pair<>(DependencyKind.FUNCTION, functionDependency));
             }
-
-            /**
-             * @return Null if there exists no vertex with the given source label or destination label, OR if the
-             * primary key / source vertex / destination vertex of an existing edge conflict with the edge to-be-added.
-             */
-            public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
-                    List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
-                    List<List<String>> sourceKeyFieldNames, String definition) {
-                if (!vertexLabelMap.containsKey(sourceLabelName)) {
-                    lastError = Error.SOURCE_VERTEX_NOT_FOUND;
-                    return null;
-
-                } else if (!vertexLabelMap.containsKey(destinationLabelName)) {
-                    lastError = Error.DESTINATION_VERTEX_NOT_FOUND;
-                    return null;
-
-                } else if (edgeLabelMap.containsKey(edgeLabelName)) {
-                    Edge existingEdge = edgeLabelMap.get(edgeLabelName);
-                    if (!existingEdge.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
-                        lastError = Error.CONFLICTING_PRIMARY_KEY;
-                        return null;
-
-                    } else if (!existingEdge.getSourceLabelName().equals(sourceLabelName)) {
-                        // This also covers any source-key conflicts.
-                        lastError = Error.CONFLICTING_SOURCE_VERTEX;
-                        return null;
-
-                    } else if (!existingEdge.getDestinationLabelName().equals(destinationLabelName)) {
-                        // This also covers any destination-key conflicts.
-                        lastError = Error.CONFLICTING_DESTINATION_VERTEX;
-                        return null;
-                    }
-                    existingEdge.getDefinitions().add(definition);
-                    return existingEdge;
-
-                } else {
-                    GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier,
-                            GraphElementIdentifier.Kind.EDGE, edgeLabelName);
-                    Edge newEdge = new Edge(identifier, primaryKeyFieldNames, destinationKeyFieldNames,
-                            sourceKeyFieldNames, vertexLabelMap.get(destinationLabelName),
-                            vertexLabelMap.get(sourceLabelName), definition);
-                    workingSchema.edgeList.add(newEdge);
-                    edgeLabelMap.put(edgeLabelName, newEdge);
-                    return newEdge;
-                }
-            }
-
-            public Schema build() {
-                // Build the element map, composed of our vertices and edges.
-                workingSchema.elementMap.clear();
-                workingSchema.getVertices().forEach(v -> workingSchema.elementMap.put(v.identifier, v));
-                workingSchema.getEdges().forEach(e -> workingSchema.elementMap.put(e.identifier, e));
-                return workingSchema;
-            }
-
-            public Error getLastError() {
-                return lastError;
-            }
-
-            public enum Error {
-                NO_ERROR,
-
-                CONFLICTING_PRIMARY_KEY,
-                CONFLICTING_SOURCE_VERTEX,
-                CONFLICTING_DESTINATION_VERTEX,
-
-                SOURCE_VERTEX_NOT_FOUND,
-                DESTINATION_VERTEX_NOT_FOUND
-            }
-        }
-    }
-
-    public static final class Vertex implements Element {
-        private static final long serialVersionUID = 1L;
-
-        private final GraphElementIdentifier identifier;
-        private final List<List<String>> primaryKeyFieldNames;
-        private final List<String> definitions;
-
-        private Vertex(GraphElementIdentifier identifier, List<List<String>> primaryKeyFieldNames, String definition) {
-            this.identifier = Objects.requireNonNull(identifier);
-            this.primaryKeyFieldNames = Objects.requireNonNull(primaryKeyFieldNames);
-            this.definitions = new ArrayList<>();
-            this.definitions.add(Objects.requireNonNull(definition));
+            return resultant.listIterator();
         }
 
-        public List<List<String>> getPrimaryKeyFieldNames() {
-            return primaryKeyFieldNames;
+        public void collectDependencies(Expression expr, IQueryRewriter queryRewriter) throws CompilationException {
+            ExpressionUtils.collectDependencies(expr, queryRewriter, datasetDependencies, synonymDependencies,
+                    functionDependencies);
         }
-
-        @Override
-        public GraphElementIdentifier getIdentifier() {
-            return identifier;
-        }
-
-        @Override
-        public String getLabelName() {
-            return identifier.getLabelName();
-        }
-
-        @Override
-        public List<String> getDefinitions() {
-            return definitions;
-        }
-
-        @Override
-        public String toString() {
-            return "(:" + getLabelName() + ") AS " + String.join(",\n", definitions);
-        }
-    }
-
-    public static final class Edge implements Element {
-        private static final long serialVersionUID = 1L;
-
-        private final List<List<String>> primaryKeyFieldNames;
-        private final List<List<String>> destinationKeyFieldNames;
-        private final List<List<String>> sourceKeyFieldNames;
-
-        private final GraphElementIdentifier identifier;
-        private final Vertex destinationVertex;
-        private final Vertex sourceVertex;
-        private final List<String> definitions;
-
-        private Edge(GraphElementIdentifier identifier, List<List<String>> primaryKeyFieldNames,
-                List<List<String>> destinationKeyFieldNames, List<List<String>> sourceKeyFieldNames,
-                Vertex destinationVertex, Vertex sourceVertex, String edgeDefinition) {
-            this.primaryKeyFieldNames = Objects.requireNonNull(primaryKeyFieldNames);
-            this.destinationKeyFieldNames = Objects.requireNonNull(destinationKeyFieldNames);
-            this.sourceKeyFieldNames = Objects.requireNonNull(sourceKeyFieldNames);
-            this.destinationVertex = Objects.requireNonNull(destinationVertex);
-            this.sourceVertex = Objects.requireNonNull(sourceVertex);
-            this.identifier = Objects.requireNonNull(identifier);
-            this.definitions = new ArrayList<>();
-            this.definitions.add(Objects.requireNonNull(edgeDefinition));
-        }
-
-        public String getDestinationLabelName() {
-            return destinationVertex.getLabelName();
-        }
-
-        public String getSourceLabelName() {
-            return sourceVertex.getLabelName();
-        }
-
-        public List<List<String>> getPrimaryKeyFieldNames() {
-            return primaryKeyFieldNames;
-        }
-
-        public List<List<String>> getDestinationKeyFieldNames() {
-            return destinationKeyFieldNames;
-        }
-
-        public List<List<String>> getSourceKeyFieldNames() {
-            return sourceKeyFieldNames;
-        }
-
-        public Vertex getDestinationVertex() {
-            return destinationVertex;
-        }
-
-        public Vertex getSourceVertex() {
-            return sourceVertex;
-        }
-
-        @Override
-        public GraphElementIdentifier getIdentifier() {
-            return identifier;
-        }
-
-        @Override
-        public String getLabelName() {
-            return identifier.getLabelName();
-        }
-
-        @Override
-        public List<String> getDefinitions() {
-            return definitions;
-        }
-
-        @Override
-        public String toString() {
-            String edgeBodyPattern = "[:" + getLabelName() + "]";
-            String sourceNodePattern = "(:" + getSourceLabelName() + ")";
-            String destinationNodePattern = "(:" + getDestinationLabelName() + ")";
-            String edgePattern = sourceNodePattern + "-" + edgeBodyPattern + "->" + destinationNodePattern;
-            return edgePattern + " AS " + String.join(",\n", getDefinitions());
-        }
-    }
-
-    public interface Element extends Serializable {
-        GraphElementIdentifier getIdentifier();
-
-        String getLabelName();
-
-        List<String> getDefinitions();
     }
 }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
deleted file mode 100644
index 0e7aa5a..0000000
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/GraphDependencies.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.apache.asterix.graphix.metadata.entities;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.common.metadata.DataverseName;
-import org.apache.asterix.lang.common.base.Expression;
-import org.apache.asterix.lang.common.base.IQueryRewriter;
-import org.apache.asterix.lang.common.util.ExpressionUtils;
-import org.apache.asterix.metadata.entities.DependencyKind;
-import org.apache.hyracks.algebricks.common.utils.Pair;
-import org.apache.hyracks.algebricks.common.utils.Triple;
-
-/**
- * Helper class to manage dependencies for a graph metadata entity.
- */
-public class GraphDependencies {
-    private final List<Triple<DataverseName, String, String>> datasetDependencies;
-    private final List<Triple<DataverseName, String, String>> synonymDependencies;
-    private final List<Triple<DataverseName, String, String>> functionDependencies;
-
-    public GraphDependencies(List<List<Triple<DataverseName, String, String>>> listRepresentation) {
-        datasetDependencies = listRepresentation.get(0);
-        synonymDependencies = listRepresentation.get(1);
-        functionDependencies = listRepresentation.get(2);
-    }
-
-    public GraphDependencies() {
-        datasetDependencies = new ArrayList<>();
-        synonymDependencies = new ArrayList<>();
-        functionDependencies = new ArrayList<>();
-    }
-
-    public List<List<Triple<DataverseName, String, String>>> getListRepresentation() {
-        return List.of(datasetDependencies, synonymDependencies, functionDependencies);
-    }
-
-    public Iterator<Pair<DependencyKind, Triple<DataverseName, String, String>>> getIterator() {
-        List<Pair<DependencyKind, Triple<DataverseName, String, String>>> resultant = new ArrayList<>();
-        for (Triple<DataverseName, String, String> datasetDependency : datasetDependencies) {
-            resultant.add(new Pair<>(DependencyKind.DATASET, datasetDependency));
-        }
-        for (Triple<DataverseName, String, String> synonymDependency : synonymDependencies) {
-            resultant.add(new Pair<>(DependencyKind.SYNONYM, synonymDependency));
-        }
-        for (Triple<DataverseName, String, String> functionDependency : functionDependencies) {
-            resultant.add(new Pair<>(DependencyKind.FUNCTION, functionDependency));
-        }
-        return resultant.listIterator();
-    }
-
-    public void collectDependencies(Expression expression, IQueryRewriter queryRewriter) throws CompilationException {
-        ExpressionUtils.collectDependencies(expression, queryRewriter, datasetDependencies, synonymDependencies,
-                functionDependencies);
-    }
-}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
new file mode 100644
index 0000000..75c696b
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Schema.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
+
+/**
+ * Metadata representation of a graph schema. A graph schema consists of:
+ * 1. A list of {@code Vertex} instances.
+ * 2. A list of {@code Edge} instances, which link the aforementioned vertices.
+ * 3. A graph identifier ({@code GraphIdentifier}
+ */
+public class Schema implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    // The element map is composed of the vertices and edges.
+    private final List<Vertex> vertexList = new ArrayList<>();
+    private final List<Edge> edgeList = new ArrayList<>();
+    private final GraphIdentifier graphIdentifier;
+
+    public List<Vertex> getVertices() {
+        return vertexList;
+    }
+
+    public List<Edge> getEdges() {
+        return edgeList;
+    }
+
+    public GraphIdentifier getGraphIdentifier() {
+        return graphIdentifier;
+    }
+
+    // Use the {@code Builder} class to create {@code Schema} instances.
+    private Schema(GraphIdentifier graphIdentifier) {
+        this.graphIdentifier = graphIdentifier;
+    }
+
+    public static class Builder {
+        private final Map<String, Vertex> vertexLabelMap = new HashMap<>();
+        private final Map<String, Edge> edgeLabelMap = new HashMap<>();
+
+        // We aim to populate the schema object below.
+        private final Schema workingSchema;
+        private final GraphIdentifier graphIdentifier;
+        private Error lastError = Error.NO_ERROR;
+
+        public Builder(GraphIdentifier graphIdentifier) {
+            this.workingSchema = new Schema(graphIdentifier);
+            this.graphIdentifier = graphIdentifier;
+        }
+
+        /**
+         * @return Null if the primary keys of an existing vertex conflict with the vertex to-be-added. The vertex
+         * to-be-added otherwise.
+         */
+        public Vertex addVertex(String labelName, List<List<String>> primaryKeyFieldNames, String definition) {
+            if (!vertexLabelMap.containsKey(labelName)) {
+                GraphElementIdentifier identifier =
+                        new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.VERTEX, labelName);
+                Vertex newVertex = new Vertex(identifier, new Vertex.Definition(primaryKeyFieldNames, definition));
+                workingSchema.vertexList.add(newVertex);
+                vertexLabelMap.put(labelName, newVertex);
+                return newVertex;
+
+            } else {
+                Vertex existingVertex = vertexLabelMap.get(labelName);
+                if (!existingVertex.getPrimaryKeyFieldNames().equals(primaryKeyFieldNames)) {
+                    lastError = Error.CONFLICTING_PRIMARY_KEY;
+                    return null;
+                }
+                existingVertex.getDefinitions().add(new Vertex.Definition(primaryKeyFieldNames, definition));
+                return existingVertex;
+            }
+        }
+
+        /**
+         * @return Null if there exists no vertex with the given source label or destination label, OR if the
+         * source vertex / destination vertex of an existing edge conflict with the edge to-be-added. Otherwise, the
+         * edge to-be-added.
+         */
+        public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                List<List<String>> destinationKeyFieldNames) {
+            Edge.Definition definition = new Edge.Definition(destinationKeyFieldNames);
+            return addEdge(edgeLabelName, destinationLabelName, sourceLabelName, definition);
+        }
+
+        /**
+         * @return Null if there exists no vertex with the given source label or destination label, OR if the
+         * source vertex / destination vertex of an existing edge conflict with the edge to-be-added. Otherwise, the
+         * edge to-be-added.
+         */
+        public Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                List<List<String>> primaryKeyFieldNames, List<List<String>> destinationKeyFieldNames,
+                List<List<String>> sourceKeyFieldNames, String definitionBody) {
+            Edge.Definition definition = new Edge.Definition(primaryKeyFieldNames, destinationKeyFieldNames,
+                    sourceKeyFieldNames, definitionBody);
+            return addEdge(edgeLabelName, destinationLabelName, sourceLabelName, definition);
+        }
+
+        private Edge addEdge(String edgeLabelName, String destinationLabelName, String sourceLabelName,
+                Edge.Definition definition) {
+            if (!vertexLabelMap.containsKey(sourceLabelName)) {
+                lastError = Error.SOURCE_VERTEX_NOT_FOUND;
+                return null;
+
+            } else if (!vertexLabelMap.containsKey(destinationLabelName)) {
+                lastError = Error.DESTINATION_VERTEX_NOT_FOUND;
+                return null;
+
+            }
+
+            if (edgeLabelMap.containsKey(edgeLabelName)) {
+                Edge existingEdge = edgeLabelMap.get(edgeLabelName);
+                if (!existingEdge.getSourceLabelName().equals(sourceLabelName)) {
+                    // This also covers any source-key conflicts.
+                    lastError = Error.CONFLICTING_SOURCE_VERTEX;
+                    return null;
+
+                } else if (!existingEdge.getDestinationLabelName().equals(destinationLabelName)) {
+                    // This also covers any destination-key conflicts.
+                    lastError = Error.CONFLICTING_DESTINATION_VERTEX;
+                    return null;
+                }
+                existingEdge.getDefinitions().add(definition);
+                return existingEdge;
+
+            } else {
+                GraphElementIdentifier identifier =
+                        new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.EDGE, edgeLabelName);
+                Edge newEdge = new Edge(identifier, vertexLabelMap.get(destinationLabelName),
+                        vertexLabelMap.get(sourceLabelName), definition);
+                workingSchema.edgeList.add(newEdge);
+                edgeLabelMap.put(edgeLabelName, newEdge);
+                return newEdge;
+            }
+        }
+
+        public Schema build() {
+            return workingSchema;
+        }
+
+        public Error getLastError() {
+            return lastError;
+        }
+
+        public enum Error {
+            NO_ERROR,
+
+            CONFLICTING_PRIMARY_KEY,
+            CONFLICTING_SOURCE_VERTEX,
+            CONFLICTING_DESTINATION_VERTEX,
+
+            SOURCE_VERTEX_NOT_FOUND,
+            DESTINATION_VERTEX_NOT_FOUND
+        }
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
new file mode 100644
index 0000000..09c28d4
--- /dev/null
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entities/Vertex.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.asterix.graphix.metadata.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+
+/**
+ * Metadata representation of a vertex. A vertex consists of the following:
+ * 1. A {@code GraphElementIdentifier}, to uniquely identify the vertex across other graph elements.
+ * 2. A collection of vertex definitions, each of which consists of a primary key and a SQL++ string.
+ */
+public class Vertex implements Element {
+    private static final long serialVersionUID = 1L;
+
+    private final GraphElementIdentifier identifier;
+    private final List<Definition> definitions;
+
+    // Use {@code Schema.Builder} to build Vertex instances instead of this constructor.
+    Vertex(GraphElementIdentifier identifier, Definition definition) {
+        this.identifier = Objects.requireNonNull(identifier);
+        this.definitions = new ArrayList<>();
+        this.definitions.add(Objects.requireNonNull(definition));
+    }
+
+    public static class Definition {
+        private final List<List<String>> primaryKeyFieldNames;
+        private final String definition;
+
+        Definition(List<List<String>> primaryKeyFieldNames, String definition) {
+            this.primaryKeyFieldNames = primaryKeyFieldNames;
+            this.definition = definition;
+        }
+
+        public List<List<String>> getPrimaryKeyFieldNames() {
+            return primaryKeyFieldNames;
+        }
+
+        public String getDefinition() {
+            return definition;
+        }
+
+        @Override
+        public String toString() {
+            return definition;
+        }
+    }
+
+    // A primary key is the same across all vertex definitions.
+    public List<List<String>> getPrimaryKeyFieldNames() {
+        return definitions.get(0).getPrimaryKeyFieldNames();
+    }
+
+    public List<Definition> getDefinitions() {
+        return definitions;
+    }
+
+    @Override
+    public GraphElementIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public String getLabelName() {
+        return identifier.getLabelName();
+    }
+
+    @Override
+    public List<String> getDefinitionBodies() {
+        return definitions.stream().map(Definition::getDefinition).collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        return "(:" + getLabelName() + ") AS " + String.join(",\n", getDefinitionBodies());
+    }
+}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
index 9c83184..544c462 100644
--- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
+++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
@@ -31,14 +31,17 @@
 import org.apache.asterix.graphix.common.metadata.GraphIdentifier;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataIndexes;
 import org.apache.asterix.graphix.metadata.bootstrap.GraphixMetadataRecordTypes;
+import org.apache.asterix.graphix.metadata.entities.Edge;
 import org.apache.asterix.graphix.metadata.entities.Graph;
-import org.apache.asterix.graphix.metadata.entities.GraphDependencies;
+import org.apache.asterix.graphix.metadata.entities.Schema;
+import org.apache.asterix.graphix.metadata.entities.Vertex;
 import org.apache.asterix.metadata.entitytupletranslators.AbstractTupleTranslator;
 import org.apache.asterix.om.base.AOrderedList;
 import org.apache.asterix.om.base.ARecord;
 import org.apache.asterix.om.base.AString;
 import org.apache.asterix.om.base.IACursor;
 import org.apache.asterix.om.types.AOrderedListType;
+import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Triple;
@@ -52,9 +55,11 @@
 
     // For constructing our dependency, edge, and vertex lists.
     protected OrderedListBuilder listBuilder;
+    protected OrderedListBuilder defListBuilder;
     protected OrderedListBuilder innerListBuilder;
     protected OrderedListBuilder nameListBuilder;
-    protected IARecordBuilder subRecordBuilder;
+    protected IARecordBuilder elemRecordBuilder;
+    protected IARecordBuilder defRecordBuilder;
     protected AOrderedListType stringListList;
     protected AOrderedListType stringList;
 
@@ -62,9 +67,13 @@
         super(getTuple, GraphixMetadataIndexes.GRAPH_DATASET, GRAPH_PAYLOAD_TUPLE_FIELD_INDEX);
         if (getTuple) {
             listBuilder = new OrderedListBuilder();
+            defListBuilder = new OrderedListBuilder();
             innerListBuilder = new OrderedListBuilder();
             nameListBuilder = new OrderedListBuilder();
-            subRecordBuilder = new RecordBuilder();
+            elemRecordBuilder = new RecordBuilder();
+            defRecordBuilder = new RecordBuilder();
+
+            // Avoid having to create the string list types multiple times.
             stringListList = new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null);
             stringList = new AOrderedListType(BuiltinType.ASTRING, null);
         }
@@ -97,13 +106,12 @@
         }
 
         // Read in the vertex and edge lists.
-        GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName);
-        Graph.Schema graphSchema = readGraphSchema(graphRecord, graphIdentifier);
-        return new Graph(graphIdentifier, graphSchema, new GraphDependencies(graphDependencies));
+        Schema graphSchema = readGraphSchema(graphRecord, new GraphIdentifier(dataverseName, graphName));
+        return new Graph(graphSchema, new Graph.Dependencies(graphDependencies));
     }
 
-    private Graph.Schema readGraphSchema(ARecord graphRecord, GraphIdentifier graphIdentifier) throws AsterixException {
-        Graph.Schema.Builder schemaBuilder = new Graph.Schema.Builder(graphIdentifier);
+    private Schema readGraphSchema(ARecord graphRecord, GraphIdentifier graphIdentifier) throws AsterixException {
+        Schema.Builder schemaBuilder = new Schema.Builder(graphIdentifier);
 
         // Read in the vertex list.
         IACursor verticesCursor = ((AOrderedList) graphRecord
@@ -112,27 +120,33 @@
             ARecord vertex = (ARecord) verticesCursor.get();
 
             // Read in the label name.
-            String labelName = ((AString) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX))
+            String labelName =
+                    ((AString) vertex.getValueByPos(GraphixMetadataRecordTypes.VERTICES_ARECORD_LABEL_FIELD_INDEX))
                             .getStringValue();
 
-            // Read in the primary key fields.
-            List<List<String>> primaryKeyFields = new ArrayList<>();
-            IACursor primaryKeyCursor = ((AOrderedList) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX))
-                            .getCursor();
-            while (primaryKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
-                primaryKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the vertex definition(s). Validate each definition.
+            // Read in our vertex definitions.
             IACursor definitionsCursor = ((AOrderedList) vertex
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX))
-                            .getCursor();
+                    .getValueByPos(GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
             while (definitionsCursor.next()) {
-                String definition = ((AString) definitionsCursor.get()).getStringValue();
-                schemaBuilder.addVertex(labelName, primaryKeyFields, definition);
+                ARecord definition = (ARecord) definitionsCursor.get();
+
+                // Read in the primary key fields.
+                List<List<String>> primaryKeyFields = new ArrayList<>();
+                IACursor primaryKeyCursor = ((AOrderedList) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX))
+                                .getCursor();
+                while (primaryKeyCursor.next()) {
+                    IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
+                    primaryKeyFields.add(readNameList(nameCursor));
+                }
+
+                // Read in the definition body.
+                String definitionBody = ((AString) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX))
+                                .getStringValue();
+
+                // Read in the vertex definition, and perform validation of the metadata record.
+                schemaBuilder.addVertex(labelName, primaryKeyFields, definitionBody);
                 switch (schemaBuilder.getLastError()) {
                     case NO_ERROR:
                         break;
@@ -157,53 +171,70 @@
 
             // Read in the label name.
             String labelName =
-                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX))
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_LABEL_FIELD_INDEX))
                             .getStringValue();
 
             // Read in the destination label name.
-            String destinationLabelName = ((AString) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX))
+            String destinationLabelName =
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_DEST_LABEL_FIELD_INDEX))
                             .getStringValue();
 
             // Read in the source label name.
-            String sourceLabelName = ((AString) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX))
+            String sourceLabelName =
+                    ((AString) edge.getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX))
                             .getStringValue();
 
-            // Read in the primary key fields.
-            List<List<String>> primaryKeyFields = new ArrayList<>();
-            IACursor primaryKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX)).getCursor();
-            while (primaryKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
-                primaryKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the destination key fields.
-            List<List<String>> destinationKeyFields = new ArrayList<>();
-            IACursor destinationKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX)).getCursor();
-            while (destinationKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) destinationKeyCursor.get()).getCursor();
-                destinationKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the source key fields.
-            List<List<String>> sourceKeyFields = new ArrayList<>();
-            IACursor sourceKeyCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX)).getCursor();
-            while (sourceKeyCursor.next()) {
-                IACursor nameCursor = ((AOrderedList) sourceKeyCursor.get()).getCursor();
-                sourceKeyFields.add(readNameList(nameCursor));
-            }
-
-            // Read in the edge definition(s). Validate each definition.
+            // Read in our edge definitions.
             IACursor definitionsCursor = ((AOrderedList) edge
-                    .getValueByPos(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
+                    .getValueByPos(GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX)).getCursor();
             while (definitionsCursor.next()) {
-                String definition = ((AString) definitionsCursor.get()).getStringValue();
-                schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, primaryKeyFields,
-                        destinationKeyFields, sourceKeyFields, definition);
+                ARecord definition = (ARecord) definitionsCursor.get();
+
+                // Read in the destination key fields (this is common to all edge definition records).
+                List<List<String>> destinationKeyFields = new ArrayList<>();
+                IACursor destinationKeyCursor = ((AOrderedList) definition
+                        .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX)).getCursor();
+                while (destinationKeyCursor.next()) {
+                    IACursor nameCursor = ((AOrderedList) destinationKeyCursor.get()).getCursor();
+                    destinationKeyFields.add(readNameList(nameCursor));
+                }
+
+                if (definition.getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX)
+                        .getType().getTypeTag().equals(ATypeTag.MISSING)) {
+                    // We are finished. Read in the edge definition, and perform validation of the metadata record.
+                    schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, destinationKeyFields);
+
+                } else {
+                    // Read in the source key fields.
+                    List<List<String>> sourceKeyFields = new ArrayList<>();
+                    IACursor sourceKeyCursor = ((AOrderedList) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX))
+                                    .getCursor();
+                    while (sourceKeyCursor.next()) {
+                        IACursor nameCursor = ((AOrderedList) sourceKeyCursor.get()).getCursor();
+                        sourceKeyFields.add(readNameList(nameCursor));
+                    }
+
+                    // Read in the primary key fields.
+                    List<List<String>> primaryKeyFields = new ArrayList<>();
+                    IACursor primaryKeyCursor = ((AOrderedList) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX))
+                                    .getCursor();
+                    while (primaryKeyCursor.next()) {
+                        IACursor nameCursor = ((AOrderedList) primaryKeyCursor.get()).getCursor();
+                        primaryKeyFields.add(readNameList(nameCursor));
+                    }
+
+                    // Read in the definition body.
+                    String definitionBody = ((AString) definition
+                            .getValueByPos(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX))
+                                    .getStringValue();
+
+                    // Finally, read in the edge definition and perform validation of the metadata record.
+                    schemaBuilder.addEdge(labelName, destinationLabelName, sourceLabelName, primaryKeyFields,
+                            destinationKeyFields, sourceKeyFields, definitionBody);
+                }
+
                 switch (schemaBuilder.getLastError()) {
                     case NO_ERROR:
                         break;
@@ -293,7 +324,7 @@
         // Write our vertex set.
         listBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.GRAPH_RECORDTYPE
                 .getFieldTypes()[GraphixMetadataRecordTypes.GRAPH_ARECORD_VERTICES_FIELD_INDEX]);
-        for (Graph.Vertex vertex : graph.getGraphSchema().getVertices()) {
+        for (Vertex vertex : graph.getGraphSchema().getVertices()) {
             writeVertexRecord(vertex, itemValue);
             listBuilder.addItem(itemValue);
         }
@@ -304,7 +335,7 @@
         // Write our edge set.
         listBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.GRAPH_RECORDTYPE
                 .getFieldTypes()[GraphixMetadataRecordTypes.GRAPH_ARECORD_EDGES_FIELD_INDEX]);
-        for (Graph.Edge edge : graph.getGraphSchema().getEdges()) {
+        for (Edge edge : graph.getGraphSchema().getEdges()) {
             writeEdgeRecord(edge, itemValue);
             listBuilder.addItem(itemValue);
         }
@@ -319,108 +350,128 @@
         return tuple;
     }
 
-    private void writeVertexRecord(Graph.Vertex vertex, ArrayBackedValueStorage itemValue) throws HyracksDataException {
-        subRecordBuilder.reset(GraphixMetadataRecordTypes.VERTICES_RECORDTYPE);
+    private void writeVertexRecord(Vertex vertex, ArrayBackedValueStorage itemValue) throws HyracksDataException {
+        elemRecordBuilder.reset(GraphixMetadataRecordTypes.VERTICES_RECORDTYPE);
 
         // Write the label name.
         fieldValue.reset();
         aString.setValue(vertex.getLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
-
-        // Write the primary key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : vertex.getPrimaryKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_PRIMARY_KEY_FIELD_INDEX,
-                fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.VERTICES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the vertex definition(s).
-        fieldValue.reset();
-        innerListBuilder.reset(stringList);
-        for (String definition : vertex.getDefinitions()) {
-            itemValue.reset();
-            aString.setValue(definition);
-            stringSerde.serialize(aString, itemValue.getDataOutput());
-            innerListBuilder.addItem(itemValue);
+        defListBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.VERTICES_RECORDTYPE
+                .getFieldTypes()[GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX]);
+        for (Vertex.Definition definition : vertex.getDefinitions()) {
+            defRecordBuilder.reset(GraphixMetadataRecordTypes.VERTEX_DEF_RECORDTYPE);
+
+            // Write the primary key fields.
+            fieldValue.reset();
+            innerListBuilder.reset(new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null));
+            for (List<String> keyField : definition.getPrimaryKeyFieldNames()) {
+                writeNameList(keyField, itemValue);
+                innerListBuilder.addItem(itemValue);
+            }
+            innerListBuilder.write(fieldValue.getDataOutput(), true);
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX,
+                    fieldValue);
+
+            // Write the definition body.
+            fieldValue.reset();
+            aString.setValue(definition.getDefinition());
+            stringSerde.serialize(aString, fieldValue.getDataOutput());
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.VERTEX_DEF_ARECORD_DEFINITION_FIELD_INDEX, fieldValue);
+
+            fieldValue.reset();
+            defRecordBuilder.write(fieldValue.getDataOutput(), true);
+            defListBuilder.addItem(fieldValue);
         }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX,
-                fieldValue);
+        fieldValue.reset();
+        defListBuilder.write(fieldValue.getDataOutput(), true);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.VERTICES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
 
         itemValue.reset();
-        subRecordBuilder.write(itemValue.getDataOutput(), true);
+        elemRecordBuilder.write(itemValue.getDataOutput(), true);
     }
 
-    private void writeEdgeRecord(Graph.Edge edge, ArrayBackedValueStorage itemValue) throws HyracksDataException {
-        subRecordBuilder.reset(GraphixMetadataRecordTypes.EDGES_RECORDTYPE);
+    private void writeEdgeRecord(Edge edge, ArrayBackedValueStorage itemValue) throws HyracksDataException {
+        elemRecordBuilder.reset(GraphixMetadataRecordTypes.EDGES_RECORDTYPE);
 
         // Write the label name.
         fieldValue.reset();
         aString.setValue(edge.getLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the destination label name.
         fieldValue.reset();
         aString.setValue(edge.getDestinationLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_LABEL_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_DEST_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the source label name.
         fieldValue.reset();
         aString.setValue(edge.getSourceLabelName());
         stringSerde.serialize(aString, fieldValue.getDataOutput());
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX, fieldValue);
-
-        // Write the primary key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getPrimaryKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_PRIMARY_KEY_FIELD_INDEX, fieldValue);
-
-        // Write the destination key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getDestinationKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEST_KEY_FIELD_INDEX, fieldValue);
-
-        // Write the source key fields.
-        fieldValue.reset();
-        innerListBuilder.reset(stringListList);
-        for (List<String> keyField : edge.getSourceKeyFieldNames()) {
-            writeNameList(keyField, itemValue);
-            innerListBuilder.addItem(itemValue);
-        }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_SOURCE_KEY_FIELD_INDEX, fieldValue);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_SOURCE_LABEL_FIELD_INDEX, fieldValue);
 
         // Write the edge definition(s).
-        fieldValue.reset();
-        innerListBuilder.reset(stringList);
-        for (String definition : edge.getDefinitions()) {
-            itemValue.reset();
-            aString.setValue(definition);
-            stringSerde.serialize(aString, itemValue.getDataOutput());
-            innerListBuilder.addItem(itemValue);
+        defListBuilder.reset((AOrderedListType) GraphixMetadataRecordTypes.EDGES_RECORDTYPE
+                .getFieldTypes()[GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX]);
+        for (Edge.Definition definition : edge.getDefinitions()) {
+            defRecordBuilder.reset(GraphixMetadataRecordTypes.EDGE_DEF_RECORDTYPE);
+
+            // Write the destination key fields.
+            fieldValue.reset();
+            innerListBuilder.reset(stringListList);
+            for (List<String> keyField : definition.getDestinationKeyFieldNames()) {
+                writeNameList(keyField, itemValue);
+                innerListBuilder.addItem(itemValue);
+            }
+            innerListBuilder.write(fieldValue.getDataOutput(), true);
+            defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEST_KEY_FIELD_INDEX, fieldValue);
+
+            if (!definition.isSimpleDefinition()) {
+                // Write the source key fields.
+                fieldValue.reset();
+                innerListBuilder.reset(stringListList);
+                for (List<String> keyField : definition.getSourceKeyFieldNames()) {
+                    writeNameList(keyField, itemValue);
+                    innerListBuilder.addItem(itemValue);
+                }
+                innerListBuilder.write(fieldValue.getDataOutput(), true);
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_SOURCE_KEY_FIELD_INDEX,
+                        fieldValue);
+
+                // Write the primary key fields.
+                fieldValue.reset();
+                innerListBuilder.reset(stringListList);
+                for (List<String> keyField : definition.getPrimaryKeyFieldNames()) {
+                    writeNameList(keyField, itemValue);
+                    innerListBuilder.addItem(itemValue);
+                }
+                innerListBuilder.write(fieldValue.getDataOutput(), true);
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_PRIMARY_KEY_FIELD_INDEX,
+                        fieldValue);
+
+                // Write the definition body.
+                fieldValue.reset();
+                aString.setValue(definition.getDefinition());
+                stringSerde.serialize(aString, fieldValue.getDataOutput());
+                defRecordBuilder.addField(GraphixMetadataRecordTypes.EDGE_DEF_ARECORD_DEFINITION_FIELD_INDEX,
+                        fieldValue);
+            }
+
+            fieldValue.reset();
+            defRecordBuilder.write(fieldValue.getDataOutput(), true);
+            defListBuilder.addItem(fieldValue);
         }
-        innerListBuilder.write(fieldValue.getDataOutput(), true);
-        subRecordBuilder.addField(GraphixMetadataRecordTypes.GRAPH_EDGES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
+        fieldValue.reset();
+        defListBuilder.write(fieldValue.getDataOutput(), true);
+        elemRecordBuilder.addField(GraphixMetadataRecordTypes.EDGES_ARECORD_DEFINITIONS_FIELD_INDEX, fieldValue);
 
         itemValue.reset();
-        subRecordBuilder.write(itemValue.getDataOutput(), true);
+        elemRecordBuilder.write(itemValue.getDataOutput(), true);
     }
 
     private void writeNameList(List<String> name, ArrayBackedValueStorage itemValue) throws HyracksDataException {
diff --git a/asterix-graphix/src/main/resources/lang-extension/lang.txt b/asterix-graphix/src/main/resources/lang-extension/lang.txt
index 6d215f0..18967a4 100644
--- a/asterix-graphix/src/main/resources/lang-extension/lang.txt
+++ b/asterix-graphix/src/main/resources/lang-extension/lang.txt
@@ -213,11 +213,10 @@
     createNewScope();
   }
   (
-    vertexDefinitionExpr = ViewBody()
-    | <LEFTPAREN> vertexDefinitionExpr = ViewBody() <RIGHTPAREN>
+    vertexDefinitionExpr = ViewBody() { endPos = token; }
+    | <LEFTPAREN> { beginPos = token; } vertexDefinitionExpr = ViewBody() { endPos = token; } <RIGHTPAREN>
   )
   {
-    endPos = token;
     String vDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
     removeCurrentScope();
     GraphConstructor.VertexElement vertexElement = new GraphConstructor.VertexElement(vertexName,
@@ -279,8 +278,8 @@
         createNewScope();
       }
       (
-        edgeDefinitionExpr = ViewBody()
-        | <LEFTPAREN> edgeDefinitionExpr = ViewBody() <RIGHTPAREN>
+        edgeDefinitionExpr = ViewBody() { endPos = token; }
+        | <LEFTPAREN> { beginPos = token; } edgeDefinitionExpr = ViewBody() { endPos = token; } <RIGHTPAREN>
       )
     )
     |
@@ -306,7 +305,6 @@
 
     String eDef = null;
     if (edgeDefinitionExpr != null) {
-      endPos = token;
       eDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
       removeCurrentScope();
     }
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
index 0820e29..7d5380b 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixExecutionTest.java
@@ -26,7 +26,7 @@
 import org.apache.asterix.test.runtime.ExecutionTestUtil;
 import org.apache.asterix.testframework.context.TestCaseContext;
 import org.apache.asterix.testframework.xml.TestGroup;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -36,10 +36,9 @@
 
 @RunWith(Parameterized.class)
 public class GraphixExecutionTest {
-    protected static final String PATH_ACTUAL = "target/rttest" + File.separator;
-    protected static final String PATH_BASE =
-            StringUtils.join(new String[] { "src", "test", "resources", "runtimets" }, File.separator);
-    protected static final String TEST_CONFIG_FILE_NAME = "src/main/resources/cc.conf";
+    protected static final String PATH_ACTUAL = FileUtil.joinPath("target", "rttest");
+    protected static final String PATH_BASE = FileUtil.joinPath("src", "test", "resources", "runtimets");
+    protected static final String FILE_TEST_CONFIG = FileUtil.joinPath("src", "main", "resources", "cc.conf");
     private static final String TEST_SUITE_FILE = "testsuite.xml";
     private static final String ONLY_SUITE_FILE = "only.xml";
 
@@ -57,7 +56,7 @@
     public static void setUp() throws Exception {
         //noinspection ResultOfMethodCallIgnored
         new File(PATH_ACTUAL).mkdirs();
-        ExecutionTestUtil.setUp(true, TEST_CONFIG_FILE_NAME, integrationUtil, false, null);
+        ExecutionTestUtil.setUp(true, FILE_TEST_CONFIG, integrationUtil, false, null);
     }
 
     @AfterClass
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
index e3ea8e9..72485f1 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixIntegrationUtil.java
@@ -16,12 +16,14 @@
 
 import org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;
 import org.apache.hyracks.test.support.TestUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
 public class GraphixIntegrationUtil extends AsterixHyracksIntegrationUtil {
-    private static final String DEFAULT_CONF_FILE = "asterixdb/asterix-opt/asterix-graphix/src/main/resources/cc.conf";
+    private static final String FILE_DEFAULT_CONF =
+            FileUtil.joinPath("asterixdb", "asterix-opt", "asterix-graphix", "src", "main", "resources", "cc.conf");
 
     public static void main(String[] args) {
         TestUtils.redirectLoggingToConsole();
@@ -29,7 +31,7 @@
         try {
             boolean cleanupOnStart = Boolean.getBoolean("cleanup.start");
             boolean cleanupOnShutdown = Boolean.getBoolean("cleanup.shutdown");
-            String confFile = System.getProperty("external.lib", DEFAULT_CONF_FILE);
+            String confFile = System.getProperty("external.lib", FILE_DEFAULT_CONF);
             graphixIntegrationUtil.run(cleanupOnStart, cleanupOnShutdown, confFile);
         } catch (Exception e) {
             e.printStackTrace();
diff --git a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
index bf2c756..70331b1 100644
--- a/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
+++ b/asterix-graphix/src/test/java/org/apache/asterix/graphix/test/GraphixMetadataTest.java
@@ -22,7 +22,7 @@
 
 import org.apache.asterix.test.common.TestExecutor;
 import org.apache.asterix.testframework.context.TestCaseContext;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.hyracks.util.file.FileUtil;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -32,10 +32,9 @@
 
 @RunWith(Parameterized.class)
 public class GraphixMetadataTest {
-    private static final String PATH_ACTUAL = "target" + File.separator + "mdtest" + File.separator;
-    private static final String PATH_BASE =
-            StringUtils.join(new String[] { "src", "test", "resources", "metadata" + File.separator }, File.separator);
-    private static final String TEST_CONFIG_FILE_NAME = "src/main/resources/cc.conf";
+    private static final String PATH_ACTUAL = FileUtil.joinPath("target", "mdtest");
+    private static final String PATH_BASE = FileUtil.joinPath("src", "test", "resources", "metadata");
+    private static final String FILE_TEST_CONFIG = FileUtil.joinPath("src", "main", "resources", "cc.conf");
 
     private static final TestExecutor testExecutor = new TestExecutor();
     private static final GraphixIntegrationUtil integrationUtil = new GraphixIntegrationUtil();
@@ -50,7 +49,7 @@
     public static void setUp() throws Exception {
         //noinspection ResultOfMethodCallIgnored
         new File(PATH_ACTUAL).mkdirs();
-        integrationUtil.init(true, TEST_CONFIG_FILE_NAME);
+        integrationUtil.init(true, FILE_TEST_CONFIG);
     }
 
     @AfterClass
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
index 31c330c..35bc5e2 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.1.adm
@@ -1,2 +1,2 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_1", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "PrimaryKey": [ [ "user_id" ] ], "Definitions": [ "Yelp_B.Yelpers" ] }, { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "Definitions": [ "( FROM    Yelp_B.Users U\n                       UNNEST  U.friends F\n                       SELECT  U.user_id, F AS friend )", "Yelp_B.Friends" ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "user_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
\ No newline at end of file
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_1", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "Definitions": [ { "PrimaryKey": [ [ "user_id" ] ], "Definition": "Yelp_B.Yelpers" } ] }, { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "Definitions": [ { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "FROM    Yelp_B.Users U\n                       UNNEST  U.friends F\n                       SELECT  U.user_id, F AS friend" }, { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "Yelp_B.Friends" } ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "user_id" ] ] } ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
index a749b72..90ebd8b 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.2.adm
@@ -1 +1 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ] ], [  ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                       SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                       SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }
diff --git a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
index 80af6cb..b723764 100644
--- a/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
+++ b/asterix-graphix/src/test/resources/metadata/results/graphix/yelp-example/yelp-example.3.adm
@@ -1 +1 @@
-{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "PrimaryKey": [ [ "user_id" ] ], "Definitions": [ "Yelp_B.Yelpers" ] }, { "Label": "Review", "PrimaryKey": [ [ "review_id" ] ], "Definitions": [ "( FROM    Yelp_A.Reviews R\n                               SELECT  VALUE R )" ] }, { "Label": "Business", "PrimaryKey": [ [ "business_id" ] ], "Definitions": [ "( FROM    Yelp_A.RelevantBusinesses() B\n                               SELECT  VALUE B )" ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "Definitions": [ "( FROM    Yelp_B.Users U\n                               UNNEST  U.friends F\n                               SELECT  U.user_id, F AS friend )", "Yelp_B.Friends" ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "user_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "PrimaryKey": [ [ "review_id" ] ], "DestinationKey": [ [ "business_id" ] ], "SourceKey": [ [ "review_id" ] ], "Definitions": [ "" ] } ] }
+{ "DataverseName": "Yelp", "GraphName": "YelpGraph_2", "Dependencies": [ [ [ "Yelp_A", "Reviews" ], [ "Yelp_B", "Users" ], [ "Yelp_B", "Friends" ] ], [ [ "Yelp_B", "Yelpers" ] ], [ [ "Yelp_A", "RelevantBusinesses", "0" ] ] ], "Vertices": [ { "Label": "User", "Definitions": [ { "PrimaryKey": [ [ "user_id" ] ], "Definition": "Yelp_B.Yelpers" } ] }, { "Label": "Review", "Definitions": [ { "PrimaryKey": [ [ "review_id" ] ], "Definition": "FROM    Yelp_A.Reviews R\n                               SELECT  VALUE R" } ] }, { "Label": "Business", "Definitions": [ { "PrimaryKey": [ [ "business_id" ] ], "Definition": "FROM    Yelp_A.RelevantBusinesses() B\n                               SELECT  VALUE B" } ] } ], "Edges": [ { "Label": "FRIENDS_WITH", "DestinationLabel": "User", "SourceLabel": "User", "Definitions": [ { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "FROM    Yelp_B.Users U\n                               UNNEST  U.friends F\n                               SELECT  U.user_id, F AS friend" }, { "DestinationKey": [ [ "friend" ] ], "SourceKey": [ [ "user_id" ] ], "PrimaryKey": [ [ "user_id" ], [ "friend" ] ], "Definition": "Yelp_B.Friends" } ] }, { "Label": "MADE_BY", "DestinationLabel": "User", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "user_id" ] ] } ] }, { "Label": "ABOUT", "DestinationLabel": "Business", "SourceLabel": "Review", "Definitions": [ { "DestinationKey": [ [ "business_id" ] ] } ] } ] }

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/14764
To unsubscribe, or for help writing mail filters, visit https://asterix-gerrit.ics.uci.edu/settings

Gerrit-Project: asterixdb-graph
Gerrit-Branch: master
Gerrit-Change-Id: I1686875b004451f3d7b4f1f45f1a854ccb5e8a04
Gerrit-Change-Number: 14764
Gerrit-PatchSet: 1
Gerrit-Owner: Glenn Galvizo <gg...@uci.edu>
Gerrit-MessageType: newchange