Ejemplo n.º 1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="vertexObject"></param>
        /// <param name="edgeDocId"></param>
        /// <param name="isReverse"></param>
        /// <param name="newEdgeObject"></param>
        public static void UpdateEdgeProperty(
            GraphViewCommand command,
            JObject vertexObject,
            string edgeDocId,     // Can be null
            bool isReverse,
            JObject newEdgeObject // With all metadata (including id, partition, srcV/sinkV, edgeId)
            )
        {
            bool   tooLarge;
            string srcOrSinkVInEdgeObject = isReverse ? KW_EDGE_SRCV : KW_EDGE_SINKV;

            if (edgeDocId == null)
            {
                JArray edgeContainer = (JArray)vertexObject[isReverse ? KW_VERTEX_REV_EDGE : KW_VERTEX_EDGE];

                // Don't use JToken.Replace() here.
                // Make sure the currently modified edge is the last child of edgeContainer, which
                // garantees the newly created edge-document won't be too large.
                //
                // NOTE: The following line applies for both incomming and outgoing edge.
                edgeContainer.Children <JObject>().First(
                    e => (string)e[KW_EDGE_ID] == (string)newEdgeObject[KW_EDGE_ID] &&
                    (string)e[srcOrSinkVInEdgeObject] == (string)newEdgeObject[srcOrSinkVInEdgeObject]
                    ).Remove();
                edgeContainer.Add(newEdgeObject);

                UploadOne(command, (string)vertexObject[KW_DOC_ID], vertexObject, false, out tooLarge);
                if (tooLarge)
                {
                    // Handle this situation: The updated edge is too large to be filled into the vertex-document
                    string existEdgeDocId, newEdgeDocId;
                    bool?  spillReverse = null;
                    EdgeDocumentHelper.SpillVertexEdgesToDocument(command, vertexObject, ref spillReverse, out existEdgeDocId, out newEdgeDocId);
                }
            }
            else
            {
                // Large vertex

                JObject edgeDocObject = command.Connection.RetrieveDocumentById(edgeDocId, command.Connection.GetDocumentPartition(vertexObject), command);
                edgeDocObject[KW_EDGEDOC_EDGE].Children <JObject>().First(
                    e => (string)e[KW_EDGE_ID] == (string)newEdgeObject[KW_EDGE_ID] &&
                    (string)e[srcOrSinkVInEdgeObject] == (string)newEdgeObject[srcOrSinkVInEdgeObject]
                    ).Remove();
                ((JArray)edgeDocObject[KW_EDGEDOC_EDGE]).Add(newEdgeObject);
                UploadOne(command, edgeDocId, edgeDocObject, false, out tooLarge);
                if (tooLarge)
                {
                    if (command.Connection.EdgeSpillThreshold == 1)
                    {
                        throw new GraphViewException("The edge is too large to be stored in one document!");
                    }

                    // Handle this situation: The modified edge is too large to be filled into the original edge-document
                    // Remove the edgeObject added just now, and upload the original edge-document
                    ((JArray)edgeDocObject[KW_EDGEDOC_EDGE]).Last.Remove();
                    UploadOne(command, edgeDocId, edgeDocObject, false, out tooLarge);
                    Debug.Assert(!tooLarge);

                    // Insert the edgeObject to one of the vertex's edge-documents
                    InsertEdgeObjectInternal(command, vertexObject, null, newEdgeObject, isReverse, out edgeDocId);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Insert edgeObject to one a vertex.
        /// NOTE: vertex-document and edge-document(s) are uploaded.
        /// NOTE: If changing _edge/_reverse_edge field from JArray to JObject, the "EdgeDocId" of existing
        ///       edges in VertexCache are updated (from null to the newly created edge-document's id)
        /// NOTE: Adding the newly created edge into VertexCache is not operated by this function. Actually,
        ///       if called by <see cref="UpdateEdgeProperty"/>, VertexCache should be updated by setting an
        ///       edge's property, but not adding a new edge.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="vertexObject"></param>
        /// <param name="vertexField">Can be null if we already know edgeContainer is JObject</param>
        /// <param name="edgeObject"></param>
        /// <param name="isReverse"></param>
        /// <param name="newEdgeDocId"></param>
        internal static void InsertEdgeObjectInternal(
            GraphViewCommand command,
            JObject vertexObject,
            VertexField vertexField,
            JObject edgeObject,
            bool isReverse,
            out string newEdgeDocId)
        {
            bool   tooLarge;
            JArray edgeContainer = (JArray)vertexObject[isReverse ? KW_VERTEX_REV_EDGE : KW_VERTEX_EDGE]; // JArray or JObject
            bool   isSpilled     = IsSpilledVertex(vertexObject, isReverse);

            //
            // This graph is compatible only, thus add an edge-document directly
            //
            if (command.Connection.GraphType != GraphType.GraphAPIOnly || command.Connection.EdgeSpillThreshold == 1)
            {
                Debug.Assert(command.Connection.EdgeSpillThreshold == 1);

                // Create a new edge-document to store the edge.
                JObject edgeDocObject = new JObject {
                    [KW_DOC_ID]               = GraphViewConnection.GenerateDocumentId(),
                    [KW_EDGEDOC_ISREVERSE]    = isReverse,
                    [KW_EDGEDOC_VERTEXID]     = (string)vertexObject[KW_DOC_ID],
                    [KW_EDGEDOC_VERTEX_LABEL] = (string)vertexObject[KW_VERTEX_LABEL],
                    [KW_EDGEDOC_EDGE]         = new JArray(edgeObject),
                    [KW_EDGEDOC_IDENTIFIER]   = (JValue)true,
                };
                if (command.Connection.PartitionPathTopLevel != null)
                {
                    // This may be KW_DOC_PARTITION, maybe not
                    edgeDocObject[command.Connection.PartitionPathTopLevel] = vertexObject[command.Connection.PartitionPathTopLevel];
                }

                // Upload the edge-document
                bool dummyTooLarge;
                UploadOne(command, (string)edgeDocObject[KW_DOC_ID], edgeDocObject, true, out dummyTooLarge);
                Debug.Assert(!dummyTooLarge);


                newEdgeDocId = (string)edgeDocObject[KW_DOC_ID];
                return;
            }


            if (isSpilled)
            {
                // Now it is a large-degree vertex, and contains at least 1 edge-document
                JArray edgeDocumentsArray = edgeContainer;
                Debug.Assert(edgeDocumentsArray != null, "edgeDocumentsArray != null");
                Debug.Assert(edgeDocumentsArray.Count == 1, "edgeDocumentsArray.Count == 1");

                string  lastEdgeDocId = (string)edgeDocumentsArray.Last[KW_DOC_ID];
                JObject edgeDocument  = command.Connection.RetrieveDocumentById(lastEdgeDocId, vertexField.Partition, command);
                Debug.Assert(((string)edgeDocument[KW_DOC_ID]).Equals(lastEdgeDocId), $"((string)edgeDocument[{KW_DOC_ID}]).Equals(lastEdgeDocId)");
                Debug.Assert((bool)edgeDocument[KW_EDGEDOC_ISREVERSE] == isReverse, $"(bool)edgeDocument['{KW_EDGEDOC_ISREVERSE}'] == isReverse");
                Debug.Assert((string)edgeDocument[KW_EDGEDOC_VERTEXID] == (string)vertexObject[KW_DOC_ID], $"(string)edgeDocument['{KW_EDGEDOC_VERTEXID}'] == (string)vertexObject['{KW_DOC_ID}']");

                JArray edgesArray = (JArray)edgeDocument[KW_EDGEDOC_EDGE];
                Debug.Assert(edgesArray != null, "edgesArray != null");
                Debug.Assert(edgesArray.Count > 0, "edgesArray.Count > 0");

                if (command.Connection.EdgeSpillThreshold == 0)
                {
                    // Don't spill an edge-document until it is too large
                    edgesArray.Add(edgeObject);
                    tooLarge = false;
                }
                else
                {
                    // Explicitly specified a threshold
                    Debug.Assert(command.Connection.EdgeSpillThreshold > 0, "connection.EdgeSpillThreshold > 0");
                    if (edgesArray.Count >= command.Connection.EdgeSpillThreshold)
                    {
                        // The threshold is reached!
                        tooLarge = true;
                    }
                    else
                    {
                        // The threshold is not reached
                        edgesArray.Add(edgeObject);
                        tooLarge = false;
                    }
                }

                // If the edge-document is not too large (reach the threshold), try to
                //   upload the edge into the document
                if (!tooLarge)
                {
                    UploadOne(command, lastEdgeDocId, edgeDocument, false, out tooLarge);
                }
                if (tooLarge)
                {
                    // The edge is too large to be filled into the last edge-document
                    // or the threashold is reached:
                    // Create a new edge-document to store the edge.
                    JObject edgeDocObject = new JObject {
                        [KW_DOC_ID]               = GraphViewConnection.GenerateDocumentId(),
                        [KW_EDGEDOC_ISREVERSE]    = isReverse,
                        [KW_EDGEDOC_VERTEXID]     = (string)vertexObject[KW_DOC_ID],
                        [KW_EDGEDOC_VERTEX_LABEL] = (string)vertexObject[KW_VERTEX_LABEL],
                        [KW_EDGEDOC_EDGE]         = new JArray(edgeObject),
                        [KW_EDGEDOC_IDENTIFIER]   = (JValue)true,
                    };
                    if (command.Connection.PartitionPathTopLevel != null)
                    {
                        // This may be KW_DOC_PARTITION, maybe not
                        edgeDocObject[command.Connection.PartitionPathTopLevel] = vertexObject[command.Connection.PartitionPathTopLevel];
                    }
                    lastEdgeDocId = command.Connection.CreateDocumentAsync(edgeDocObject, command).Result;

                    // Replace the newly created edge-document to vertexObject
                    Debug.Assert(edgeDocumentsArray.Count == 1);
                    edgeDocumentsArray[0][KW_DOC_ID] = lastEdgeDocId;
                }
                newEdgeDocId = lastEdgeDocId;

                // Upload the vertex documention (at least, its _nextXxx is changed)
                bool dummyTooLarge;
                UploadOne(command, (string)vertexObject[KW_DOC_ID], vertexObject, false, out dummyTooLarge);
                Debug.Assert(!dummyTooLarge);
            }
            else
            {
                // This vertex is not spilled
                bool?spillReverse;
                ((JArray)edgeContainer).Add(edgeObject);
                if (command.Connection.EdgeSpillThreshold == 0)
                {
                    // Don't spill an edge-document until it is too large
                    tooLarge     = false;
                    spillReverse = null;
                }
                else
                {
                    // Explicitly specified a threshold
                    Debug.Assert(command.Connection.EdgeSpillThreshold > 0, "connection.EdgeSpillThreshold > 0");
                    tooLarge     = (((JArray)edgeContainer).Count > command.Connection.EdgeSpillThreshold);
                    spillReverse = isReverse;
                }

                if (!tooLarge)
                {
                    UploadOne(command, (string)vertexObject[KW_DOC_ID], vertexObject, false, out tooLarge);
                }
                if (tooLarge)
                {
                    string existEdgeDocId;
                    // The vertex object is uploaded in SpillVertexEdgesToDocument
                    EdgeDocumentHelper.SpillVertexEdgesToDocument(command, vertexObject, ref spillReverse, out existEdgeDocId, out newEdgeDocId);

                    // the edges are spilled into two ducuments.
                    // one stores old edges(docId = existEdgeDocId), the other one stores the new edge.
                    // Because the new edge is not in the vertexCache, hence we can set all edges' docId as existEdgeDocId
                    Debug.Assert(spillReverse != null);
                    Debug.Assert(vertexField != null);
                    if (spillReverse.Value)
                    {
                        vertexField.RevAdjacencyList.ResetFetchedEdgesDocId(existEdgeDocId);
                    }
                    else
                    {
                        vertexField.AdjacencyList.ResetFetchedEdgesDocId(existEdgeDocId);
                    }
                }
                else
                {
                    newEdgeDocId = null;
                }
            }
        }