/// <summary> /// Get a full one height tree with joint edges and unmaterlized edges. /// </summary> /// <param name="graph"></param> /// <param name="component"></param> /// <returns></returns> private OneHeightTree GetNodeUnits(ConnectedComponent graph, MatchComponent component) { var useOriginalEdge = new List <OneHeightTree>(); var useRevEdge = new List <OneHeightTree>(); // Return node deinfed in the outer context preferably foreach (var node in graph.Nodes.Values.Where(n => n.IsFromOuterContext && !component.Nodes.Contains(n))) { var remainingEdges = node.Neighbors.Where(e => !component.EdgeMaterilizedDict.ContainsKey(e)).ToList(); if (component.UnmaterializedNodeMapping.ContainsKey(node) || remainingEdges.Any(e => component.Nodes.Contains(e.SinkNode))) { return(new OneHeightTree { TreeRoot = node, Edges = remainingEdges }); } } foreach (var node in graph.Nodes.Values.Where(n => !component.Nodes.Contains(n))) { var remainingEdges = node.Neighbors.Where(e => !component.EdgeMaterilizedDict.ContainsKey(e)).ToList(); if (component.UnmaterializedNodeMapping.ContainsKey(node)) { useOriginalEdge.Add(new OneHeightTree { TreeRoot = node, Edges = remainingEdges, }); break; } if (remainingEdges.Any(e => component.Nodes.Contains(e.SinkNode))) { useRevEdge.Add(new OneHeightTree { TreeRoot = node, Edges = remainingEdges }); } } if (useOriginalEdge.Any()) { return(useOriginalEdge[0]); } if (useRevEdge.Any()) { return(useRevEdge[0]); } return(null); }
/// <summary> /// Get a full one height tree with joint edges and unmaterlized edges. /// </summary> /// <param name="graph"></param> /// <param name="component"></param> /// <returns></returns> private OneHeightTree GetNodeUnits(ConnectedComponent graph, MatchComponent component) { foreach (MatchNode node in graph.Nodes.Values.Where(n => !component.Nodes.ContainsKey(n.NodeAlias))) { List <MatchEdge> remainingEdges = node.Neighbors.Where(e => !component.EdgeMaterilizedDict.ContainsKey(e)).ToList(); if (component.UnmaterializedNodeMapping.ContainsKey(node) || remainingEdges.Any(e => component.Nodes.ContainsKey(e.SinkNode.NodeAlias))) { return(new OneHeightTree { TreeRoot = node, Edges = remainingEdges, }); } } return(null); }
// We firstly think every node are isolated, and we will find subgraphs later internal string AddTable(WNamedTableReference table) { string alias = table.Alias.Value; this.TableInputDependency[alias] = new HashSet <string>(); MatchNode matchNode = new MatchNode() { NodeAlias = alias, Neighbors = new List <MatchEdge>(), ReverseNeighbors = new List <MatchEdge>(), DanglingEdges = new List <MatchEdge>(), Predicates = new List <WBooleanExpression>(), Properties = new HashSet <string>() }; ConnectedComponent subgraph = new ConnectedComponent(); subgraph.Nodes[alias] = matchNode; this.GraphPattern.ConnectedSubgraphs.Add(subgraph); matchNode.Position = this.TableInputDependency.Count; return(alias); }
/// <summary> /// Get a full one height tree with joint edges and unmaterlized edges, /// returns a tuple whose first item is the one height tree and the second item /// indicates whether the one height tree only joins with the component's materialized /// node on its root. /// </summary> /// <param name="graph"></param> /// <param name="component"></param> /// <returns></returns> private IEnumerable<Tuple<OneHeightTree, bool>> GetNodeUnits(ConnectedComponent graph, MatchComponent component) { var nodes = graph.Nodes; foreach (var node in nodes.Values.Where(e => !graph.IsTailNode[e])) { var remainingEdges = node.Neighbors.Where(e => !component.EdgeMaterilizedDict.ContainsKey(e)).ToList(); // If there exists a component's edge pointing to the 1-heright tree's root // or a 1-height tree's edge pointing to the component's node, then generates a valid // 1-height tree with edges. Otherwise, if a node can be joint to component as a single // split node with unmaterialized edges, generates a 1-height tree with a tag set to true. if (component.UnmaterializedNodeMapping.ContainsKey(node) || remainingEdges.Any(e => component.Nodes.Contains(e.SinkNode))) { yield return new Tuple<OneHeightTree, bool>(new OneHeightTree { TreeRoot = node, Edges = remainingEdges }, false); } else if (remainingEdges.Count > 0 && component.MaterializedNodeSplitCount.Count > 1 && component.MaterializedNodeSplitCount.ContainsKey(node)) { yield return new Tuple<OneHeightTree, bool>(new OneHeightTree { TreeRoot = node, Edges = remainingEdges }, true); } } }
public abstract List <Tuple <MatchNode, MatchEdge, MatchNode, List <MatchEdge>, List <MatchEdge> > > GetOptimizedTraversalOrder2(ConnectedComponent subGraph);
// 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 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; }
public abstract List <Tuple <MatchNode, MatchEdge> > GetOptimizedTraversalOrder(ConnectedComponent subGraph, out Dictionary <string, List <Tuple <MatchEdge, MaterializedEdgeType> > > nodeToMaterializedEdgesDict);
/// <summary> /// Get a full one height tree with joint edges and unmaterlized edges, /// returns a tuple whose first item is the one height tree and the second item /// indicates whether the one height tree only joins with the component's materialized /// node on its root. /// </summary> /// <param name="graph"></param> /// <param name="component"></param> /// <returns></returns> private IEnumerable<OneHeightTree> GetNodeUnits(ConnectedComponent graph, MatchComponent component) { //var nodes = graph.Nodes; //foreach (var node in graph.Nodes.Values) foreach (var node in graph.Nodes.Values.Where(n => !component.Nodes.Contains(n))) { var remainingEdges = node.Neighbors.Where(e => !component.EdgeMaterilizedDict.ContainsKey(e)).ToList(); if (component.UnmaterializedNodeMapping.ContainsKey(node) || remainingEdges.Any(e => component.Nodes.Contains(e.SinkNode))) { yield return new OneHeightTree { TreeRoot = node, Edges = remainingEdges }; } } }
/// <summary> /// Get the optimal join component for the given connected graph /// 1. Generate the initial states /// 2. DP, Iterate on each states: /// Get smallest join units -> Enumerate on all possible combination of the join units /// -> Join to the current component to get the next states /// -> Those components with the largest average size per edge will be eliminate if exceeding the upper bound /// 3. If all the components has reached its end states, return the component with the smallest join cost /// </summary> /// <param name="subGraph"></param> /// <param name="revEdgeDict"></param> /// <param name="srcNodeStatisticsDict"></param> /// <returns></returns> private MatchComponent ConstructComponent(ConnectedComponent subGraph, Dictionary<string, MatchEdge> revEdgeDict, Dictionary<Tuple<string, bool>, Statistics> srcNodeStatisticsDict) { //var componentStates = new List<MatchComponent>(); MatchComponent optimalFinalComponent = null; //Init double maxValue = Double.MinValue; var componentStates = subGraph.Nodes.Select(node => new MatchComponent(node.Value)).ToList(); // DP while (componentStates.Any()) { int maxIndex = -1; var nextCompnentStates = new List<MatchComponent>(); // Iterate on current components foreach (var curComponent in componentStates) { var nodeUnits = GetNodeUnits(subGraph, curComponent); if (!nodeUnits.Any() && curComponent.ActiveNodeCount == subGraph.ActiveNodeCount && curComponent.EdgeMaterilizedDict.Count(e => e.Value == true) == subGraph.EdgeCount ) { if (optimalFinalComponent == null || curComponent.Cost < optimalFinalComponent.Cost) { optimalFinalComponent = curComponent; } continue; } var candidateUnits = _pruningStrategy.GetCandidateUnits(nodeUnits, curComponent, revEdgeDict); // Iterates on the candidate node units & add it to the current component to generate next states foreach (var candidateUnit in candidateUnits) { // Pre-filter. If the lower bound of the current totoal join cost // > current optimal join cost, prunes this component. if (optimalFinalComponent != null) { double candidateSize = candidateUnit.TreeRoot.EstimatedRows* candidateUnit.PreMatOutgoingEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next)* candidateUnit.PostMatOutgoingEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next)* candidateUnit.UnmaterializedEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next); double costLowerBound = curComponent.Cardinality* candidateUnit.PreMatIncomingEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur * next) + candidateSize; if (candidateUnit.JoinHint == JoinHint.Loop) costLowerBound = Math.Min(costLowerBound, Math.Log(candidateUnit.TreeRoot.EstimatedRows, 512)); if (curComponent.Cost + costLowerBound > optimalFinalComponent.Cost ) { continue; } } var newComponent = curComponent.GetNextState(candidateUnit, _statisticsCalculator, _graphMetaData, srcNodeStatisticsDict); if (nextCompnentStates.Count >= MaxStates) { if (maxIndex < 0) { var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } else { int compEdgeCount = newComponent.EdgeMaterilizedDict.Count; compEdgeCount = compEdgeCount == 0 ? 1 : compEdgeCount; if (newComponent.Cost/compEdgeCount < maxValue) { nextCompnentStates[maxIndex] = newComponent; var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } continue; } } nextCompnentStates.Add(newComponent); } } componentStates = nextCompnentStates; } return optimalFinalComponent; }
/// <summary> /// Get the optimal join component for the given connected graph /// 1. Generate the initial states /// 2. DP, Iterate on each states: /// Get smallest join units -> Enumerate on all possible combination of the join units /// -> Join to the current component to get the next states /// -> Those components with the largest average size per edge will be eliminate if exceeding the upper bound /// 3. If all the components has reached its end states, return the component with the smallest join cost /// </summary> /// <param name="subGraph"></param> /// <returns></returns> private MatchComponent ConstructComponent(ConnectedComponent subGraph) { var componentStates = new List<MatchComponent>(); MatchComponent optimalFinalComponent = null; //Init double maxValue = Double.MinValue; foreach (var node in subGraph.Nodes) { if (!subGraph.IsTailNode[node.Value]) { // Enumerate on each edge for a node to generate the intial states var nodeEdgeCount = node.Value.Neighbors.Count; int eNum = (int) Math.Pow(2, nodeEdgeCount) - 1; while (eNum > 0) { var nodeInitialEdges = new List<MatchEdge>(); for (int i = 0; i < nodeEdgeCount; i++) { int index = (1 << i); if ((eNum & index) != 0) { nodeInitialEdges.Add(node.Value.Neighbors[i]); } } componentStates.Add(new MatchComponent(node.Value, nodeInitialEdges, _graphMetaData)); eNum--; } } } // DP while (componentStates.Any()) { int maxIndex = -1; var nextCompnentStates = new List<MatchComponent>(); // Iterate on current components foreach (var curComponent in componentStates) { var nodeUnits = GetNodeUnits(subGraph, curComponent); if (!nodeUnits.Any() && curComponent.ActiveNodeCount == subGraph.ActiveNodeCount && curComponent.EdgeMaterilizedDict.Count == subGraph.EdgeCount ) { if (optimalFinalComponent == null || curComponent.Cost < optimalFinalComponent.Cost) { optimalFinalComponent = curComponent; } continue; } var candidateUnits = _pruningStrategy.GetCandidateUnits(nodeUnits, curComponent); // Iterates on the candidate node units & add it to the current component to generate next states foreach (var candidateUnit in candidateUnits) { // Pre-filter. If the lower bound of the current totoal join cost // > current optimal join cost, prunes this component. if (optimalFinalComponent != null) { double candidateSize = candidateUnit.TreeRoot.EstimatedRows* candidateUnit.UnmaterializedEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next)* candidateUnit.MaterializedEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next); double costLowerBound = curComponent.Cardinality + candidateSize; if (candidateUnit.MaterializedEdges.Count == 0) costLowerBound = Math.Min(costLowerBound, Math.Log(candidateUnit.TreeRoot.EstimatedRows, 512)); if (curComponent.Cost + costLowerBound > optimalFinalComponent.Cost ) { continue; } } var newComponent = curComponent.GetNextState(candidateUnit, _statisticsCalculator,_graphMetaData); if (nextCompnentStates.Count >= MaxStates) { if (maxIndex < 0) { var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } else { int compEdgeCount = newComponent.EdgeMaterilizedDict.Count; compEdgeCount = compEdgeCount == 0 ? 1 : compEdgeCount; if (newComponent.Cost/compEdgeCount < maxValue) { nextCompnentStates[maxIndex] = newComponent; var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } continue; } } nextCompnentStates.Add(newComponent); } } componentStates = nextCompnentStates; } return optimalFinalComponent; }
public override List <Tuple <MatchNode, MatchEdge> > GetOptimizedTraversalOrder(ConnectedComponent subGraph, out Dictionary <string, List <Tuple <MatchEdge, MaterializedEdgeType> > > nodeToMaterializedEdgesDict) { nodeToMaterializedEdgesDict = null; if (subGraph.Nodes.Count == 1) { return new List <Tuple <MatchNode, MatchEdge> > { new Tuple <MatchNode, MatchEdge>(subGraph.Nodes.First().Value, null), } } ; // If it exists, pick a node defined in the outer context as the start point var componentStates = subGraph.Nodes.Where(node => node.Value.IsFromOuterContext). Select(node => new MatchComponent(node.Value)).Take(1).ToList(); // Else, pick a node without incoming edges as the start point if (!componentStates.Any()) { componentStates = subGraph.Nodes.Where(node => node.Value.ReverseNeighbors.Count == 0). Select(node => new MatchComponent(node.Value)).Take(1).ToList(); } // Otherwise, pick a node randomly as the start point else if (!componentStates.Any()) { componentStates.Add(new MatchComponent(subGraph.Nodes.First().Value)); } // DP while (componentStates.Any()) { var nextCompnentStates = new List <MatchComponent>(); // Iterate on current components foreach (var curComponent in componentStates) { var nodeUnits = GetNodeUnits(subGraph, curComponent); if (nodeUnits == null && curComponent.ActiveNodeCount == subGraph.ActiveNodeCount && curComponent.EdgeMaterilizedDict.Count(e => e.Value == true) == subGraph.EdgeCount) { curComponent.TraversalChain.Reverse(); nodeToMaterializedEdgesDict = curComponent.NodeToMaterializedEdgesDict; return(curComponent.TraversalChain); } var candidateUnit = GetCandidateUnits(nodeUnits, curComponent); // Add it to the current component to generate next states var newComponent = GetNextState(curComponent, candidateUnit); if (nextCompnentStates.Count >= MaxStates) { throw new GraphViewException("This graph pattern is not supported yet."); } nextCompnentStates.Add(newComponent); } componentStates = nextCompnentStates; } return(null); }
/// <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; }
/// <summary> /// Get the optimal join component for the given connected graph /// 1. Generate the initial states /// 2. DP, Iterate on each states: /// Get smallest join units -> Enumerate on all possible combination of the join units /// -> Join to the current component to get the next states /// -> Those components with the largest average size per edge will be eliminate if exceeding the upper bound /// 3. If all the components has reached its end states, return the component with the smallest join cost /// </summary> /// <param name="subGraph"></param> /// <returns></returns> public MatchComponent ConstructComponent(ConnectedComponent subGraph) { var componentStates = new List<MatchComponent>(); var nodes = subGraph.Nodes; var edges = subGraph.Edges; int nodeCount = subGraph.IsTailNode.Count(e => !e.Value); MatchComponent finishedComponent = null; //Init int maxIndex = -1; double maxValue = Double.MinValue; foreach (var node in nodes) { if (!subGraph.IsTailNode[node.Value]) { // Enumerate on each edge for a node to generate the intial states var edgeCount = node.Value.Neighbors.Count; int eNum = (int) Math.Pow(2, edgeCount) - 1; while (eNum > 0) { var nodeInitialEdges = new List<MatchEdge>(); for (int i = 0; i < edgeCount; i++) { int index = (1 << i); if ((eNum & index) != 0) { nodeInitialEdges.Add(node.Value.Neighbors[i]); } } componentStates.Add(new MatchComponent(node.Value, nodeInitialEdges, _context)); eNum--; } } } // DP while (componentStates.Any()) { maxIndex = -1; var nextCompnentStates = new List<MatchComponent>(); // Iterate on current components foreach (var curComponent in componentStates) { var nodeUnits = GetNodeUnits(subGraph, curComponent).ToList(); if (!nodeUnits.Any()) { if (finishedComponent == null || curComponent.Cost < finishedComponent.Cost) { finishedComponent = curComponent; } continue; } var candidateUnits = _pruningStrategy.GetCandidateUnits(nodeUnits, curComponent); // Iterate on the candidate node units & add it to the current component to generate next states foreach (var candidateUnit in candidateUnits) { // Pre-filter if (finishedComponent != null && (curComponent.Size + candidateUnit.TreeRoot.EstimatedRows* candidateUnit.UnmaterializedEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next)* candidateUnit.MaterializedEdges.Select(e => e.AverageDegree) .Aggregate(1.0, (cur, next) => cur*next) > finishedComponent.Cost)) { continue; } // TODO : redundant work if newSize>maxvalue var newComponent = curComponent.GetNextState(candidateUnit, _tableIdDensity, _statisticsCalculator); if (nextCompnentStates.Count >= MaxStates) { if (maxIndex < 0) { var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } else { int edgeCount = newComponent.EdgeMaterilizedDict.Count; edgeCount = edgeCount == 0 ? 1 : edgeCount; if (newComponent.Cost / edgeCount < maxValue) { var temp = nextCompnentStates[maxIndex]; nextCompnentStates[maxIndex] = newComponent; var tuple = GetMostExpensiveMatchComponent(nextCompnentStates); maxIndex = tuple.Item1; maxValue = tuple.Item2; } continue; } } nextCompnentStates.Add(newComponent); } } componentStates = nextCompnentStates; } return finishedComponent; }
/// <summary> /// Get a full one height tree with joint edges and unmaterlized edges, /// returns a tuple whose first item is the one height tree and the second item /// indicates whether the one height tree only joins with the component's materialized /// node on its root. /// </summary> /// <param name="graph"></param> /// <param name="component"></param> /// <returns></returns> public IEnumerable<Tuple<OneHeightTree,bool>> GetNodeUnits(ConnectedComponent graph, MatchComponent component) { var nodes = graph.Nodes; foreach (var node in nodes.Values.Where(e => !graph.IsTailNode[e])) { //var newJoinUnitList = new List<MatchJoinUnit>() bool joint = false; var jointEdges = new List<MatchEdge>(); var nodeEdgeDict = node.Neighbors.ToDictionary(e => e, e => component.EdgeMaterilizedDict.ContainsKey(e)); // Edge to component node foreach (var edge in node.Neighbors.Where(e => !nodeEdgeDict[e])) { if (component.Nodes.Contains(edge.SinkNode)) { joint = true; nodeEdgeDict[edge] = true; jointEdges.Add(edge); } } // Component edge to node if (!joint && component.UnmaterializedNodeMapping.ContainsKey(node)) { joint = true; } // Add unpopulated edges var nodeUnpopulatedEdges = nodeEdgeDict.Where(e => !e.Value).Select(e => e.Key).ToList(); if (joint) yield return new Tuple<OneHeightTree, bool>(new OneHeightTree { TreeRoot = node, MaterializedEdges = jointEdges, UnmaterializedEdges = nodeUnpopulatedEdges, }, false); // Single node edge else if (nodeUnpopulatedEdges.Count > 0 && component.MaterializedNodeSplitCount.Count > 1 && component.MaterializedNodeSplitCount.ContainsKey(node)) { yield return new Tuple<OneHeightTree, bool>(new OneHeightTree { TreeRoot = node, MaterializedEdges = jointEdges, UnmaterializedEdges = nodeUnpopulatedEdges, }, true); } } }
/// <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; }
public override List <Tuple <MatchNode, MatchEdge, List <MatchEdge>, List <MatchEdge>, List <MatchEdge> > > GetOptimizedTraversalOrder(ConnectedComponent subGraph) { if (subGraph.Nodes.Count == 1) { return (this.GenerateTraversalOrderFromTraversalChain( new List <Tuple <MatchNode, MatchEdge, MatchNode, List <MatchEdge>, List <MatchEdge> > > { new Tuple <MatchNode, MatchEdge, MatchNode, List <MatchEdge>, List <MatchEdge> >( subGraph.Nodes.First().Value, null, null, null, null) })); } // If it exists, pick a node without incoming edges as the start point List <MatchComponent> componentStates = subGraph.Nodes.Where(node => node.Value.ReverseNeighbors.Count == 0). Select(node => new MatchComponent(node.Value)).Take(1).ToList(); // Otherwise, pick a node randomly as the start point if (!componentStates.Any()) { componentStates.Add(new MatchComponent(subGraph.Nodes.First().Value)); } // DP while (componentStates.Any()) { List <MatchComponent> nextCompnentStates = new List <MatchComponent>(); // Iterate on current components foreach (MatchComponent curComponent in componentStates) { OneHeightTree nodeUnits = this.GetNodeUnits(subGraph, curComponent); if (nodeUnits == null && curComponent.ActiveNodeCount == subGraph.ActiveNodeCount && curComponent.EdgeMaterilizedDict.Count(e => e.Value == true) == subGraph.Edges.Count(e => e.Value.IsDanglingEdge == false)) { return(this.GenerateTraversalOrderFromTraversalChain(curComponent.TraversalChain)); } CandidateJoinUnit candidateUnit = this.GetCandidateUnits2(nodeUnits, curComponent); // Add it to the current component to generate next states MatchComponent newComponent = this.GetNextState(curComponent, candidateUnit); if (nextCompnentStates.Count >= MaxStates) { throw new GraphViewException("This graph pattern is not supported yet."); } nextCompnentStates.Add(newComponent); } componentStates = nextCompnentStates; } return(null); }