/// <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>());
            }
        }