/// <summary> /// For every vertex in the vertexIdSet, retrieve their spilled edge documents to construct their forward or backward /// adjacency list. /// When connection.useReverseEdge is false, this method will retrieve all the forward edge documents whose sink /// is the target vertex to build a virtual reverse adjacency list. /// </summary> /// <param name="command"></param> /// <param name="edgeType"></param> /// <param name="vertexIdSet"></param> /// <param name="vertexPartitionKeySet"></param> public static void ConstructLazyAdjacencyList( GraphViewCommand command, EdgeType edgeType, HashSet <string> vertexIdSet, HashSet <string> vertexPartitionKeySet) { if (!vertexIdSet.Any()) { return; } const string ALIAS = "edgeDoc"; var queryBoth = new JsonQuery { EdgeAlias = ALIAS }; queryBoth.AddSelectElement("*"); queryBoth.RawWhereClause = new WInPredicate(new WColumnReferenceExpression(ALIAS, KW_EDGEDOC_VERTEXID), vertexIdSet.ToList()); queryBoth.FlatProperties.Add(KW_EDGEDOC_VERTEXID); if (vertexPartitionKeySet.Any()) { // TODO: Refactor this. queryBoth.WhereConjunction(new WInPredicate(new WValueExpression($"{ALIAS}{command.Connection.GetPartitionPathIndexer()}"), vertexPartitionKeySet.ToList()), BooleanBinaryExpressionType.And); } if (edgeType == EdgeType.Outgoing) { queryBoth.WhereConjunction(new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression(ALIAS, KW_EDGEDOC_ISREVERSE), SecondExpr = new WValueExpression("false") }, BooleanBinaryExpressionType.And); queryBoth.FlatProperties.Add(KW_EDGEDOC_ISREVERSE); } else if (edgeType == EdgeType.Incoming) { queryBoth.WhereConjunction(new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression(ALIAS, KW_EDGEDOC_ISREVERSE), SecondExpr = new WValueExpression("true") }, BooleanBinaryExpressionType.And); queryBoth.FlatProperties.Add(KW_EDGEDOC_ISREVERSE); } // var qqq = queryBoth.ToJsonServerString(); List <JObject> edgeDocuments = command.Connection.CreateDatabasePortal().GetEdgeDocuments(queryBoth); // Dictionary<vertexId, Dictionary<edgeDocumentId, edgeDocument>> var edgeDict = new Dictionary <string, Dictionary <string, JObject> >(); foreach (JObject edgeDocument in edgeDocuments) { // Save edgeDocument's etag if necessary command.VertexCache.SaveCurrentEtagNoOverride(edgeDocument); } EdgeDocumentHelper.FillEdgeDict(edgeDict, edgeDocuments); // // Use all edges whose sink is vertexId to construct a virtual reverse adjacency list of this vertex // if (!command.Connection.UseReverseEdges && edgeType.HasFlag(EdgeType.Incoming)) { // TODO: old Version JsonQuery, delete it when you understand this query. // string selectClause = $"{{" + // $" \"{EdgeDocumentHelper.VirtualReverseEdgeObject}\": edge, " + // $" \"{KW_EDGE_SRCV}\": doc.{KW_DOC_ID}, " + // $" \"{KW_EDGE_SRCV_LABEL}\": doc.{KW_VERTEX_LABEL}," + // (command.Connection.PartitionPath != null // ? $" \"{KW_EDGE_SRCV_PARTITION}\": doc{command.Connection.GetPartitionPathIndexer()}," // : "") + // $" \"{KW_EDGEDOC_VERTEXID}\": doc.{KW_EDGEDOC_VERTEXID}," + // $" \"{KW_EDGEDOC_VERTEX_LABEL}\": doc.{KW_EDGEDOC_VERTEX_LABEL}" + // $"}} AS {EdgeDocumentHelper.VirtualReverseEdge}"; // string alise = "doc"; // string joinClause = $"JOIN edge IN doc.{DocumentDBKeywords.KW_VERTEX_EDGE}"; // string inClause = string.Join(", ", vertexIdSet.Select(vertexId => $"'{vertexId}'")); // string whereSearchCondition = $"edge.{KW_EDGE_SINKV} IN ({inClause})"; const string NODE_ALISE_S = "node"; const string EDGE_ALISE_S = "edge"; var queryReversed = new JsonQuery { NodeAlias = NODE_ALISE_S, EdgeAlias = EDGE_ALISE_S }; // Construct select clause. List <WPrimaryExpression> selectList = new List <WPrimaryExpression> { new WValueExpression($"{{ \"{EdgeDocumentHelper.VirtualReverseEdgeObject}\": "), new WColumnReferenceExpression(EDGE_ALISE_S, "*"), new WValueExpression($", \"{KW_EDGE_SRCV}\": "), new WColumnReferenceExpression(NODE_ALISE_S, KW_DOC_ID), new WValueExpression($", \"{KW_EDGE_SRCV_LABEL}\": "), new WColumnReferenceExpression(NODE_ALISE_S, KW_VERTEX_LABEL), new WValueExpression($", \"{KW_EDGEDOC_VERTEXID}\": "), new WColumnReferenceExpression(NODE_ALISE_S, KW_EDGEDOC_VERTEXID), new WValueExpression($", \"{KW_EDGEDOC_VERTEX_LABEL}\": "), new WColumnReferenceExpression(NODE_ALISE_S, KW_EDGEDOC_VERTEX_LABEL), }; if (command.Connection.PartitionPath != null) { selectList.Add(new WValueExpression($", \"{KW_EDGE_SRCV_PARTITION}\": ")); // TODO: hack operation, when meet columnName[0] = '[', the toDocDbString function will do something special. selectList.Add(new WColumnReferenceExpression(NODE_ALISE_S, command.Connection.GetPartitionPathIndexer())); } selectList.Add(new WValueExpression("}")); queryReversed.AddSelectElement(EdgeDocumentHelper.VirtualReverseEdge, selectList); // Construct join clause queryReversed.JoinDictionary.Add(EDGE_ALISE_S, $"{NODE_ALISE_S}.{DocumentDBKeywords.KW_VERTEX_EDGE}"); // construct where clause queryReversed.RawWhereClause = new WInPredicate( new WColumnReferenceExpression(EDGE_ALISE_S, KW_EDGE_SINKV), new List <string>(vertexIdSet)); edgeDocuments = command.Connection.CreateDatabasePortal().GetEdgeDocuments(queryReversed); List <JObject> virtualReverseEdgeDocuments = EdgeDocumentHelper.ConstructVirtualReverseEdgeDocuments(edgeDocuments); EdgeDocumentHelper.FillEdgeDict(edgeDict, virtualReverseEdgeDocuments); } foreach (KeyValuePair <string, Dictionary <string, JObject> > pair in edgeDict) { string vertexId = pair.Key; Dictionary <string, JObject> edgeDocDict = pair.Value; // contains both in & out edges VertexField vertexField; command.VertexCache.TryGetVertexField(vertexId, out vertexField); vertexField.ConstructSpilledOrVirtualAdjacencyListField(edgeType, edgeDocDict); vertexIdSet.Remove(vertexId); } foreach (string vertexId in vertexIdSet) { VertexField vertexField; command.VertexCache.TryGetVertexField(vertexId, out vertexField); vertexField.ConstructSpilledOrVirtualAdjacencyListField(edgeType, new Dictionary <string, JObject>()); } }