Inheritance: MatchEdge
        /// <summary>
        /// Constructs the graph pattern specified by the MATCH clause. 
        /// The graph pattern may consist of multiple fully-connected sub-graphs.
        /// </summary>
        /// <param name="query">The SELECT query block</param>
        /// <returns>A graph object contains all the connected componeents</returns>
        private MatchGraph ConstructGraph(WSelectQueryBlock query)
        {
            if (query == null || query.MatchClause == null)
                return null;

            var columnsOfNodeTables = _graphMetaData.ColumnsOfNodeTables;
            var nodeViewMapping = _graphMetaData.NodeViewMapping;
            var unionFind = new UnionFind();
            var edgeColumnToAliasesDict = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
            var pathDictionary = new Dictionary<string, MatchPath>(StringComparer.OrdinalIgnoreCase);
            var reversedEdgeDict = new Dictionary<string, MatchEdge>();
            var matchClause = query.MatchClause;
            var nodes = new Dictionary<string, MatchNode>(StringComparer.OrdinalIgnoreCase);
            var connectedSubGraphs = new List<ConnectedComponent>();
            var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.OrdinalIgnoreCase);
            var parent = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            unionFind.Parent = parent;

            // Constructs the graph pattern specified by the path expressions in the MATCH clause
            foreach (var path in matchClause.Paths)
            {
                var index = 0;
                MatchEdge preEdge = null;
                for (var count = path.PathEdgeList.Count; index < count; ++index)
                {
                    var currentNodeTableRef = path.PathEdgeList[index].Item1;
                    var currentEdgeColumnRef = path.PathEdgeList[index].Item2;
                    var currentNodeExposedName = currentNodeTableRef.BaseIdentifier.Value;
                    var nextNodeTableRef = index != count - 1
                        ? path.PathEdgeList[index + 1].Item1
                        : path.Tail;
                    var nextNodeExposedName = nextNodeTableRef.BaseIdentifier.Value;
                    var patternNode = nodes.GetOrCreate(currentNodeExposedName);
                    if (patternNode.NodeAlias == null)
                    {
                        patternNode.NodeAlias = currentNodeExposedName;
                        patternNode.Neighbors = new List<MatchEdge>();
                        patternNode.External = false;
                        var nodeTable = _context[currentNodeExposedName] as WNamedTableReference;
                        if (nodeTable != null)
                        {
                            patternNode.NodeTableObjectName = nodeTable.TableObjectName;
                            if (patternNode.NodeTableObjectName.SchemaIdentifier == null)
                                patternNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"});
                        }
                    }

                    Identifier edgeIdentifier = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last();
                    string schema = patternNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower();
                    string nodeTableName = patternNode.NodeTableObjectName.BaseIdentifier.Value;
                    string bindTableName =
                        _context.EdgeNodeBinding[
                            new Tuple<string, string>(nodeTableName.ToLower(), edgeIdentifier.Value.ToLower())].ToLower();
                    var bindNodeTableObjName = new WSchemaObjectName(
                        new Identifier {Value = schema},
                        new Identifier {Value = bindTableName}
                        );
                    var edgeColumn =
                        columnsOfNodeTables[
                            WNamedTableReference.SchemaNameToTuple(bindNodeTableObjName)][
                                currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value];
                    string edgeAlias = currentEdgeColumnRef.Alias;
                    //string revEdgeAlias = edgeAlias;
                    bool isReversed = path.IsReversed || edgeColumn.EdgeInfo.IsReversedEdge;
                    string currentRevEdgeName = null;

                    // get original edge name
                    var currentEdgeName = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value;
                    var originalSourceName = isReversed ?
                        (_context[nextNodeExposedName] as WNamedTableReference).TableObjectName.BaseIdentifier.Value
                        : (_context[currentNodeExposedName] as WNamedTableReference).TableObjectName.BaseIdentifier.Value;

                    if (isReversed)
                    {
                        var i = currentEdgeName.IndexOf(originalSourceName, StringComparison.OrdinalIgnoreCase) +
                            originalSourceName.Length;
                        currentRevEdgeName = currentEdgeName.Substring(i + 1,
                            currentEdgeName.Length - "Reversed".Length - i - 1);
                    }
                    else
                    {
                        var srcTuple = WNamedTableReference.SchemaNameToTuple(patternNode.NodeTableObjectName);
                        // [nodeView]-[edge]->[node/nodeView]
                        if (edgeColumn.Role == WNodeTableColumnRole.Edge && nodeViewMapping.ContainsKey(srcTuple))
                        {
                            var physicalNodeName =
                                _context.EdgeNodeBinding[new Tuple<string, string>(srcTuple.Item2, currentEdgeName.ToLower())];
                            currentRevEdgeName = physicalNodeName + "_" + currentEdgeName + "Reversed";
                        }
                        else
                        {
                            currentRevEdgeName = originalSourceName + "_" + currentEdgeName + "Reversed";
                        }
                    }

                    if (edgeAlias == null)
                    {
                        edgeAlias = !isReversed
                            ? string.Format("{0}_{1}_{2}", currentNodeExposedName, currentEdgeName, nextNodeExposedName)
                            : string.Format("{0}_{1}_{2}", nextNodeExposedName, currentRevEdgeName, currentNodeExposedName);

                        //when the current edge is a reversed edge, the key should still be the original edge name
                        //e.g.: TestDeleteEdgeWithTableAlias
                        var edgeNameKey = isReversed ? currentRevEdgeName : currentEdgeName;
                        if (edgeColumnToAliasesDict.ContainsKey(edgeNameKey))
                        {
                            edgeColumnToAliasesDict[edgeNameKey].Add(edgeAlias);
                        }
                        else
                        {
                            edgeColumnToAliasesDict.Add(edgeNameKey, new List<string> { edgeAlias });
                        }
                    }

                    MatchEdge edge, revEdge;
                    if (currentEdgeColumnRef.MinLength == 1 && currentEdgeColumnRef.MaxLength == 1)
                    {
                        var isEdgeView = edgeColumn.Role == WNodeTableColumnRole.EdgeView;
                        var hasRevEdge = edgeColumn.EdgeInfo.HasReversedEdge;
                        edge = new MatchEdge
                        {
                            IsEdgeView = isEdgeView,
                            IsReversedEdge = false,
                            HasReversedEdge = hasRevEdge,
                            SourceNode = patternNode,
                            EdgeColumn = currentEdgeColumnRef,
                            EdgeAlias = edgeAlias,
                            BindNodeTableObjName = bindNodeTableObjName,
                        };
                        _context.AddEdgeReference(edge);

                        if (hasRevEdge)
                        {
                            revEdge = new MatchEdge
                            {
                                IsEdgeView = isEdgeView,
                                IsReversedEdge = true,
                                SinkNode = patternNode,
                                EdgeColumn = new WEdgeColumnReferenceExpression()
                                {
                                    ColumnType = currentEdgeColumnRef.ColumnType,
                                    Alias = currentEdgeColumnRef.Alias,
                                    MaxLength = currentEdgeColumnRef.MaxLength,
                                    MinLength = currentEdgeColumnRef.MinLength,
                                    AttributeValueDict = currentEdgeColumnRef.AttributeValueDict,
                                    MultiPartIdentifier = new WMultiPartIdentifier(new Identifier()
                                    {
                                        QuoteType = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().QuoteType,
                                        Value = currentRevEdgeName,
                                    }),
                                },
                                EdgeAlias = edgeAlias,
                                //EdgeAlias = revEdgeAlias,
                            };
                            reversedEdgeDict[edge.EdgeAlias] = revEdge;
                        }
                    }
                    else
                    {
                        MatchPath matchPath = new MatchPath
                        {
                            SourceNode = patternNode,
                            EdgeColumn = currentEdgeColumnRef,
                            EdgeAlias = edgeAlias,
                            BindNodeTableObjName =
                                new WSchemaObjectName(
                                    new Identifier {Value = schema},
                                    new Identifier {Value = bindTableName}
                                    ),
                            MinLength = currentEdgeColumnRef.MinLength,
                            MaxLength = currentEdgeColumnRef.MaxLength,
                            ReferencePathInfo = false,
                            AttributeValueDict = currentEdgeColumnRef.AttributeValueDict
                        };
                        _context.AddEdgeReference(matchPath);
                        pathDictionary[edgeAlias] = matchPath;
                        edge = matchPath;
                    }

                    if (preEdge != null)
                    {
                        preEdge.SinkNode = patternNode;
                        if (preEdge.HasReversedEdge)
                        {
                            //var preSourceNode = preEdge.SourceNode;
                            var preEdgeBindNodeTableObjName = preEdge.BindNodeTableObjName;
                            var preEdgeColName = preEdge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value;
                            var preEdgeColumn =
                                columnsOfNodeTables[
                                    WNamedTableReference.SchemaNameToTuple(preEdgeBindNodeTableObjName)][
                                        preEdgeColName];
                            var isEdgeView = preEdgeColumn.Role == WNodeTableColumnRole.EdgeView;
                            var isNodeView =
                                _graphMetaData.NodeViewMapping.ContainsKey(
                                    WNamedTableReference.SchemaNameToTuple(patternNode.NodeTableObjectName));

                            reversedEdgeDict[preEdge.EdgeAlias].SourceNode = patternNode;
                            // [node/nodeView]-[edgeView]->[node/nodeView]
                            if (isEdgeView)
                            {
                                reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = preEdgeBindNodeTableObjName;
                            }
                            // [node/nodeView]-[edge]->[nodeView]
                            else if (!isEdgeView && isNodeView)
                            {
                                reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName(
                                    new Identifier { Value = "dbo" },
                                    new Identifier { Value = preEdgeColumn.EdgeInfo.SinkNodes.First() }
                                    );
                            }
                            else
                            {
                                reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName(
                                    new Identifier { Value = schema },
                                    new Identifier { Value = bindTableName }
                                    );
                            }
                        }
                    }
                    preEdge = edge;

                    if (!parent.ContainsKey(currentNodeExposedName))
                        parent[currentNodeExposedName] = currentNodeExposedName;
                    if (!parent.ContainsKey(nextNodeExposedName))
                        parent[nextNodeExposedName] = nextNodeExposedName;

                    unionFind.Union(currentNodeExposedName, nextNodeExposedName);

                    patternNode.Neighbors.Add(edge);

                }
                var tailExposedName = path.Tail.BaseIdentifier.Value;
                var tailNode = nodes.GetOrCreate(tailExposedName);
                if (tailNode.NodeAlias == null)
                {
                    tailNode.NodeAlias = tailExposedName;
                    tailNode.Neighbors = new List<MatchEdge>();
                    var nodeTable = _context[tailExposedName] as WNamedTableReference;
                    if (nodeTable != null)
                    {
                        tailNode.NodeTableObjectName = nodeTable.TableObjectName;
                        if (tailNode.NodeTableObjectName.SchemaIdentifier == null)
                            tailNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"});
                    }
                }
                if (preEdge != null)
                {
                    preEdge.SinkNode = tailNode;
                    if (preEdge.HasReversedEdge)
                    {
                        var schema = tailNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower();
                        var nodeTableName = tailNode.NodeTableObjectName.BaseIdentifier.Value;
                        //var preSourceNode = preEdge.SourceNode;
                        var preEdgeBindNodeTableObjName = preEdge.BindNodeTableObjName;
                        var preEdgeColName = preEdge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value;
                        var preEdgeColumn =
                            columnsOfNodeTables[
                                WNamedTableReference.SchemaNameToTuple(preEdgeBindNodeTableObjName)][
                                    preEdgeColName];
                        var isEdgeView = preEdgeColumn.Role == WNodeTableColumnRole.EdgeView;
                        var isNodeView =
                            _graphMetaData.NodeViewMapping.ContainsKey(
                                WNamedTableReference.SchemaNameToTuple(tailNode.NodeTableObjectName));
                        reversedEdgeDict[preEdge.EdgeAlias].SourceNode = tailNode;
                        // [node/nodeView]-[edgeView]->[node/nodeView]
                        if (isEdgeView)
                        {
                            reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = preEdgeBindNodeTableObjName;
                        }
                        // [node/nodeView]-[edge]->[nodeView]
                        else if (!isEdgeView && isNodeView)
                        {
                            reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName(
                                new Identifier { Value = "dbo" },
                                new Identifier { Value = preEdgeColumn.EdgeInfo.SinkNodes.First() }
                                );
                        }
                        else
                        {
                            reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName(
                                new Identifier { Value = schema },
                                new Identifier { Value = nodeTableName.ToLower() }
                                );
                        }
                    }
                }
            }

            // Puts nodes into subgraphs
            foreach (var node in nodes)
            {
                string root = unionFind.Find(node.Key);
                if (!subGrpahMap.ContainsKey(root))
                {
                    var subGraph = new ConnectedComponent();
                    subGraph.Nodes[node.Key] = node.Value;
                    foreach (var edge in node.Value.Neighbors)
                    {
                        subGraph.Edges[edge.EdgeAlias] = edge;
                    }
                    subGrpahMap[root] = subGraph;
                    connectedSubGraphs.Add(subGraph);
                    subGraph.IsTailNode[node.Value] = false;
                }
                else
                {
                    var subGraph = subGrpahMap[root];
                    subGraph.Nodes[node.Key] = node.Value;
                    foreach (var edge in node.Value.Neighbors)
                    {
                        subGraph.Edges[edge.EdgeAlias] = edge;
                    }
                    subGraph.IsTailNode[node.Value] = false;
                }
            }

            var graph = new MatchGraph
            {
                ReversedEdgeDict = reversedEdgeDict,
                ConnectedSubGraphs = connectedSubGraphs,
                SourceNodeStatisticsDict = new Dictionary<Tuple<string, bool>, Statistics>(),
            };
            unionFind.Parent = null;

            // When an edge in the MATCH clause is not associated with an alias,
            // assigns to it a default alias: sourceAlias_EdgeColumnName_sinkAlias.
            // Also rewrites edge attributes anywhere in the query that can be bound to this default alias.
            var replaceTableRefVisitor = new ReplaceEdgeReferenceVisitor();
            replaceTableRefVisitor.Invoke(query, edgeColumnToAliasesDict);

            // Rematerializes node tables in the MATCH clause which are defined in the upper-level context
            // and join them with the upper-level table references.
            RematerilizeExtrenalNodeTableReference(query, nodes);

            // Transforms the path reference in the SELECT elements into a
            // scalar function to display path information.
            TransformPathInfoDisplaySelectElement(query, pathDictionary);

            return graph;
        }
        // Greate the MatchGraph of this AggregationBlock. If some free nodes and free edges are connected, they are in the same ConnectedComponent
        internal HashSet <string> CreateMatchGraph(WMatchClause matchClause)
        {
            HashSet <string> freeNodesAndEdges = new HashSet <string>();
            Dictionary <string, MatchPath>          pathCollection     = new Dictionary <string, MatchPath>(StringComparer.OrdinalIgnoreCase);
            Dictionary <string, MatchNode>          nodeCollection     = new Dictionary <string, MatchNode>(StringComparer.OrdinalIgnoreCase);
            Dictionary <string, MatchEdge>          edgeCollection     = new Dictionary <string, MatchEdge>(StringComparer.OrdinalIgnoreCase);
            Dictionary <string, ConnectedComponent> subgraphCollection = new Dictionary <string, ConnectedComponent>(StringComparer.OrdinalIgnoreCase);

            // we use Disjoint-set data structure to determine whether tables are in the same component or not.
            UnionFind unionFind = new UnionFind();

            foreach (ConnectedComponent subgraph in this.GraphPattern.ConnectedSubgraphs)
            {
                foreach (KeyValuePair <string, MatchNode> pair in subgraph.Nodes)
                {
                    nodeCollection.Add(pair.Key, pair.Value);
                    unionFind.Add(pair.Key);
                }
            }

            if (matchClause != null)
            {
                foreach (WMatchPath path in matchClause.Paths)
                {
                    int       index         = 0;
                    bool      outOfBlock    = false;
                    MatchEdge edgeToSrcNode = null;

                    for (int count = path.PathEdgeList.Count; index < count; ++index)
                    {
                        WSchemaObjectName currentNodeTableRef = path.PathEdgeList[index].Item1;
                        WEdgeColumnReferenceExpression currentEdgeColumnRef = path.PathEdgeList[index].Item2;
                        WSchemaObjectName nextNodeTableRef = index != count - 1
                            ? path.PathEdgeList[index + 1].Item1
                            : path.Tail;
                        string currentNodeExposedName = currentNodeTableRef.BaseIdentifier.Value;
                        string edgeAlias           = currentEdgeColumnRef.Alias;
                        string nextNodeExposedName = nextNodeTableRef != null ? nextNodeTableRef.BaseIdentifier.Value : null;

                        // Get the source node of a path
                        if (!nodeCollection.ContainsKey(currentNodeExposedName))
                        {
                            continue;
                        }
                        MatchNode srcNode = nodeCollection[currentNodeExposedName];

                        // Get the edge of a path, and set required attributes
                        // Because the sourceNode is relative, we need to construct new edges or paths
                        // But they need to share the same predicates and proerties
                        MatchEdge edgeFromSrcNode;
                        if (currentEdgeColumnRef.MinLength == 1 && currentEdgeColumnRef.MaxLength == 1)
                        {
                            if (!edgeCollection.ContainsKey(edgeAlias))
                            {
                                edgeCollection[edgeAlias] = new MatchEdge()
                                {
                                    LinkAlias            = edgeAlias,
                                    SourceNode           = srcNode,
                                    EdgeType             = currentEdgeColumnRef.EdgeType,
                                    Predicates           = new List <WBooleanExpression>(),
                                    BindNodeTableObjName = new WSchemaObjectName(),
                                    IsReversed           = false,
                                    Properties           = new List <string>(GraphViewReservedProperties.ReservedEdgeProperties)
                                };
                                unionFind.Add(edgeAlias);
                            }

                            edgeFromSrcNode = new MatchEdge
                            {
                                LinkAlias            = edgeAlias,
                                SourceNode           = srcNode,
                                EdgeType             = edgeCollection[edgeAlias].EdgeType,
                                Predicates           = edgeCollection[edgeAlias].Predicates,
                                BindNodeTableObjName = edgeCollection[edgeAlias].BindNodeTableObjName,
                                IsReversed           = false,
                                Properties           = edgeCollection[edgeAlias].Properties
                            };
                        }
                        else
                        {
                            if (!pathCollection.ContainsKey(edgeAlias))
                            {
                                pathCollection[edgeAlias] = new MatchPath
                                {
                                    SourceNode           = srcNode,
                                    LinkAlias            = edgeAlias,
                                    Predicates           = new List <WBooleanExpression>(),
                                    BindNodeTableObjName = new WSchemaObjectName(),
                                    MinLength            = currentEdgeColumnRef.MinLength,
                                    MaxLength            = currentEdgeColumnRef.MaxLength,
                                    ReferencePathInfo    = false,
                                    AttributeValueDict   = currentEdgeColumnRef.AttributeValueDict,
                                    IsReversed           = false,
                                    EdgeType             = currentEdgeColumnRef.EdgeType,
                                    Properties           = new List <string>(GraphViewReservedProperties.ReservedEdgeProperties)
                                };
                            }

                            edgeFromSrcNode = new MatchPath
                            {
                                SourceNode           = srcNode,
                                LinkAlias            = edgeAlias,
                                Predicates           = pathCollection[edgeAlias].Predicates,
                                BindNodeTableObjName = pathCollection[edgeAlias].BindNodeTableObjName,
                                MinLength            = pathCollection[edgeAlias].MinLength,
                                MaxLength            = pathCollection[edgeAlias].MaxLength,
                                ReferencePathInfo    = false,
                                AttributeValueDict   = pathCollection[edgeAlias].AttributeValueDict,
                                IsReversed           = false,
                                EdgeType             = pathCollection[edgeAlias].EdgeType,
                                Properties           = pathCollection[edgeAlias].Properties
                            };
                        }

                        if (path.IsReversed)
                        {
                            unionFind.Union(edgeAlias, currentNodeExposedName);
                        }
                        else
                        {
                            unionFind.Union(currentNodeExposedName, edgeAlias);
                        }

                        if (edgeToSrcNode != null)
                        {
                            edgeToSrcNode.SinkNode = srcNode;

                            if (!(edgeToSrcNode is MatchPath))
                            {
                                // Construct reverse edge
                                MatchEdge reverseEdge = new MatchEdge
                                {
                                    SourceNode           = edgeToSrcNode.SinkNode,
                                    SinkNode             = edgeToSrcNode.SourceNode,
                                    LinkAlias            = edgeToSrcNode.LinkAlias,
                                    Predicates           = edgeToSrcNode.Predicates,
                                    BindNodeTableObjName = edgeToSrcNode.BindNodeTableObjName,
                                    IsReversed           = true,
                                    EdgeType             = edgeToSrcNode.EdgeType,
                                    Properties           = edgeToSrcNode.Properties,
                                };
                                srcNode.ReverseNeighbors.Add(reverseEdge);
                            }
                        }

                        edgeToSrcNode = edgeFromSrcNode;

                        // Add this edge to node's neightbors
                        if (nextNodeExposedName != null)
                        {
                            if (path.IsReversed)
                            {
                                unionFind.Union(nextNodeExposedName, edgeAlias);
                            }
                            else
                            {
                                unionFind.Union(edgeAlias, nextNodeExposedName);
                            }

                            srcNode.Neighbors.Add(edgeFromSrcNode);
                        }
                        // Add this edge to node's dangling edges
                        else
                        {
                            srcNode.DanglingEdges.Add(edgeFromSrcNode);
                        }
                    }

                    if (path.Tail == null)
                    {
                        continue;
                    }

                    // Get destination node of a path
                    string tailExposedName = path.Tail.BaseIdentifier.Value;

                    if (!nodeCollection.ContainsKey(tailExposedName))
                    {
                        continue;
                    }

                    MatchNode destNode = nodeCollection[tailExposedName];

                    if (edgeToSrcNode != null)
                    {
                        edgeToSrcNode.SinkNode = destNode;
                        if (!(edgeToSrcNode is MatchPath))
                        {
                            // Construct reverse edge
                            MatchEdge reverseEdge = new MatchEdge
                            {
                                SourceNode           = edgeToSrcNode.SinkNode,
                                SinkNode             = edgeToSrcNode.SourceNode,
                                LinkAlias            = edgeToSrcNode.LinkAlias,
                                Predicates           = edgeToSrcNode.Predicates,
                                BindNodeTableObjName = edgeToSrcNode.BindNodeTableObjName,
                                IsReversed           = true,
                                EdgeType             = edgeToSrcNode.EdgeType,
                                Properties           = edgeToSrcNode.Properties,
                            };
                            destNode.ReverseNeighbors.Add(reverseEdge);
                        }
                    }
                }
            }

            // Use union find algorithmn to define which subgraph does a node belong to and put it into where it belongs to.
            foreach (var node in nodeCollection)
            {
                freeNodesAndEdges.Add(node.Key);
                string root = unionFind.Find(node.Key);

                ConnectedComponent subGraph;
                if (!subgraphCollection.ContainsKey(root))
                {
                    subGraph = new ConnectedComponent();
                    subgraphCollection[root] = subGraph;
                }
                else
                {
                    subGraph = subgraphCollection[root];
                }

                subGraph.Nodes[node.Key] = node.Value;

                subGraph.IsTailNode[node.Value] = false;

                foreach (MatchEdge edge in node.Value.Neighbors)
                {
                    subGraph.Edges[edge.LinkAlias] = edge;
                    freeNodesAndEdges.Add(edge.LinkAlias);
                }

                foreach (MatchEdge edge in node.Value.DanglingEdges)
                {
                    subGraph.Edges[edge.LinkAlias] = edge;
                    edge.IsDanglingEdge            = true;
                    freeNodesAndEdges.Add(edge.LinkAlias);
                }

                if (node.Value.Neighbors.Count + node.Value.ReverseNeighbors.Count + node.Value.DanglingEdges.Count > 0)
                {
                    node.Value.Properties.Add(GremlinKeyword.Star);
                }
            }

            this.GraphPattern = new MatchGraph(subgraphCollection.Values.ToList());

            return(freeNodesAndEdges);
        }
        /// <summary>
        /// Constructs the graph pattern specified by the MATCH clause. 
        /// The graph pattern may consist of multiple fully-connected sub-graphs.
        /// </summary>
        /// <param name="query">The SELECT query block</param>
        /// <returns>A graph object contains all the connected componeents</returns>
        private MatchGraph ConstructGraph(WSelectQueryBlock query)
        {
            if (query == null || query.MatchClause == null)
                return null;

            var unionFind = new UnionFind();
            var edgeColumnToAliasesDict = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
            var pathDictionary = new Dictionary<string, MatchPath>(StringComparer.OrdinalIgnoreCase);
            var matchClause = query.MatchClause;
            var nodes = new Dictionary<string, MatchNode>(StringComparer.OrdinalIgnoreCase);
            var connectedSubGraphs = new List<ConnectedComponent>();
            var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.OrdinalIgnoreCase);
            var parent = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            unionFind.Parent = parent;

            // Constructs the graph pattern specified by the path expressions in the MATCH clause
            foreach (var path in matchClause.Paths)
            {
                var index = 0;
                MatchEdge preEdge = null;
                for (var count = path.PathEdgeList.Count; index < count; ++index)
                {
                    var currentNodeTableRef = path.PathEdgeList[index].Item1;
                    var currentEdgeColumnRef = path.PathEdgeList[index].Item2;
                    var currentNodeExposedName = currentNodeTableRef.BaseIdentifier.Value;
                    var nextNodeTableRef = index != count - 1
                        ? path.PathEdgeList[index + 1].Item1
                        : path.Tail;
                    var nextNodeExposedName = nextNodeTableRef.BaseIdentifier.Value;
                    var patternNode = nodes.GetOrCreate(currentNodeExposedName);
                    if (patternNode.NodeAlias == null)
                    {
                        patternNode.NodeAlias = currentNodeExposedName;
                        patternNode.Neighbors = new List<MatchEdge>();
                        patternNode.External = false;
                        var nodeTable = _context[currentNodeExposedName] as WNamedTableReference;
                        if (nodeTable != null)
                        {
                            patternNode.NodeTableObjectName = nodeTable.TableObjectName;
                            if (patternNode.NodeTableObjectName.SchemaIdentifier == null)
                                patternNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"});
                        }
                    }

                    string edgeAlias = currentEdgeColumnRef.Alias;
                    if (edgeAlias == null)
                    {
                        var currentEdgeName = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value;
                        edgeAlias = string.Format("{0}_{1}_{2}", currentNodeExposedName, currentEdgeName,
                            nextNodeExposedName);
                        if (edgeColumnToAliasesDict.ContainsKey(currentEdgeName))
                        {
                            edgeColumnToAliasesDict[currentEdgeName].Add(edgeAlias);
                        }
                        else
                        {
                            edgeColumnToAliasesDict.Add(currentEdgeName, new List<string> { edgeAlias });
                        }
                    }

                    Identifier edgeIdentifier = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last();
                    string schema = patternNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower();
                    string nodeTableName = patternNode.NodeTableObjectName.BaseIdentifier.Value;
                    string bindTableName =
                        _context.EdgeNodeBinding[
                            new Tuple<string, string>(nodeTableName.ToLower(), edgeIdentifier.Value.ToLower())].ToLower();
                    MatchEdge edge;
                    if (currentEdgeColumnRef.MinLength == 1 && currentEdgeColumnRef.MaxLength == 1)
                    {
                        edge = new MatchEdge
                        {
                            SourceNode = patternNode,
                            EdgeColumn = currentEdgeColumnRef,
                            EdgeAlias = edgeAlias,
                            BindNodeTableObjName =
                                new WSchemaObjectName(
                                    new Identifier {Value = schema},
                                    new Identifier {Value = bindTableName}
                                    ),
                        };
                        _context.AddEdgeReference(edge);
                    }
                    else
                    {
                        MatchPath matchPath = new MatchPath
                        {
                            SourceNode = patternNode,
                            EdgeColumn = currentEdgeColumnRef,
                            EdgeAlias = edgeAlias,
                            BindNodeTableObjName =
                                new WSchemaObjectName(
                                    new Identifier {Value = schema},
                                    new Identifier {Value = bindTableName}
                                    ),
                            MinLength = currentEdgeColumnRef.MinLength,
                            MaxLength = currentEdgeColumnRef.MaxLength,
                            ReferencePathInfo = false,
                            AttributeValueDict = currentEdgeColumnRef.AttributeValueDict
                        };
                        _context.AddEdgeReference(matchPath);
                        pathDictionary[edgeAlias] = matchPath;
                        edge = matchPath;
                    }

                    if (preEdge != null)
                    {
                        preEdge.SinkNode = patternNode;
                    }
                    preEdge = edge;

                    if (!parent.ContainsKey(currentNodeExposedName))
                        parent[currentNodeExposedName] = currentNodeExposedName;
                    if (!parent.ContainsKey(nextNodeExposedName))
                        parent[nextNodeExposedName] = nextNodeExposedName;

                    unionFind.Union(currentNodeExposedName, nextNodeExposedName);


                    patternNode.Neighbors.Add(edge);


                }
                var tailExposedName = path.Tail.BaseIdentifier.Value;
                var tailNode = nodes.GetOrCreate(tailExposedName);
                if (tailNode.NodeAlias == null)
                {
                    tailNode.NodeAlias = tailExposedName;
                    tailNode.Neighbors = new List<MatchEdge>();
                    var nodeTable = _context[tailExposedName] as WNamedTableReference;
                    if (nodeTable != null)
                    {
                        tailNode.NodeTableObjectName = nodeTable.TableObjectName;
                        if (tailNode.NodeTableObjectName.SchemaIdentifier == null)
                            tailNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"});
                    }
                }
                if (preEdge != null)
                    preEdge.SinkNode = tailNode;
            }

            // Puts nodes into subgraphs
            foreach (var node in nodes)
            {
                string root = unionFind.Find(node.Key);
                if (!subGrpahMap.ContainsKey(root))
                {
                    var subGraph = new ConnectedComponent();
                    subGraph.Nodes[node.Key] = node.Value;
                    foreach (var edge in node.Value.Neighbors)
                    {
                        subGraph.Edges[edge.EdgeAlias] = edge;
                    }
                    subGrpahMap[root] = subGraph;
                    connectedSubGraphs.Add(subGraph);
                    subGraph.IsTailNode[node.Value] = false;
                }
                else
                {
                    var subGraph = subGrpahMap[root];
                    subGraph.Nodes[node.Key] = node.Value;
                    foreach (var edge in node.Value.Neighbors)
                    {
                        subGraph.Edges[edge.EdgeAlias] = edge;
                    }
                    subGraph.IsTailNode[node.Value] = false;
                }
            }

            var graph = new MatchGraph
            {
                ConnectedSubGraphs = connectedSubGraphs,
            };
            unionFind.Parent = null;

            // When an edge in the MATCH clause is not associated with an alias, 
            // assigns to it a default alias: sourceAlias_EdgeColumnName_sinkAlias. 
            // Also rewrites edge attributes anywhere in the query that can be bound to this default alias. 
            var replaceTableRefVisitor = new ReplaceEdgeReferenceVisitor();
            replaceTableRefVisitor.Invoke(query, edgeColumnToAliasesDict);

            // Rematerializes node tables in the MATCH clause which are defined in the upper-level context
            // and join them with the upper-level table references.
            RematerilizeExtrenalNodeTableReference(query, nodes);

            // Transforms the path reference in the SELECT elements into a 
            // scalar function to display path information.
            TransformPathInfoDisplaySelectElement(query, pathDictionary);

            
            return graph;
        }