// SourceFromEntries returns a new Source from the given a set of entries with // the same source VName. func SourceFromEntries(entries []*spb.Entry) *Source { if len(entries) == 0 { return nil } src := &Source{ Ticket: kytheuri.ToString(entries[0].Source), Facts: make(map[string][]byte), Edges: make(map[string][]string), } edgeTargets := make(map[string]stringset.Set) for _, e := range entries { if graphstore.IsEdge(e) { tgts, ok := edgeTargets[e.EdgeKind] if !ok { tgts = stringset.New() edgeTargets[e.EdgeKind] = tgts } tgts.Add(kytheuri.ToString(e.Target)) } else { src.Facts[e.FactName] = e.FactValue } } for kind, targets := range edgeTargets { src.Edges[kind] = targets.Slice() sort.Strings(src.Edges[kind]) } return src }
// EnsureReverseEdges checks if gs contains reverse edges. If it doesn't, it // will scan gs for all forward edges, adding a reverse for each back into the // GraphStore. This is necessary for a GraphStoreService to work properly. func EnsureReverseEdges(ctx context.Context, gs graphstore.Service) error { var edge *spb.Entry if err := gs.Scan(ctx, &spb.ScanRequest{}, func(e *spb.Entry) error { if graphstore.IsEdge(e) { edge = e return io.EOF } return nil }); err != nil { return err } if edge == nil { log.Println("No edges found in GraphStore") return nil } else if schema.EdgeDirection(edge.EdgeKind) == schema.Reverse { return nil } var foundReverse bool if err := gs.Read(ctx, &spb.ReadRequest{ Source: edge.Target, EdgeKind: schema.MirrorEdge(edge.EdgeKind), }, func(entry *spb.Entry) error { foundReverse = true return nil }); err != nil { return fmt.Errorf("error checking for reverse edge: %v", err) } if foundReverse { return nil } return addReverseEdges(ctx, gs) }
// AppendEntry adds the given Entry to the Source's facts or edges. It is // assumed that src.Ticket == kytheuri.ToString(e.Source). func AppendEntry(src *ipb.Source, e *spb.Entry) { if graphstore.IsEdge(e) { kind, ordinal, _ := edges.ParseOrdinal(e.EdgeKind) group, ok := src.EdgeGroups[kind] if !ok { group = &ipb.Source_EdgeGroup{} src.EdgeGroups[kind] = group } ticket := kytheuri.ToString(e.Target) ord := int32(ordinal) for _, edge := range group.Edges { if edge.Ticket == ticket && edge.Ordinal == ord { // Don't add duplicate edge return } } group.Edges = append(group.Edges, &ipb.Source_Edge{ Ticket: ticket, Ordinal: ord, }) } else { src.Facts[e.FactName] = e.FactValue } }
// NodeFact returns a Node_Fact from the given Entry. If e == nil or e is not a // node fact, nil is returned. func NodeFact(e *spb.Entry) *srvpb.Node_Fact { if e == nil || graphstore.IsEdge(e) { return nil } return &srvpb.Node_Fact{ Name: e.FactName, Value: e.FactValue, } }
// getEdges returns edgeTargets with the given node as their source. Only edge // entries that return true when applied to pred are returned. func getEdges(ctx context.Context, gs graphstore.Service, node *spb.VName, pred func(*spb.Entry) bool) ([]*edgeTarget, error) { var targets []*edgeTarget if err := gs.Read(ctx, &spb.ReadRequest{ Source: node, EdgeKind: "*", }, func(entry *spb.Entry) error { if graphstore.IsEdge(entry) && pred(entry) { targets = append(targets, &edgeTarget{entry.EdgeKind, entry.Target}) } return nil }); err != nil { return nil, fmt.Errorf("read error: %v", err) } return targets, nil }
// toTriple converts an Entry to the triple file format. Returns an error if // the entry is not valid. func toTriple(entry *spb.Entry) (*rdf.Triple, error) { if err := graphstore.ValidEntry(entry); err != nil { return nil, fmt.Errorf("invalid entry {%+v}: %v", entry, err) } t := &rdf.Triple{ Subject: kytheuri.FromVName(entry.Source).String(), } if graphstore.IsEdge(entry) { t.Predicate = entry.EdgeKind t.Object = kytheuri.FromVName(entry.Target).String() } else { t.Predicate = entry.FactName t.Object = string(entry.FactValue) } return t, nil }