// Nodes implements part of the Service interface. func (g *GraphStoreService) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { patterns := xrefs.ConvertFilters(req.Filter) var names []*spb.VName for _, ticket := range req.Ticket { name, err := kytheuri.ToVName(ticket) if err != nil { return nil, err } names = append(names, name) } nodes := make(map[string]*xpb.NodeInfo) for i, vname := range names { ticket := req.Ticket[i] info := &xpb.NodeInfo{Facts: make(map[string][]byte)} if err := g.gs.Read(ctx, &spb.ReadRequest{Source: vname}, func(entry *spb.Entry) error { if len(patterns) == 0 || xrefs.MatchesAny(entry.FactName, patterns) { info.Facts[entry.FactName] = entry.FactValue } return nil }); err != nil { return nil, err } if len(info.Facts) > 0 { nodes[ticket] = info } } return &xpb.NodesReply{Nodes: nodes}, nil }
// Nodes implements part of the xrefs Service interface. func (t *tableImpl) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { reply := &xpb.NodesReply{} patterns := xrefs.ConvertFilters(req.Filter) for _, rawTicket := range req.Ticket { ticket, err := kytheuri.Fix(rawTicket) if err != nil { return nil, fmt.Errorf("invalid ticket %q: %v", rawTicket, err) } n, err := t.node(ctx, ticket) if err == table.ErrNoSuchKey { continue } else if err != nil { return nil, fmt.Errorf("lookup error for node %q: %v", ticket, err) } ni := &xpb.NodeInfo{Ticket: n.Ticket} for _, fact := range n.Fact { if len(patterns) == 0 || xrefs.MatchesAny(fact.Name, patterns) { ni.Fact = append(ni.Fact, &xpb.Fact{Name: fact.Name, Value: fact.Value}) } } if len(ni.Fact) > 0 { reply.Node = append(reply.Node, ni) } } return reply, nil }
// Nodes implements part of the Service interface. func (g *GraphStoreService) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { patterns := xrefs.ConvertFilters(req.Filter) var names []*spb.VName for _, ticket := range req.Ticket { name, err := kytheuri.ToVName(ticket) if err != nil { return nil, err } names = append(names, name) } var nodes []*xpb.NodeInfo for i, vname := range names { info := &xpb.NodeInfo{Ticket: req.Ticket[i]} if err := g.gs.Read(ctx, &spb.ReadRequest{Source: vname}, func(entry *spb.Entry) error { if len(patterns) == 0 || xrefs.MatchesAny(entry.FactName, patterns) { info.Fact = append(info.Fact, entryToFact(entry)) } return nil }); err != nil { return nil, err } if len(info.Fact) > 0 { nodes = append(nodes, info) } } return &xpb.NodesReply{Node: nodes}, nil }
func nodeToInfo(patterns []*regexp.Regexp, n *srvpb.Node) *xpb.NodeInfo { ni := &xpb.NodeInfo{Facts: make(map[string][]byte, len(n.Fact))} for _, f := range n.Fact { if xrefs.MatchesAny(f.Name, patterns) { ni.Facts[f.Name] = f.Value } } return ni }
func nodeToInfo(patterns []*regexp.Regexp, n *srvpb.Node) *xpb.NodeInfo { ni := &xpb.NodeInfo{Ticket: n.Ticket} for _, f := range n.Fact { if xrefs.MatchesAny(f.Name, patterns) { ni.Fact = append(ni.Fact, &xpb.Fact{Name: f.Name, Value: f.Value}) } } sort.Sort(xrefs.ByName(ni.Fact)) return ni }
func filterNode(patterns []*regexp.Regexp, node *xpb.NodeInfo) *xpb.NodeInfo { if len(patterns) == 0 { return nil } filteredFacts := make(map[string][]byte) for name, value := range node.Facts { if xrefs.MatchesAny(name, patterns) { filteredFacts[name] = value } } if len(filteredFacts) == 0 { return nil } return &xpb.NodeInfo{ Facts: filteredFacts, } }
func filterNode(patterns []*regexp.Regexp, node *xpb.NodeInfo) *xpb.NodeInfo { if len(patterns) == 0 { return nil } var filteredFacts []*xpb.Fact for _, f := range node.Fact { if xrefs.MatchesAny(f.Name, patterns) { filteredFacts = append(filteredFacts, f) } } if len(filteredFacts) == 0 { return nil } return &xpb.NodeInfo{ Ticket: node.Ticket, Fact: filteredFacts, } }
// Nodes implements part of the xrefs Service interface. func (t *tableImpl) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { tickets, err := fixTickets(req.Ticket) if err != nil { return nil, err } rs, err := t.pagedEdgeSets(ctx, tickets) if err != nil { return nil, err } defer func() { // drain channel in case of errors for _ = range rs { } }() reply := &xpb.NodesReply{} patterns := xrefs.ConvertFilters(req.Filter) for r := range rs { if r.Err == table.ErrNoSuchKey { continue } else if r.Err != nil { return nil, r.Err } node := r.PagedEdgeSet.EdgeSet.Source ni := &xpb.NodeInfo{Ticket: node.Ticket} for _, f := range node.Fact { if len(patterns) == 0 || xrefs.MatchesAny(f.Name, patterns) { ni.Fact = append(ni.Fact, &xpb.Fact{Name: f.Name, Value: f.Value}) } } if len(ni.Fact) > 0 { sort.Sort(xrefs.ByName(ni.Fact)) reply.Node = append(reply.Node, ni) } } return reply, nil }
// Nodes implements part of the xrefs.Service interface. func (db *DB) Nodes(req *xpb.NodesRequest) (*xpb.NodesReply, error) { reply := &xpb.NodesReply{} patterns := xrefs.ConvertFilters(req.Filter) for _, t := range req.Ticket { uri, err := kytheuri.Parse(t) if err != nil { return nil, fmt.Errorf("invalid ticket %q: %v", t, err) } rows, err := db.nodeFactsStmt.Query(uri.Signature, uri.Corpus, uri.Root, uri.Language, uri.Path) if err != nil { return nil, fmt.Errorf("sql select error for node %q: %v", t, err) } var facts []*xpb.Fact for rows.Next() { var fact xpb.Fact err = rows.Scan(&fact.Name, &fact.Value) if err != nil { rows.Close() return nil, fmt.Errorf("sql scan error for node %q: %v", t, err) } if len(patterns) == 0 || xrefs.MatchesAny(fact.Name, patterns) { facts = append(facts, &fact) } } if err := rows.Close(); err != nil { return nil, fmt.Errorf("error closing rows for node %q: %v", t, err) } if len(facts) != 0 { reply.Node = append(reply.Node, &xpb.NodeInfo{ Ticket: t, Fact: facts, }) } } return reply, nil }
// Nodes implements part of the xrefs Service interface. func (t *tableImpl) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { tickets, err := xrefs.FixTickets(req.Ticket) if err != nil { return nil, err } rs, err := t.pagedEdgeSets(ctx, tickets) if err != nil { return nil, err } defer func() { // drain channel in case of errors for _ = range rs { } }() reply := &xpb.NodesReply{Nodes: make(map[string]*xpb.NodeInfo, len(req.Ticket))} patterns := xrefs.ConvertFilters(req.Filter) for r := range rs { if r.Err == table.ErrNoSuchKey { continue } else if r.Err != nil { return nil, r.Err } node := r.PagedEdgeSet.Source ni := &xpb.NodeInfo{Facts: make(map[string][]byte, len(node.Fact))} for _, f := range node.Fact { if len(patterns) == 0 || xrefs.MatchesAny(f.Name, patterns) { ni.Facts[f.Name] = f.Value } } if len(ni.Facts) > 0 { reply.Nodes[node.Ticket] = ni } } return reply, nil }
// Edges implements part of the Service interface. func (g *GraphStoreService) Edges(ctx context.Context, req *xpb.EdgesRequest) (*xpb.EdgesReply, error) { if len(req.Ticket) == 0 { return nil, errors.New("no tickets specified") } else if req.PageToken != "" { return nil, errors.New("UNIMPLEMENTED: page_token") } patterns := xrefs.ConvertFilters(req.Filter) allowedKinds := stringset.New(req.Kind...) targetSet := stringset.New() reply := &xpb.EdgesReply{ EdgeSets: make(map[string]*xpb.EdgeSet), Nodes: make(map[string]*xpb.NodeInfo), } for _, ticket := range req.Ticket { vname, err := kytheuri.ToVName(ticket) if err != nil { return nil, fmt.Errorf("invalid ticket %q: %v", ticket, err) } var ( // EdgeKind -> TargetTicket -> OrdinalSet filteredEdges = make(map[string]map[string]map[int32]struct{}) filteredFacts = make(map[string][]byte) ) if err := g.gs.Read(ctx, &spb.ReadRequest{ Source: vname, EdgeKind: "*", }, func(entry *spb.Entry) error { edgeKind := entry.EdgeKind if edgeKind == "" { // node fact if len(patterns) > 0 && xrefs.MatchesAny(entry.FactName, patterns) { filteredFacts[entry.FactName] = entry.FactValue } } else { // edge edgeKind, ordinal, _ := schema.ParseOrdinal(edgeKind) if len(req.Kind) == 0 || allowedKinds.Contains(edgeKind) { targets, ok := filteredEdges[edgeKind] if !ok { targets = make(map[string]map[int32]struct{}) filteredEdges[edgeKind] = targets } ticket := kytheuri.ToString(entry.Target) ordSet, ok := targets[ticket] if !ok { ordSet = make(map[int32]struct{}) targets[ticket] = ordSet } ordSet[int32(ordinal)] = struct{}{} } } return nil }); err != nil { return nil, fmt.Errorf("failed to retrieve entries for ticket %q", ticket) } // Only add a EdgeSet if there are targets for the requested edge kinds. if len(filteredEdges) > 0 { groups := make(map[string]*xpb.EdgeSet_Group) for edgeKind, targets := range filteredEdges { g := &xpb.EdgeSet_Group{} for target, ordinals := range targets { for ordinal := range ordinals { g.Edge = append(g.Edge, &xpb.EdgeSet_Group_Edge{ TargetTicket: target, Ordinal: ordinal, }) } targetSet.Add(target) } groups[edgeKind] = g } reply.EdgeSets[ticket] = &xpb.EdgeSet{ Groups: groups, } // In addition, only add a NodeInfo if the filters have resulting facts. if len(filteredFacts) > 0 { reply.Nodes[ticket] = &xpb.NodeInfo{ Facts: filteredFacts, } } } } // Only request Nodes when there are fact filters given. if len(req.Filter) > 0 { // Eliminate redundant work by removing already requested nodes from targetSet for ticket := range reply.Nodes { targetSet.Remove(ticket) } // Batch request all leftover target nodes nodesReply, err := g.Nodes(ctx, &xpb.NodesRequest{ Ticket: targetSet.Slice(), Filter: req.Filter, }) if err != nil { return nil, fmt.Errorf("failure getting target nodes: %v", err) } for ticket, node := range nodesReply.Nodes { reply.Nodes[ticket] = node } } return reply, nil }
// Nodes implements part of the xrefs.Interface. func (d *DB) Nodes(ctx context.Context, req *xpb.NodesRequest) (*xpb.NodesReply, error) { tickets, err := xrefs.FixTickets(req.Ticket) if err != nil { return nil, err } setQ, args := sqlSetQuery(1, tickets) rs, err := d.Query(fmt.Sprintf("SELECT * FROM Nodes WHERE ticket IN %s;", setQ), args...) if err != nil { return nil, fmt.Errorf("error querying for nodes: %v", err) } defer closeRows(rs) var reply xpb.NodesReply for rs.Next() { var ticket, nodeKind string var subkind, textEncoding sql.NullString var text, otherFacts []byte var startOffset, endOffset, snippetStart, snippetEnd sql.NullInt64 var otherFactsNum int if err := rs.Scan(&ticket, &nodeKind, &subkind, &text, &textEncoding, &startOffset, &endOffset, &snippetStart, &snippetEnd, &otherFactsNum, &otherFacts); err != nil { return nil, fmt.Errorf("error scanning nodes: %v", err) } n := new(xpb.NodeInfo) if otherFactsNum > 0 { if err := proto.Unmarshal(otherFacts, n); err != nil { return nil, fmt.Errorf("unexpected node internal format: %v", err) } } if nodeKind != "" { n.Facts[ticket] = []byte(nodeKind) } if subkind.Valid { n.Facts[ticket] = []byte(subkind.String) } if text != nil { // TODO(schroederc): NULL text n.Facts[ticket] = text } if textEncoding.Valid { n.Facts[ticket] = []byte(textEncoding.String) } if startOffset.Valid { n.Facts[ticket] = []byte(strconv.FormatInt(startOffset.Int64, 10)) } if endOffset.Valid { n.Facts[ticket] = []byte(strconv.FormatInt(endOffset.Int64, 10)) } if snippetStart.Valid { n.Facts[ticket] = []byte(strconv.FormatInt(snippetStart.Int64, 10)) } if snippetEnd.Valid { n.Facts[ticket] = []byte(strconv.FormatInt(snippetEnd.Int64, 10)) } if len(req.Filter) > 0 { patterns := xrefs.ConvertFilters(req.Filter) matched := make(map[string][]byte, len(n.Facts)) for name, value := range n.Facts { if xrefs.MatchesAny(name, patterns) { matched[name] = value } } n.Facts = matched } if len(n.Facts) > 0 { reply.Nodes[ticket] = n } } return &reply, nil }
// Edges implements part of the Service interface. func (g *GraphStoreService) Edges(ctx context.Context, req *xpb.EdgesRequest) (*xpb.EdgesReply, error) { if len(req.Ticket) == 0 { return nil, errors.New("no tickets specified") } else if req.PageToken != "" { return nil, errors.New("UNIMPLEMENTED: page_token") } patterns := xrefs.ConvertFilters(req.Filter) allowedKinds := stringset.New(req.Kind...) targetSet := stringset.New() reply := new(xpb.EdgesReply) for _, ticket := range req.Ticket { vname, err := kytheuri.ToVName(ticket) if err != nil { return nil, fmt.Errorf("invalid ticket %q: %v", ticket, err) } var ( // EdgeKind -> StringSet<TargetTicket> filteredEdges = make(map[string]stringset.Set) filteredFacts []*xpb.Fact ) if err := g.gs.Read(ctx, &spb.ReadRequest{ Source: vname, EdgeKind: "*", }, func(entry *spb.Entry) error { edgeKind := entry.EdgeKind if edgeKind == "" { // node fact if len(patterns) > 0 && xrefs.MatchesAny(entry.FactName, patterns) { filteredFacts = append(filteredFacts, entryToFact(entry)) } } else { // edge if len(req.Kind) == 0 || allowedKinds.Contains(edgeKind) { targets := filteredEdges[edgeKind] if targets == nil { targets = stringset.New() filteredEdges[edgeKind] = targets } targets.Add(kytheuri.ToString(entry.Target)) } } return nil }); err != nil { return nil, fmt.Errorf("failed to retrieve entries for ticket %q", ticket) } // Only add a EdgeSet if there are targets for the requested edge kinds. if len(filteredEdges) > 0 { var groups []*xpb.EdgeSet_Group for edgeKind, targets := range filteredEdges { g := &xpb.EdgeSet_Group{Kind: edgeKind} for target := range targets { g.TargetTicket = append(g.TargetTicket, target) targetSet.Add(target) } groups = append(groups, g) } reply.EdgeSet = append(reply.EdgeSet, &xpb.EdgeSet{ SourceTicket: ticket, Group: groups, }) // In addition, only add a NodeInfo if the filters have resulting facts. if len(filteredFacts) > 0 { reply.Node = append(reply.Node, &xpb.NodeInfo{ Ticket: ticket, Fact: filteredFacts, }) } } } // Only request Nodes when there are fact filters given. if len(req.Filter) > 0 { // Ensure reply.Node is a unique set by removing already requested nodes from targetSet for _, n := range reply.Node { targetSet.Remove(n.Ticket) } // Batch request all leftover target nodes nodesReply, err := g.Nodes(ctx, &xpb.NodesRequest{ Ticket: targetSet.Slice(), Filter: req.Filter, }) if err != nil { return nil, fmt.Errorf("failure getting target nodes: %v", err) } reply.Node = append(reply.Node, nodesReply.Node...) } return reply, nil }