// handleMerge merges a list of vertices onto the final vertex in the Vertices list func (d *Data) handleMerge(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, labelgraph *LabelGraph) error { numverts := len(labelgraph.Vertices) if numverts < 2 { return fmt.Errorf("Must specify at least two vertices for merging") } overlapweights := make(map[dvid.VertexID]float64) vertweight := float64(0) var keepvertex dvid.GraphVertex allverts := make(map[dvid.VertexID]struct{}) keepverts := make(map[dvid.VertexID]struct{}) // accumulate weights, find common edges for i, vertex := range labelgraph.Vertices { vert, err := db.GetVertex(ctx, vertex.Id) if err != nil { return fmt.Errorf("Failed to retrieve vertex %d: %v\n", vertex.Id, err) } allverts[vert.Id] = struct{}{} vertweight += vert.Weight if i == (numverts - 1) { keepvertex = vert } else { for _, vert2 := range vert.Vertices { edge, err := db.GetEdge(ctx, vert.Id, vert2) if err != nil { return fmt.Errorf("Failed to retrieve edge %d-%d: %v\n", vertex.Id, vert2, err) } overlapweights[vert2] += edge.Weight } } } // examine keep vertex (save edges so only the weights are updated) for _, vert2 := range keepvertex.Vertices { edge, err := db.GetEdge(ctx, keepvertex.Id, vert2) if err != nil { return fmt.Errorf("Failed to retrieve edge %d-%d: %v\n", keepvertex.Id, vert2, err) } overlapweights[vert2] += edge.Weight keepverts[vert2] = struct{}{} } // use specified weights even if marked as 0 for _, edge := range labelgraph.Edges { id := edge.Id1 baseid := edge.Id2 if keepvertex.Id == edge.Id1 { id = edge.Id2 baseid = edge.Id1 } if baseid == keepvertex.Id { if _, ok := overlapweights[id]; ok { overlapweights[id] = edge.Weight } } } // if user specifies an edge weight other than 0 (?! -- somehow allow in future) // use that weight if labelgraph.Vertices[numverts-1].Weight != 0 { vertweight = labelgraph.Vertices[numverts-1].Weight } for id2, newweight := range overlapweights { if _, ok := allverts[id2]; !ok { // only examine edges where the node is not internal if _, ok := keepverts[id2]; !ok { // if not in keepverts create new edge err := db.AddEdge(ctx, keepvertex.Id, id2, newweight) if err != nil { return fmt.Errorf("Failed to create edge %d-%d: %v\n", keepvertex.Id, id2, err) } } else { // else just update weight err := db.SetEdgeWeight(ctx, keepvertex.Id, id2, newweight) if err != nil { return fmt.Errorf("Failed to update weight on edge %d-%d: %v\n", keepvertex.Id, id2, err) } } } } // update vertex weight err := db.SetVertexWeight(ctx, labelgraph.Vertices[numverts-1].Id, vertweight) if err != nil { return fmt.Errorf("Failed to update weight on vertex %d: %v\n", keepvertex.Id, err) } // remove old vertices which will remove the old edges for i, vertex := range labelgraph.Vertices { if i == (numverts - 1) { break } err := db.RemoveVertex(ctx, vertex.Id) if err != nil { return fmt.Errorf("Failed to remove vertex %d: %v\n", vertex.Id, err) } } return nil }
// handleWeightUpdate POST vertex/edge weight increment/decrement. POSTing to an uncreated // node or edge will create the node or edge (default 0 weight). Limit of 1000 vertices and 1000 edges. func (d *Data) handleWeightUpdate(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, labelgraph *LabelGraph) error { // collect all vertices that need to be locked and wrap in transaction ("read only") open_vertices := d.extractOpenVertices(labelgraph) transaction_group, err := d.transaction_log.createTransactionGroup(open_vertices, true) if err != nil { transaction_group.closeTransaction() return fmt.Errorf("Could not create transaction group") } leftover := true // iterate until leftovers are empty for leftover { if len(transaction_group.lockedold_ids) == 0 { leftover = false } for i, vertex := range labelgraph.Vertices { // if it was already examined, continue if vertex.Id == 0 { continue } // if it the vertex was not locked, continue if _, ok := transaction_group.locked_ids[vertex.Id]; !ok { continue } // retrieve vertex, update or create depending on error status storedvert, err := db.GetVertex(ctx, vertex.Id) if err != nil { err = db.AddVertex(ctx, vertex.Id, vertex.Weight) if err != nil { transaction_group.closeTransaction() return fmt.Errorf("Failed to add vertex: %v\n", err) } } else { // increment/decrement weight err = db.SetVertexWeight(ctx, vertex.Id, storedvert.Weight+vertex.Weight) if err != nil { transaction_group.closeTransaction() return fmt.Errorf("Failed to add vertex: %v\n", err) } } // do not revisit labelgraph.Vertices[i].Id = 0 } for i, edge := range labelgraph.Edges { // if it was already examined, continue if edge.Id1 == 0 { continue } // if it the edge was not locked, continue if _, ok := transaction_group.locked_ids[edge.Id1]; !ok { continue } if _, ok := transaction_group.locked_ids[edge.Id2]; !ok { continue } // retrieve edge, update or create depending on error status storededge, err := db.GetEdge(ctx, edge.Id1, edge.Id2) if err != nil { err = db.AddEdge(ctx, edge.Id1, edge.Id2, edge.Weight) if err != nil { transaction_group.closeTransaction() return fmt.Errorf("Failed to add edge: %v\n", err) } } else { // increment/decrement weight err = db.SetEdgeWeight(ctx, edge.Id1, edge.Id2, storededge.Weight+edge.Weight) if err != nil { transaction_group.closeTransaction() return fmt.Errorf("Failed to update edge: %v\n", err) } } // do not revisit labelgraph.Edges[i].Id1 = 0 } if leftover { // wait on update to channel if there are leftovers transaction_group.waitForChange() // close transaction, create new transaction from leftovers in labelgraph transaction_group.closeTransaction() open_vertices = d.extractOpenVertices(labelgraph) transaction_group, _ = d.transaction_log.createTransactionGroup(open_vertices, true) } } transaction_group.closeTransaction() return nil }