// handleNeighbors returns the vertices and edges connected to the provided vertex // (Should I protect this transaction like the update weight function? It is probably // unnecessary because any update based on retrieved information will be written to a // property which has transactional protection) func (d *Data) handleNeighbors(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, path []string) error { labelgraph := new(LabelGraph) temp, err := strconv.Atoi(path[0]) if err != nil { return fmt.Errorf("Vertex number not provided") } id := dvid.VertexID(temp) storedvert, err := db.GetVertex(ctx, id) if err != nil { return fmt.Errorf("Failed to retrieve vertix %d: %v\n", id, err) } labelgraph.Vertices = append(labelgraph.Vertices, labelVertex{storedvert.Id, storedvert.Weight}) for _, vert2 := range storedvert.Vertices { vertex, err := db.GetVertex(ctx, vert2) if err != nil { return fmt.Errorf("Failed to retrieve vertex %d: %v\n", vertex.Id, err) } labelgraph.Vertices = append(labelgraph.Vertices, labelVertex{vertex.Id, vertex.Weight}) edge, err := db.GetEdge(ctx, id, vert2) if err != nil { return fmt.Errorf("Failed to retrieve edge %d-%d: %v\n", vertex.Id, vert2, err) } labelgraph.Edges = append(labelgraph.Edges, labelEdge{edge.Vertexpair.Vertex1, edge.Vertexpair.Vertex2, edge.Weight}) } m, err := json.Marshal(labelgraph) if err != nil { return fmt.Errorf("Could not serialize graph") } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(m)) return nil }
// 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 }
// handleSubgraph loads, retrieves, or deletes a subgraph (more description in REST interface) func (d *Data) handleSubgraphBulk(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, labelgraph *LabelGraph, method string) error { var err error if !d.setBusy() { return fmt.Errorf("Server busy with bulk transaction") } defer d.setNotBusy() // initial new graph labelgraph2 := new(LabelGraph) labelgraph2.Transactions = make([]transactionItem, 0) labelgraph2.Vertices = make([]labelVertex, 0) labelgraph2.Edges = make([]labelEdge, 0) // ?! do not grab edges that connect to outside vertices if method == "get" { var vertices []dvid.GraphVertex var edges []dvid.GraphEdge if len(labelgraph.Vertices) > 0 { used_vertices := make(map[dvid.VertexID]struct{}) for _, vertex := range labelgraph.Vertices { used_vertices[vertex.Id] = struct{}{} storedvert, err := db.GetVertex(ctx, vertex.Id) if err != nil { return fmt.Errorf("Failed to retrieve vertix %d: %v\n", vertex.Id, err) } vertices = append(vertices, storedvert) for _, vert2 := range storedvert.Vertices { if _, ok := used_vertices[vert2]; !ok { edge, err := db.GetEdge(ctx, storedvert.Id, vert2) if err != nil { return fmt.Errorf("Failed to retrieve edge %d-%d: %v\n", storedvert.Id, vert2, err) } edges = append(edges, edge) } } } } else { // if no set of vertices are supplied, just grab the whole graph vertices, err = db.GetVertices(ctx) if err != nil { return fmt.Errorf("Failed to retrieve vertices: %v\n", err) } edges, err = db.GetEdges(ctx) if err != nil { return fmt.Errorf("Failed to retrieve edges: %v\n", err) } } for _, vertex := range vertices { labelgraph2.Vertices = append(labelgraph2.Vertices, labelVertex{vertex.Id, vertex.Weight}) } for _, edge := range edges { labelgraph2.Edges = append(labelgraph2.Edges, labelEdge{edge.Vertexpair.Vertex1, edge.Vertexpair.Vertex2, edge.Weight}) } m, err := json.Marshal(labelgraph2) if err != nil { return fmt.Errorf("Could not serialize graph") } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(m)) } else if method == "post" { // add list of vertices and edges from supplied JSON -- overwrite existing values for _, vertex := range labelgraph.Vertices { err := db.AddVertex(ctx, vertex.Id, vertex.Weight) if err != nil { return fmt.Errorf("Failed to add vertex: %v\n", err) } } for _, edge := range labelgraph.Edges { err := db.AddEdge(ctx, edge.Id1, edge.Id2, edge.Weight) if err != nil { return fmt.Errorf("Failed to add edge: %v\n", err) } } } else if method == "delete" { // delete the vertices supplied and all of their edges or delete the whole graph if len(labelgraph.Vertices) > 0 || len(labelgraph.Edges) > 0 { for _, vertex := range labelgraph.Vertices { db.RemoveVertex(ctx, vertex.Id) } for _, edge := range labelgraph.Edges { db.RemoveEdge(ctx, edge.Id1, edge.Id2) } } else { err = db.RemoveGraph(ctx) if err != nil { return fmt.Errorf("Failed to remove graph: %v\n", err) } } } else { err = fmt.Errorf("Does not support PUT") } return err }
// handleProperty retrieves or deletes properties that can be added to a vertex or edge -- data posted // or retrieved uses default compression func (d *Data) handleProperty(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, r *http.Request, path []string, method string) error { edgemode := false var propertyname string if len(path) == 3 { edgemode = true propertyname = path[2] } else if len(path) != 2 { return fmt.Errorf("Incorrect number of parameters specified for handling properties") } else { propertyname = path[1] } temp, err := strconv.Atoi(path[0]) if err != nil { return fmt.Errorf("Vertex number not provided") } id1 := dvid.VertexID(temp) id2 := dvid.VertexID(0) if edgemode { temp, err := strconv.Atoi(path[1]) if err != nil { return fmt.Errorf("Vertex number not provided") } id2 = dvid.VertexID(temp) } // remove a property from a vertex or edge if method == "delete" { if edgemode { db.RemoveEdgeProperty(ctx, id1, id2, propertyname) if err != nil { return fmt.Errorf("Failed to remove edge property %d-%d %s: %v\n", id1, id2, propertyname, err) } } else { db.RemoveVertexProperty(ctx, id1, propertyname) if err != nil { return fmt.Errorf("Failed to remove vertex property %d %s: %v\n", id1, propertyname, err) } } } else if method == "get" { var data []byte if edgemode { data, err = db.GetEdgeProperty(ctx, id1, id2, propertyname) } else { data, err = db.GetVertexProperty(ctx, id1, propertyname) } if err != nil { return fmt.Errorf("Failed to get property %s: %v\n", propertyname, err) } uncompress := true value, _, e := dvid.DeserializeData(data, uncompress) if e != nil { err = fmt.Errorf("Unable to deserialize data for property '%s': %v\n", propertyname, e.Error()) return err } w.Header().Set("Content-Type", "application/octet-stream") _, err = w.Write(value) if err != nil { return err } } else if method == "post" { // read as binary and load into propertyname data, err := ioutil.ReadAll(r.Body) if err != nil { return err } serialization, err := dvid.SerializeData(data, d.Compression(), d.Checksum()) if err != nil { return fmt.Errorf("Unable to serialize data: %v\n", err) } if edgemode { err = db.SetEdgeProperty(ctx, id1, id2, propertyname, serialization) } else { err = db.SetVertexProperty(ctx, id1, propertyname, serialization) } if err != nil { return fmt.Errorf("Failed to add property %s: %v\n", propertyname, err) } } return err }
// handelPropertyTransaction allows gets/posts (really puts) of edge or vertex properties. func (d *Data) handlePropertyTransaction(ctx *datastore.VersionedCtx, db storage.GraphDB, w http.ResponseWriter, r *http.Request, path []string, method string) error { if len(path) < 2 { return fmt.Errorf("Must specify edges or vertices in URI and property name") } if method == "delete" { return fmt.Errorf("Transactional delete not supported") } edgemode := false if path[0] == "edges" { edgemode = true } else if path[0] != "vertices" { return fmt.Errorf("Must specify edges or vertices in URI") } propertyname := path[1] readonly := false if method == "get" { readonly = true } data, err := ioutil.ReadAll(r.Body) // only allow 1000 vertices to be locked transactions, start, err := d.transaction_log.createTransactionGroupBinary(data, readonly) defer transactions.closeTransaction() if err != nil { return fmt.Errorf("Failed to create property transaction: %v", err) } returned_data := transactions.exportTransactionsBinary() if method == "post" { // deserialize transaction (vertex or edge) -- use URI? num_properties := binary.LittleEndian.Uint64(data[start:]) start += 8 for i := uint64(0); i < num_properties; i++ { temp := binary.LittleEndian.Uint64(data[start:]) id := dvid.VertexID(temp) var id2 dvid.VertexID start += 8 if edgemode { temp = binary.LittleEndian.Uint64(data[start:]) id2 = dvid.VertexID(temp) start += 8 } data_size := binary.LittleEndian.Uint64(data[start:]) start += 8 data_begin := start start += int(data_size) data_end := start if data_begin == data_end { continue } // check if post is possible if _, ok := transactions.locked_ids[id]; !ok { continue } if edgemode { if _, ok := transactions.locked_ids[id2]; !ok { continue } } // execute post serialization, err := dvid.SerializeData(data[data_begin:data_end], d.Compression(), d.Checksum()) if err != nil { return fmt.Errorf("Unable to serialize data: %v\n", err) } if edgemode { err = db.SetEdgeProperty(ctx, id, id2, propertyname, serialization) } else { err = db.SetVertexProperty(ctx, id, propertyname, serialization) } if err != nil { return fmt.Errorf("Failed to add property %s: %v\n", propertyname, err) } } } else { num_properties := binary.LittleEndian.Uint64(data[start:]) start += 8 num_properties_loc := len(returned_data) longbuf := make([]byte, 8, 8) binary.LittleEndian.PutUint64(longbuf, 0) returned_data = append(returned_data, longbuf...) num_executed_transactions := uint64(0) // read the vertex or edge properties desired for i := uint64(0); i < num_properties; i++ { temp := binary.LittleEndian.Uint64(data[start:]) id := dvid.VertexID(temp) var id2 dvid.VertexID start += 8 if edgemode { temp := binary.LittleEndian.Uint64(data[start:]) id2 = dvid.VertexID(temp) start += 8 } // check if post is possible if _, ok := transactions.locked_ids[id]; !ok { continue } if edgemode { if _, ok := transactions.locked_ids[id2]; !ok { continue } } // execute get command var dataout []byte if edgemode { dataout, err = db.GetEdgeProperty(ctx, id, id2, propertyname) } else { dataout, err = db.GetVertexProperty(ctx, id, propertyname) } // serialize return data only if there is return data and no error; // otherwise return just return the id and size of 0 var data_serialized []byte if (err == nil) && len(dataout) > 0 { uncompress := true data_serialized, _, err = dvid.DeserializeData(dataout, uncompress) if err != nil { return fmt.Errorf("Unable to deserialize data for property '%s': %v\n", propertyname, err) } } // save transaction num_executed_transactions += 1 binary.LittleEndian.PutUint64(longbuf, uint64(id)) returned_data = append(returned_data, longbuf...) if edgemode { binary.LittleEndian.PutUint64(longbuf, uint64(id2)) returned_data = append(returned_data, longbuf...) } binary.LittleEndian.PutUint64(longbuf, uint64(len(data_serialized))) returned_data = append(returned_data, longbuf...) returned_data = append(returned_data, data_serialized...) } // update the number of transactions binary.LittleEndian.PutUint64(returned_data[num_properties_loc:], num_executed_transactions) } w.Header().Set("Content-Type", "application/octet-stream") _, err = w.Write(returned_data) return err }