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;
}