public void Invoke(WWhereClause node, MatchGraph graph, Dictionary<string, string> columnTableMapping) { _graph = graph; _columnTableMapping = columnTableMapping; if (node.SearchCondition != null) node.SearchCondition.Accept(this); }
public void Invoke(WWhereClause node, MatchGraph graph) { _graph = graph; if (node.SearchCondition != null) { node.SearchCondition.Accept(this); } }
public void Invoke(WWhereClause node, MatchGraph graph, Dictionary <string, string> columnTableMapping) { _graph = graph; _columnTableMapping = columnTableMapping; if (node.SearchCondition != null) { node.SearchCondition.Accept(this); } }
public WWhereClause Invoke(WWhereClause node, MatchGraph graph, Dictionary <string, string> columnTableMapping) { _checkNodeEdgeReferenceVisitor = new CheckNodeEdgeReferenceVisitor(graph, columnTableMapping) ; if (node.SearchCondition != null) { node.SearchCondition.Accept(this); } return(_nodeEdgePredicatesWhenClause); }
private void ConstructSelectGraph() { QueryGraph = GraphViewDocDBCommand.DocDB_ConstructGraph(SelectQueryBlock); NodeTable = QueryGraph.ConnectedSubGraphs[0].Nodes; var AttachPredicateVisitor = new AttachWhereClauseVisitor(); var TableContext = new WSqlTableContext(); var GraphMeta = new GraphMetaData(); var columnTableMapping = TableContext.GetColumnToAliasMapping(GraphMeta.ColumnsOfNodeTables); if (SelectQueryBlock != null) { AttachPredicateVisitor.Invoke(SelectQueryBlock.WhereClause, QueryGraph, columnTableMapping); } int GroupNumber = 0; foreach (var node in NodeTable) { GraphViewDocDBCommand.GetQuery(node.Value); if (!GraphDescription.ContainsKey(node.Value.NodeAlias)) { GraphDescription[node.Value.NodeAlias] = ++GroupNumber; } } }
/// <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; }
/// <summary> /// Replaces the SELECT * expression with all visible columns /// </summary> /// <param name="node"></param> /// <param name="graph"></param> private void ChangeSelectStarExpression(WSelectQueryBlock node, MatchGraph graph) { var newSelectElements = new List<WSelectElement>(); Dictionary<string, List<WSelectElement>> starReplacement = null; foreach (var element in node.SelectElements) { var starElement = element as WSelectStarExpression; if (starElement != null) { if (starReplacement == null) { starReplacement = new Dictionary<string, List<WSelectElement>>(StringComparer.OrdinalIgnoreCase); // Fetch table in order foreach (var table in _context.NodeTableDictionary) { var alias = table.Key; var namedTable = table.Value as WNamedTableReference; if (namedTable != null) { foreach ( var column in _graphMetaData.ColumnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(namedTable.TableObjectName)].Where( e => e.Value.Role != WNodeTableColumnRole.Edge).Select(e => e.Key)) { var elementList = starReplacement.GetOrCreate(alias); elementList.Add(new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = alias}, new Identifier {Value = column} } } } }); } if (graph == null) continue; foreach (var subGraph in graph.ConnectedSubGraphs) { if (subGraph.Nodes.ContainsKey(alias)) { var matchNode = subGraph.Nodes[alias]; foreach (var edge in matchNode.Neighbors) { var schemaName = edge.SourceNode.NodeTableObjectName.SchemaIdentifier == null ? "dbo" : edge.SourceNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower(); var nodeTuple = new Tuple<string, string>(schemaName, edge.SourceNode.NodeTableObjectName.BaseIdentifier.Value.ToLower()); var edgeColumnName = edge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); if (!_graphMetaData.ColumnsOfNodeTables[nodeTuple].ContainsKey(edgeColumnName)) { throw new GraphViewException("Invalid Edge Alias"); } foreach ( var column in _graphMetaData.ColumnsOfNodeTables[nodeTuple][edgeColumnName].EdgeInfo .ColumnAttributes) { var elementList = starReplacement.GetOrCreate(edge.EdgeAlias); elementList.Add(new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = edge.EdgeAlias}, new Identifier {Value = column} } } } }); } } } } } else { var derivedTable = table.Value as WQueryDerivedTable; if (derivedTable == null) continue; var elementList = starReplacement.GetOrCreate(alias); elementList.Add(new WSelectStarExpression { Qulifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> {new Identifier {Value = alias}} } }); } } } if (starElement.Qulifier != null) { newSelectElements.AddRange(starReplacement[starElement.Qulifier.Identifiers.Last().Value]); } else { foreach (var value in starReplacement.Values) { newSelectElements.AddRange(value); } } } else { newSelectElements.Add(element); } } if (newSelectElements.Any()) node.SelectElements = newSelectElements; }
private void AttachPredicates(WWhereClause whereClause, MatchGraph graph) { // Attaches proper parts of the where clause into the Estimiation Query var attachPredicateVisitor = new AttachWhereClauseVisitor(); var columnTableMapping = _context.GetColumnToAliasMapping(_graphMetaData.ColumnsOfNodeTables); attachPredicateVisitor.Invoke(whereClause, graph, columnTableMapping); }
/// <summary> /// Estimates the average degree of the edges and retrieve density value. /// Send sa query to retrieve the varbinary of the sink in the edge sampling table with edge predicates, /// then generates the statistics histogram for each edge /// </summary> private void RetrieveStatistics(MatchGraph graph) { if (graph == null) throw new ArgumentNullException("graph"); // Declare the parameters if any var declareParameter = ""; if (_variables != null) { declareParameter = _variables.Aggregate(declareParameter, (current, parameter) => current + ("DECLARE " + parameter.VariableName.Value + " " + TsqlFragmentToString.DataType(parameter.DataType) + "\r\n")); } // Calculates the average degree var sb = new StringBuilder(); bool first = true; sb.Append("SELECT [Edge].*, [EdgeDegrees].[SampleRowCount], [EdgeDegrees].[AverageDegree] FROM"); sb.Append("(\n"); foreach (var edge in graph.ConnectedSubGraphs.SelectMany(subGraph => subGraph.Edges.Values)) { if (!first) sb.Append("\nUNION ALL\n"); else { first = false; } var tableObjectName = edge.SourceNode.NodeTableObjectName; string schema = tableObjectName.SchemaIdentifier.Value.ToLower(); string tableName = tableObjectName.BaseIdentifier.Value.ToLower(); string edgeName = edge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); string bindTableName = _context.EdgeNodeBinding[new Tuple<string, string>(tableName, edgeName)]; // Distinguished between path and edge //var sinkColumnName = edge.IsPath ? "COUNT(Sink)" : "[dbo].[GraphViewUDFGlobalNodeIdEncoder](Sink)"; sb.Append( string.Format(@" SELECT '{0}' as TableSchema, '{1}' as TableName, '{2}' as ColumnName, '{3}' as Alias, [dbo].[GraphViewUDFGlobalNodeIdEncoder](Src) as Src, [dbo].[GraphViewUDFGlobalNodeIdEncoder](Sink) as Sink, 0 as IsReversed, NULL as OriginalEdgeAlias FROM [{0}_{1}_{2}_Sampling] as [{3}]", schema, bindTableName, edgeName, edge.EdgeAlias)); var predicatesExpr = edge.RetrievePredicatesExpression(); if (predicatesExpr!=null) sb.AppendFormat("\n WHERE {0}", predicatesExpr); MatchEdge revEdge; if (graph.ReversedEdgeDict.TryGetValue(edge.EdgeAlias, out revEdge)) { sb.Append("\nUNION ALL\n"); var isEdgeView = revEdge.IsEdgeView; var revTableObjectName = revEdge.SourceNode.NodeTableObjectName; var revSchema = revTableObjectName.SchemaIdentifier.Value.ToLower(); var revEdgeName = revEdge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); var revBindTableName = revEdge.BindNodeTableObjName.Identifiers.Last().Value.ToLower(); var revSamplingTableName = revSchema + "_" + revBindTableName + "_" + revEdgeName; var revSrcTuple = WNamedTableReference.SchemaNameToTuple(revEdge.BindNodeTableObjName); //var revSinkTuple = WNamedTableReference.SchemaNameToTuple(revEdge.SinkNode.NodeTableObjectName); var originalEdgeName = edge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); // [node/nodeView]-[edgeView]->[node/nodeView] if (isEdgeView) { revEdgeName = revSrcTuple.Item2 + "_" + originalEdgeName + "Reversed"; revSamplingTableName = revSrcTuple.Item1 + "_" + revSrcTuple.Item2 + "_" + revEdgeName; } sb.Append( string.Format(@" SELECT '{0}' as TableSchema, '{1}' as TableName, '{2}' as ColumnName, '{3}' as Alias, [dbo].[GraphViewUDFGlobalNodeIdEncoder](Src) as Src, [dbo].[GraphViewUDFGlobalNodeIdEncoder](Sink) as Sink, 1 as IsReversed, '{4}' as OriginalEdgeAlias FROM [{5}_Sampling] as [{3}]", revSchema, revBindTableName, revEdgeName, revEdge.EdgeAlias, edge.EdgeAlias, revSamplingTableName)); var revPredicatesExpr = revEdge.RetrievePredicatesExpression(); if (revPredicatesExpr != null) sb.AppendFormat("\n WHERE {0}", revPredicatesExpr); } } sb.Append("\n) as Edge \n"); sb.Append(String.Format(@" INNER JOIN [{0}] as [EdgeDegrees] ON [EdgeDegrees].[TableSchema] = [Edge].[TableSchema] AND [EdgeDegrees].[TableName] = [Edge].[TableName] AND [EdgeDegrees].[ColumnName] = [Edge].[ColumnName]", GraphViewConnection.MetadataTables[3])); using (var command = Tx.Connection.CreateCommand()) { command.Transaction = Tx; command.CommandText = declareParameter + sb.ToString(); using (var reader = command.ExecuteReader()) { var srcNodeStatisticsDict = graph.SourceNodeStatisticsDict; while (reader.Read()) { MatchEdge edge = null; bool isReversed = reader["isReversed"].ToString().Equals("1"); if (!isReversed && !graph.TryGetEdge(reader["Alias"].ToString(), out edge)) throw new GraphViewException(string.Format("Edge {0} not exists", reader["Alias"].ToString())); if (isReversed && !graph.TryGetEdge(reader["OriginalEdgeAlias"].ToString(), out edge)) throw new GraphViewException(string.Format("Edge {0} not exists", reader["OriginalEdgeAlias"].ToString())); if (isReversed) edge = graph.ReversedEdgeDict[edge.EdgeAlias]; var srcBytes = reader["Src"] as byte[]; var cursor = 0; if (srcBytes != null) { var srcList = new List<long>(); while (cursor < srcBytes.Length) { var src = BitConverter.ToInt64(srcBytes, cursor); cursor += 8; srcList.Add(src); } var tmpEdge = new MatchEdge(); Statistics.UpdateEdgeHistogram(tmpEdge, srcList); srcNodeStatisticsDict[new Tuple<string, bool>(edge.EdgeAlias, isReversed)] = tmpEdge.Statistics; } else srcNodeStatisticsDict[new Tuple<string, bool>(edge.EdgeAlias, isReversed)] = null; var sinkBytes = reader["Sink"] as byte[]; if (sinkBytes == null) { edge.Statistics = new Statistics { Density = 0, Histogram = new Dictionary<long, Tuple<double, bool>>(), MaxValue = 0, RowCount = 0, //Selectivity = 1.0 }; continue; } List<long> sinkList = new List<long>(); cursor = 0; while (cursor < sinkBytes.Length) { var sink = BitConverter.ToInt64(sinkBytes, cursor); cursor += 8; sinkList.Add(sink); } Statistics.UpdateEdgeHistogram(edge, sinkList); edge.AverageDegree = Convert.ToDouble(reader["AverageDegree"])*sinkList.Count*1.0/ Convert.ToInt64(reader["SampleRowCount"]); var path = edge as MatchPath; if (path != null) { if (path.AverageDegree > 1) if (path.MaxLength != -1) { path.AverageDegree = Math.Pow(path.AverageDegree, path.MaxLength) - (path.MinLength > 0 ? Math.Pow(path.AverageDegree, path.MinLength - 1) : 0); } else path.AverageDegree = double.MaxValue; } } } // Retrieves density value for each node table string tempTableName = Path.GetRandomFileName().Replace(".", "").Substring(0, 8); var dbccDensityQuery = new StringBuilder(); dbccDensityQuery.Append(string.Format(@"CREATE TABLE #{0} (Density float, Len int, Col sql_variant); INSERT INTO #{0} EXEC('", tempTableName)); Dictionary<Tuple<string, string>, List<MatchNode>> schemaTableToNodeListMapping = new Dictionary<Tuple<string, string>, List<MatchNode>>(); foreach (var subGraph in graph.ConnectedSubGraphs) { foreach (var node in subGraph.Nodes.Values) { var tableTuple = WNamedTableReference.SchemaNameToTuple(node.NodeTableObjectName); if (_graphMetaData.NodeViewMapping.ContainsKey(tableTuple)) { node.GlobalNodeIdDensity = Statistics.DefaultDensity; } else { var nodeList = schemaTableToNodeListMapping.GetOrCreate(tableTuple); nodeList.Add(node); } } } foreach (var tableTuple in schemaTableToNodeListMapping.Keys) { dbccDensityQuery.Append(string.Format( "DBCC SHOW_STATISTICS (\"{0}.{1}\", [{0}{1}_PK_GlobalNodeId]) with DENSITY_VECTOR;\n", tableTuple.Item1, tableTuple.Item2)); } dbccDensityQuery.Append("');\n"); dbccDensityQuery.Append(string.Format("SELECT Density FROM #{0} WHERE Col = 'GlobalNodeId'", tempTableName)); command.CommandText = dbccDensityQuery.ToString(); using (var reader = command.ExecuteReader()) { foreach (var item in schemaTableToNodeListMapping) { double density; if (!reader.Read()) density = Statistics.DefaultDensity; else { density = Convert.ToDouble(reader["Density"]); if (Math.Abs(density - 1.0) < 0.0001) density = Statistics.DefaultDensity; } foreach (var node in item.Value) { node.GlobalNodeIdDensity = density; } } } } }
public CheckNodeEdgeReferenceVisitor(MatchGraph graph, Dictionary<string, string> columnTableMapping) { _graph = graph; _columnTableMapping = columnTableMapping; }
public CheckNodeEdgeReferenceVisitor(MatchGraph graph, Dictionary <string, string> columnTableMapping) { _graph = graph; _columnTableMapping = columnTableMapping; }
public DocDbGraphOptimizer(MatchGraph graph) : base(graph) { }
/// <summary> /// Estimate the average degree of the edges and retrieve density value. /// Send a query to retrieve the varbinary of the sink in the edge sampling table with edge predicates, /// then generate the statistics histogram for each edge /// </summary> /// <param name="subGraph"></param> private void EstimateAverageDegree(MatchGraph graph) { // Declare the parameters if any var declareParameter = ""; if (_variables != null) { foreach (var parameter in _variables) { declareParameter += "DECLARE " + parameter.VariableName.Value + " " + TsqlFragmentToString.DataType(parameter.DataType) + "\r\n"; } } // Calculate the average degree var sb = new StringBuilder(); bool first = true; sb.Append("SELECT [Edge].*, [sysindexes].[rows], [EdgeDegrees].[AverageDegree] FROM"); sb.Append("(\n"); foreach (var edge in graph.ConnectedSubGraphs.SelectMany(subGraph => subGraph.Edges.Values)) { if (!first) sb.Append("\nUNION ALL\n"); else { first = false; } var tableObjectName = edge.SourceNode.TableObjectName; sb.Append( string.Format(@" SELECT '{0}' as TableSchema, '{1}' as TableName, '{2}' as ColumnName, '{3}' as Alias, [dbo].[GraphViewUDFGlobalNodeIdEncoder](Sink) as Sink FROM [{0}_{1}_{2}_Sampling] as [{3}]", tableObjectName.SchemaIdentifier.Value, tableObjectName.BaseIdentifier.Value, edge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value, edge.EdgeAlias)); if (edge.Predicates != null) { sb.Append("\n WHERE "); bool fisrtPre = true; foreach (var predicate in edge.Predicates) { if (fisrtPre) fisrtPre = false; else { sb.Append(" AND "); } sb.Append(predicate); } } } sb.Append("\n) as Edge \n"); sb.Append(String.Format(@"INNER JOIN [sysindexes] ON [id] = OBJECT_ID([TableSchema] + '_' + [TableName] + '_' + [ColumnName] + '_' + 'Sampling') and [indid]<2 INNER JOIN [{0}] as [EdgeDegrees] ON [EdgeDegrees].[TableSchema] = [Edge].[TableSchema] AND [EdgeDegrees].[TableName] = [Edge].[TableName] AND [EdgeDegrees].[ColumnName] = [Edge].[ColumnName]", GraphViewConnection.MetadataTables[3])); // Retrieve density value for each node table _tableIdDensity.Clear(); string tempTableName = Path.GetRandomFileName().Replace(".", "").Substring(0, 8); var dbccDensityQuery = new StringBuilder(); dbccDensityQuery.Append(string.Format(@"CREATE TABLE #{0} (Density float, Len int, Col sql_variant); INSERT INTO #{0} EXEC('", tempTableName)); foreach (var nodeTable in graph.NodeTypesSet) { _tableIdDensity[string.Format("[{0}].[{1}]", nodeTable.Item1, nodeTable.Item2)] = ColumnStatistics.DefaultDensity; dbccDensityQuery.Append(string.Format( "DBCC SHOW_STATISTICS (\"{0}.{1}\", [{0}{1}_PK_GlobalNodeId]) with DENSITY_VECTOR;\n", nodeTable.Item1, nodeTable.Item2)); } dbccDensityQuery.Append("');\n"); dbccDensityQuery.Append(string.Format("SELECT Density FROM #{0} WHERE Col = 'GlobalNodeId'", tempTableName)); using (var command = Conn.CreateCommand()) { command.CommandText = declareParameter + sb.ToString(); using (var reader = command.ExecuteReader()) { while (reader.Read()) { MatchEdge edge; if (!graph.TryGetEdge(reader["Alias"].ToString(), out edge)) throw new GraphViewException(string.Format("Edge {0} not exists", reader["Alias"].ToString())); var sinkBytes = reader["Sink"] as byte[]; if (sinkBytes == null) { _context.AddEdgeStatistics(edge, new ColumnStatistics { Density = 0, Histogram = new Dictionary<long, Tuple<double, bool>>(), MaxValue = 0, RowCount = 0, Selectivity = 1.0 }); continue; } List<long> sinkList = new List<long>(); var cursor = 0; while (cursor < sinkBytes.Length) { var sink = BitConverter.ToInt64(sinkBytes, cursor); cursor += 8; sinkList.Add(sink); } UpdateEdgeHistogram(edge, sinkList); edge.AverageDegree = Convert.ToDouble(reader["AverageDegree"]) * sinkList.Count * 1.0 / Convert.ToInt64(reader["rows"]); } } var tableKey = _tableIdDensity.Keys.ToArray(); command.CommandText = dbccDensityQuery.ToString(); using (var reader = command.ExecuteReader()) { foreach (var key in tableKey) { if (!reader.Read()) break; _tableIdDensity[key] = Convert.ToDouble(reader["Density"]); } } } _tableIdDensity = _tableIdDensity.OrderBy(e => e.Key).ToDictionary(e => e.Key, e => e.Value); }
/// <summary> /// Estimate number of rows of node table in graph. /// </summary> /// <param name="graph">Constructed node graph</param> private void EstimateRows(WSelectQueryBlock query, MatchGraph graph) { var declareParameter = ""; if (_variables != null) { foreach (var parameter in _variables) { declareParameter += "DECLARE " + parameter.VariableName.Value + " " + TsqlFragmentToString.DataType(parameter.DataType) + "\r\n"; } } var estimator = new TableSizeEstimator(Conn); bool first = true; var fromQuerySb = new StringBuilder(1024); foreach (var subGraph in graph.ConnectedSubGraphs) { foreach (var node in subGraph.Nodes) { if (first) first = false; else fromQuerySb.Append(", "); var currentNode = node.Value; fromQuerySb.AppendFormat("{0} AS [{1}] WITH (ForceScan)", currentNode.TableObjectName, currentNode.NodeAlias); } } // Attach proper parts of the where clause into the Estimiation Query var columnTableMapping = _context.GetColumnTableMapping(_columnsOfNodeTables); var attachWhereClauseVisiter = new AttachNodeEdgePredictesVisitor(); var nodeEstimationWhereClause = attachWhereClauseVisiter.Invoke(query.WhereClause, graph, columnTableMapping); string nodeEstimationQuery = string.Format("{0}\r\n SELECT * FROM {1}\r\n", declareParameter, fromQuerySb); if (nodeEstimationWhereClause.SearchCondition != null) nodeEstimationQuery += nodeEstimationWhereClause.ToString(); var estimateRows = estimator.GetQueryTableEstimatedRows(nodeEstimationQuery); foreach (var subGraph in graph.ConnectedSubGraphs) { // Update Row Estimation for nodes foreach (var node in subGraph.Nodes) { var currentNode = node.Value; currentNode.EstimatedRows = estimateRows[node.Key]; var tableSchema = currentNode.TableObjectName.SchemaIdentifier.Value; var tableName = currentNode.TableObjectName.BaseIdentifier.Value; currentNode.TableRowCount = estimator.GetTableRowCount(tableSchema, tableName); } } // Attach predicates to nodes and edges var attachPredicateVisitor = new AttachWhereClauseVisitor(); attachPredicateVisitor.Invoke(query.WhereClause, graph, columnTableMapping); }
/// <summary> /// Construct Graph from the match clause. The Graph can consist of multiple connected SubGraph. /// Not supported in this version /// </summary> /// <param name="query"></param> /// <returns></returns> private MatchGraph ConstructGraph(WSelectQueryBlock query) { var unionFind = new UnionFind(); if (query.MatchClause == null) return null; var edgeTableReferenceDict = new Dictionary<string, List<string>>(StringComparer.CurrentCultureIgnoreCase); var matchClause = query.MatchClause; var nodes = new Dictionary<string, MatchNode>(StringComparer.CurrentCultureIgnoreCase); var connectedSubGraphs = new List<ConnectedComponent>(); var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.CurrentCultureIgnoreCase); var parent = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); unionFind.Parent = parent; HashSet<Tuple<string, string>> nodeTypes = new HashSet<Tuple<string, string>>(); //Construct Graph from Match Pattern foreach (var path in matchClause.Paths) { var index = 0; MatchEdge preEdge = null; for (var count = path.PathNodeList.Count; index < count; ++index) { var currentNode = path.PathNodeList[index].Item1; var currentEdge = path.PathNodeList[index].Item2; var currentNodeExposedName = currentNode.BaseIdentifier.Value; var nextNode = index != count - 1 ? path.PathNodeList[index + 1].Item1 : path.Tail; var nextNodeExposedName = nextNode.BaseIdentifier.Value; var node = nodes.GetOrCreate(currentNodeExposedName); if (node.NodeAlias == null) { node.NodeAlias = currentNodeExposedName; node.Neighbors = new List<MatchEdge>(); node.External = false; var nodeTable = _context[currentNodeExposedName] as WNamedTableReference; if (nodeTable != null) { node.TableObjectName = nodeTable.TableObjectName; if (node.TableObjectName.SchemaIdentifier == null) node.TableObjectName.Identifiers.Insert(0, new Identifier { Value = "dbo" }); var nodeTypeTuple = WNamedTableReference.SchemaNameToTuple(node.TableObjectName); if (!nodeTypes.Contains(nodeTypeTuple)) nodeTypes.Add(nodeTypeTuple); } } if (currentEdge.AliasRole == AliasType.Default) { var currentEdgeName = currentEdge.MultiPartIdentifier.Identifiers.Last().Value; if (edgeTableReferenceDict.ContainsKey(currentEdgeName)) { edgeTableReferenceDict[currentEdgeName].Add(currentEdge.Alias); } else { edgeTableReferenceDict.Add(currentEdgeName, new List<string> { currentEdge.Alias }); } } var edge = new MatchEdge { SourceNode = node, EdgeColumn = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = node.NodeAlias}, currentEdge.MultiPartIdentifier.Identifiers.Last() } } }, EdgeAlias = currentEdge.Alias }; if (preEdge != null) { preEdge.SinkNode = node; } preEdge = edge; if (!parent.ContainsKey(currentNodeExposedName)) parent[currentNodeExposedName] = currentNodeExposedName; if (!parent.ContainsKey(nextNodeExposedName)) parent[nextNodeExposedName] = nextNodeExposedName; unionFind.Union(currentNodeExposedName, nextNodeExposedName); node.Neighbors.Add(edge); _context.AddEdgeReference(currentEdge.Alias, edge.SourceNode.TableObjectName, currentEdge); } 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.TableObjectName = nodeTable.TableObjectName; if (tailNode.TableObjectName.SchemaIdentifier == null) tailNode.TableObjectName.Identifiers.Insert(0, new Identifier { Value = "dbo" }); var nodeTypeTuple = WNamedTableReference.SchemaNameToTuple(tailNode.TableObjectName); if (!nodeTypes.Contains(nodeTypeTuple)) nodeTypes.Add(nodeTypeTuple); } } if (preEdge != null) preEdge.SinkNode = tailNode; } // Put 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; } } // Replace Edge name alias with proper alias in the query var replaceTableRefVisitor = new ReplaceTableRefVisitor(); replaceTableRefVisitor.Invoke(query, edgeTableReferenceDict); // If a table alias in the MATCH clause is defined in an upper-level context, // to be able to translate this MATCH clause, this table alias must be re-materialized // in the FROM clause of the current context and joined with the corresponding table // in the upper-level context. var tableRefs = query.FromClause.TableReferences; var tableSet = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase); var newTableRefs = new List<WTableReference>(); for (int index = 0; index < tableRefs.Count; ++index) { var table = tableRefs[index] as WNamedTableReference; if (table == null) { newTableRefs.Add(tableRefs[index]); continue; } var tableTuple = WNamedTableReference.SchemaNameToTuple(table.TableObjectName); if (!nodeTypes.Contains(tableTuple)) { newTableRefs.Add(table); } else { tableSet.Add(table.ExposedName.Value); } } query.FromClause = new WFromClause { TableReferences = newTableRefs, }; WBooleanExpression whereCondiction = null; foreach (var node in nodes) { if (!tableSet.Contains(node.Key)) { node.Value.External = true; var newWhereCondition = new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Key }, new Identifier { Value = "GlobalNodeId" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Value.RefAlias }, new Identifier { Value = "GlobalNodeId" }) }, }; whereCondiction = WBooleanBinaryExpression.Conjunction(whereCondiction, newWhereCondition); } } if (whereCondiction != null) { if (query.WhereClause == null) { query.WhereClause = new WWhereClause { SearchCondition = whereCondiction }; } else { if (query.WhereClause.SearchCondition == null) { query.WhereClause.SearchCondition = whereCondiction; } else { query.WhereClause.SearchCondition = new WBooleanBinaryExpression { BooleanExpressionType = BooleanBinaryExpressionType.And, FirstExpr = new WBooleanParenthesisExpression { Expression = query.WhereClause.SearchCondition }, SecondExpr = new WBooleanParenthesisExpression { Expression = whereCondiction } }; } } } var graph = new MatchGraph { ConnectedSubGraphs = connectedSubGraphs, NodeTypesSet = nodeTypes, }; unionFind.Parent = null; return graph; }
protected GraphOptimizer(MatchGraph graph) { Graph = graph; }
/// <summary> /// Estimates the number of rows of each node table in the graph pattern. /// </summary> /// <param name="graph">The graph pattern specified by the MATCH clause</param> private void EstimateRows(MatchGraph graph) { var declareParameter = ""; if (_variables != null) { declareParameter = _variables.Aggregate(declareParameter, (current, parameter) => current + ("DECLARE " + parameter.VariableName.Value + " " + TsqlFragmentToString.DataType(parameter.DataType) + "\r\n")); } var estimator = new TableSizeEstimator(Tx); bool first = true; // Constructs a union-all query in which each sub-query is a selection of // a node table alias plus predicates that can be associated with it. // This query is sent to the SQL DB to get the estimated cardinality of // each node table alias. var estimateQuerySb = new StringBuilder(1024); foreach (var subGraph in graph.ConnectedSubGraphs) { foreach (var node in subGraph.Nodes) { if (first) first = false; else estimateQuerySb.Append("\r\nUNION ALL\r\n"); var currentNode = node.Value; estimateQuerySb.AppendFormat("SELECT GlobalNodeId FROM {0} AS [{1}]", currentNode.NodeTableObjectName, currentNode.NodeAlias); if (currentNode.Predicates != null && currentNode.Predicates.Count > 0) { estimateQuerySb.AppendFormat("\r\nWHERE {0}", currentNode.Predicates[0]); for (int i = 1; i < currentNode.Predicates.Count; i++) { var predicate = currentNode.Predicates[i]; estimateQuerySb.AppendFormat(" AND {0}", predicate); } } } } string nodeEstimationQuery = string.Format("{0}\r\n {1}\r\n", declareParameter, estimateQuerySb); // A list of estimated cardinalities var estimateRows = estimator.GetUnionQueryTableEstimatedRows(nodeEstimationQuery); int j = 0; foreach (var subGraph in graph.ConnectedSubGraphs) { // Update Row Estimation for nodes foreach (var node in subGraph.Nodes) { var currentNode = node.Value; var tableSchema = currentNode.NodeTableObjectName.SchemaIdentifier.Value; var tableName = currentNode.NodeTableObjectName.BaseIdentifier.Value; var tableTuple = WNamedTableReference.SchemaNameToTuple(currentNode.NodeTableObjectName); if (_graphMetaData.NodeViewMapping.ContainsKey(tableTuple)) { var nodeSet = _graphMetaData.NodeViewMapping[tableTuple]; int n = nodeSet.Count; double nodeViewEstRows = 0.0; currentNode.TableRowCount = nodeSet.Aggregate(0, (cur, next) => cur + estimator.GetTableRowCount(tableSchema, next)); bool bug = false; while (n > 0) { n--; if (j > estimateRows.Count - 1) { bug = true; break; } nodeViewEstRows += estimateRows[j]; j++; } currentNode.EstimatedRows = bug ? currentNode.TableRowCount : nodeViewEstRows; } else { currentNode.TableRowCount = estimator.GetTableRowCount(tableSchema, tableName); if (j > estimateRows.Count - 1) currentNode.EstimatedRows = currentNode.TableRowCount; else currentNode.EstimatedRows = estimateRows[j]; j++; } } } }
/// <summary> /// Marks the node table alias, if it has no outgoing edges in the graph pattern /// and its node properties are never referenced or projected in the query. /// Such node table aliases will not appear in the FROM clause in the translated /// query, but only be materialized in adjacency lists, i.e., table-valued functions. /// </summary> /// <param name="query"></param> /// <param name="graph"></param> private void OptimizeTail(WSelectQueryBlock query, MatchGraph graph) { var visitor = new CheckTableReferenceVisitor(); foreach (var connectedSubGraph in graph.ConnectedSubGraphs) { var toRemove = connectedSubGraph.Nodes.Where( node => node.Value.Neighbors.Count == 0 && !visitor.Invoke(query, node.Key, _context, _graphMetaData.ColumnsOfNodeTables) ) .ToArray(); foreach (var item in toRemove) { connectedSubGraph.IsTailNode[item.Value] = true; } } }
public WWhereClause Invoke(WWhereClause node, MatchGraph graph, Dictionary<string, string> columnTableMapping) { _checkNodeEdgeReferenceVisitor = new CheckNodeEdgeReferenceVisitor(graph, columnTableMapping) ; if (node.SearchCondition != null) node.SearchCondition.Accept(this); return _nodeEdgePredicatesWhenClause; }
/// <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; }