Пример #1
0
Файл: xrefs.go Проект: bzz/kythe
// 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)
}
Пример #2
0
Файл: xrefs.go Проект: bzz/kythe
func addReverseEdges(ctx context.Context, gs graphstore.Service) error {
	log.Println("Adding reverse edges")
	var (
		totalEntries int
		addedEdges   int
	)
	startTime := time.Now()
	err := gs.Scan(ctx, new(spb.ScanRequest), func(entry *spb.Entry) error {
		kind := entry.EdgeKind
		if kind != "" && schema.EdgeDirection(kind) == schema.Forward {
			if err := gs.Write(ctx, &spb.WriteRequest{
				Source: entry.Target,
				Update: []*spb.WriteRequest_Update{{
					Target:    entry.Source,
					EdgeKind:  schema.MirrorEdge(kind),
					FactName:  entry.FactName,
					FactValue: entry.FactValue,
				}},
			}); err != nil {
				return fmt.Errorf("Failed to write reverse edge: %v", err)
			}
			addedEdges++
		}
		totalEntries++
		return nil
	})
	log.Printf("Wrote %d reverse edges to GraphStore (%d total entries): %v", addedEdges, totalEntries, time.Since(startTime))
	return err
}
Пример #3
0
// CrossReference returns a (Referent, TargetAnchor) *ipb.CrossReference
// equivalent to the given decoration.  The decoration's anchor is expanded
// given its parent file and associated Normalizer.
func CrossReference(file *srvpb.File, norm *xrefs.Normalizer, d *srvpb.FileDecorations_Decoration, tgt *srvpb.Node) (*ipb.CrossReference, error) {
	if file == nil || norm == nil {
		return nil, errors.New("missing decoration's parent file")
	}

	ea, err := ExpandAnchor(d.Anchor, file, norm, schema.MirrorEdge(d.Kind))
	if err != nil {
		return nil, fmt.Errorf("error expanding anchor {%+v}: %v", d.Anchor, err)
	}
	// Throw away most of the referent's facts.  They are not needed.
	var facts []*cpb.Fact
	if tgt != nil {
		for _, fact := range tgt.Fact {
			if fact.Name == schema.CompleteFact {
				facts = append(facts, fact)
			}
		}
	}
	return &ipb.CrossReference{
		Referent: &srvpb.Node{
			Ticket: d.Target,
			Fact:   facts,
		},
		TargetAnchor: ea,
	}, nil
}
Пример #4
0
// ReverseSinglePath returns the reverse p, assuming it is a single-edge Path.
func ReverseSinglePath(p *ipb.Path) *ipb.Path {
	return &ipb.Path{
		Pivot: p.Edges[0].Target,
		Edges: []*ipb.Path_Edge{{
			Kind:    schema.MirrorEdge(p.Edges[0].Kind),
			Ordinal: p.Edges[0].Ordinal,
			Target:  p.Pivot,
		}},
	}
}
Пример #5
0
// ToString returns a human-readable string representation of p.
func ToString(p *ipb.Path) string {
	s := "**" + p.Pivot.NodeKind + "**"
	for _, e := range p.Edges {
		if schema.EdgeDirection(e.Kind) == schema.Forward {
			s += fmt.Sprintf(" -[%s]> %s", e.Kind, e.Target.NodeKind)
		} else {
			s += fmt.Sprintf(" <[%s]- %s", schema.MirrorEdge(e.Kind), e.Target.NodeKind)
		}
	}
	return s
}
Пример #6
0
// expandEdgeKind prefixes unrooted (not starting with "/") edge kinds with the
// standard Kythe edge prefix ("/kythe/edge/").
func expandEdgeKind(kind string) string {
	ck := schema.Canonicalize(kind)
	if strings.HasPrefix(ck, "/") {
		return kind
	}

	expansion := schema.EdgePrefix + ck
	if schema.EdgeDirection(kind) == schema.Reverse {
		return schema.MirrorEdge(expansion)
	}
	return expansion
}
Пример #7
0
// CrossReference returns a (Referent, TargetAnchor) *srvpb.CrossReference
// equivalent to the given decoration.  The decoration's anchor is expanded
// given its parent file and associated Normalizer.
func CrossReference(file *srvpb.File, norm *xrefs.Normalizer, d *srvpb.FileDecorations_Decoration) (*srvpb.CrossReference, error) {
	if file == nil || norm == nil {
		return nil, errors.New("missing decoration's parent file")
	}

	ea, err := expandAnchor(d.Anchor, file, norm, schema.MirrorEdge(d.Kind))
	if err != nil {
		return nil, fmt.Errorf("error expanding anchor {%+v}: %v", d.Anchor, err)
	}
	return &srvpb.CrossReference{
		// Throw away the referent's facts.  They are not needed.
		Referent:     &srvpb.Node{Ticket: d.Target.Ticket},
		TargetAnchor: ea,
	}, nil
}
Пример #8
0
func writeCompletedEdges(ctx context.Context, edges disksort.Interface, e *srvpb.Edge) error {
	if err := edges.Add(&srvpb.Edge{
		Source: &srvpb.Node{Ticket: e.Source.Ticket},
		Kind:   e.Kind,
		Target: e.Target,
	}); err != nil {
		return fmt.Errorf("error writing complete edge: %v", err)
	}
	if err := edges.Add(&srvpb.Edge{
		Source: &srvpb.Node{Ticket: e.Target.Ticket},
		Kind:   schema.MirrorEdge(e.Kind),
		Target: assemble.FilterTextFacts(e.Source),
	}); err != nil {
		return fmt.Errorf("error writing complete edge mirror: %v", err)
	}
	return nil
}
Пример #9
0
func writeWithReverses(ctx context.Context, tbl *table.KVProto, src, kind string, targets []string) error {
	if err := tbl.Put(ctx, []byte(src+tempTableKeySep+kind+tempTableKeySep), &srvpb.EdgeSet_Group{
		Kind:         kind,
		TargetTicket: targets,
	}); err != nil {
		return fmt.Errorf("error writing edges group: %v", err)
	}
	revGroup := &srvpb.EdgeSet_Group{
		Kind:         schema.MirrorEdge(kind),
		TargetTicket: []string{src},
	}
	for _, tgt := range targets {
		if err := tbl.Put(ctx, []byte(tgt+tempTableKeySep+revGroup.Kind+tempTableKeySep+src), revGroup); err != nil {
			return fmt.Errorf("error writing rev edges group: %v", err)
		}
	}
	return nil
}
Пример #10
0
func writeWithReverses(ctx context.Context, gs graphstore.Service, req *spb.WriteRequest) error {
	if err := gs.Write(ctx, req); err != nil {
		return fmt.Errorf("error writing edges: %v", err)
	}
	for _, u := range req.Update {
		if err := gs.Write(ctx, &spb.WriteRequest{
			Source: u.Target,
			Update: []*spb.WriteRequest_Update{{
				Target:    req.Source,
				EdgeKind:  schema.MirrorEdge(u.EdgeKind),
				FactName:  u.FactName,
				FactValue: u.FactValue,
			}},
		}); err != nil {
			return fmt.Errorf("error writing rev edge: %v", err)
		}
	}
	return nil
}
Пример #11
0
// PartialReverseEdges returns the set of partial reverse edges from the given source.  Each
// reversed Edge has its Target fully populated and its Source will have no facts.  To ensure every
// node has at least 1 Edge, the first Edge will be a self-edge without a Kind or Target.  To reduce
// the size of edge sets, each Target will have any text facts filtered (see FilterTextFacts).
func PartialReverseEdges(src *Source) []*srvpb.Edge {
	node := src.Node()

	edges := []*srvpb.Edge{{
		Source: node, // self-edge to ensure every node has at least 1 edge
	}}

	targetNode := FilterTextFacts(node)

	for kind, targets := range src.Edges {
		rev := schema.MirrorEdge(kind)
		for _, target := range targets {
			edges = append(edges, &srvpb.Edge{
				Source: &srvpb.Node{Ticket: target},
				Kind:   rev,
				Target: targetNode,
			})
		}
	}

	return edges
}
Пример #12
0
// FromSource creates a set of *ipb.Paths for each of its edges as well as a
// single *ipb.Path with only its pivot set to be the source node.
func FromSource(src *ipb.Source) []*ipb.Path {
	var paths []*ipb.Path

	n := specialize(assemble.Node(src))
	paths = append(paths, &ipb.Path{Pivot: n})
	for kind, group := range src.EdgeGroups {
		if schema.EdgeDirection(kind) != schema.Forward {
			continue
		}
		for _, tgt := range group.Edges {
			paths = append(paths, &ipb.Path{
				Pivot: &ipb.Path_Node{
					Ticket: tgt.Ticket,
				},
				Edges: []*ipb.Path_Edge{{
					Kind:    schema.MirrorEdge(kind),
					Ordinal: int32(tgt.Ordinal),
					Target:  n,
				}},
			})
		}
	}
	return paths
}
Пример #13
0
	} `json:"span"`
	Kind string `json:"kind"`

	Node struct {
		Ticket  string   `json:"ticket"`
		Names   []string `json:"names,omitempty"`
		Kind    string   `json:"kind,omitempty"`
		Subkind string   `json:"subkind,omitempty"`
		Typed   string   `json:"typed,omitempty"`

		Definitions []*definition `json:"definitions,omitempty"`
	} `json:"node"`
}

var (
	definedAtEdge        = schema.MirrorEdge(schema.DefinesEdge)
	definedBindingAtEdge = schema.MirrorEdge(schema.DefinesBindingEdge)
)

func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		flagutil.UsageErrorf("unknown non-flag argument(s): %v", flag.Args())
	} else if *offset < 0 && (*lineNumber < 0 || *columnOffset < 0) {
		flagutil.UsageError("non-negative --offset (or --line and --column) required")
	} else if *signature == "" && *path == "" {
		flagutil.UsageError("must provide at least --path or --signature")
	}

	defer (*apiFlag).Close()
	xs, idx = *apiFlag, *apiFlag
Пример #14
0
		if err := gs.Write(ctx, &spb.WriteRequest{
			Source: u.Target,
			Update: []*spb.WriteRequest_Update{{
				Target:    req.Source,
				EdgeKind:  schema.MirrorEdge(u.EdgeKind),
				FactName:  u.FactName,
				FactValue: u.FactValue,
			}},
		}); err != nil {
			return fmt.Errorf("error writing rev edge: %v", err)
		}
	}
	return nil
}

var revChildOfEdgeKind = schema.MirrorEdge(schema.ChildOfEdge)

func writeDecorations(ctx context.Context, t table.Proto, es xrefs.NodesEdgesService, files []string) error {
	log.Println("Writing Decorations")

	edges := make(chan *xpb.EdgesReply)
	var eErr error
	go func() {
		eErr = readEdges(ctx, es, files, edges,
			decorationFilters, []string{revChildOfEdgeKind})
		close(edges)
	}()

	for e := range edges {
		decor := &srvpb.FileDecorations{}
		if len(e.EdgeSet) == 0 {
Пример #15
0
	testAnchorVName       = sig("testAnchor")
	testAnchorTargetVName = sig("someSemanticNode")

	testNodes = []*node{
		{sig("orphanedNode"), facts(schema.NodeKindFact, "orphan"), nil},
		{testFileVName, facts(
			schema.NodeKindFact, schema.FileKind,
			schema.TextFact, testFileContent,
			schema.TextEncodingFact, testFileEncoding), map[string][]*spb.VName{
			revChildOfEdgeKind: {testAnchorVName},
		}},
		{sig("sig2"), facts(schema.NodeKindFact, "test"), map[string][]*spb.VName{
			"someEdgeKind": {sig("signature")},
		}},
		{sig("signature"), facts(schema.NodeKindFact, "test"), map[string][]*spb.VName{
			schema.MirrorEdge("someEdgeKind"): {sig("sig2")},
		}},
		{testAnchorVName, facts(
			schema.AnchorEndFact, "4",
			schema.AnchorStartFact, "1",
			schema.NodeKindFact, schema.AnchorKind,
		), map[string][]*spb.VName{
			schema.ChildOfEdge: {testFileVName},
			schema.RefEdge:     {testAnchorTargetVName},
		}},
		{testAnchorTargetVName, facts(schema.NodeKindFact, "record"), map[string][]*spb.VName{
			schema.MirrorEdge(schema.RefEdge): {testAnchorVName},
		}},
	}
	testEntries = nodesToEntries(testNodes)
)
Пример #16
0
			Ticket: allRelatedNodes.Slice(),
			Filter: req.Filter,
		})
		if err != nil {
			return nil, fmt.Errorf("error retrieving related nodes: %v", err)
		}
		for _, n := range nReply.Node {
			reply.Nodes[n.Ticket] = n
		}
	}

	return reply, nil
}

var (
	revDefinesEdge        = schema.MirrorEdge(schema.DefinesEdge)
	revDefinesBindingEdge = schema.MirrorEdge(schema.DefinesBindingEdge)

	revRefEdge = schema.MirrorEdge(schema.RefEdge)

	revDocumentsEdge = schema.MirrorEdge(schema.DocumentsEdge)
)

func isDefKind(requestedKind xpb.CrossReferencesRequest_DefinitionKind, edgeKind string) bool {
	switch requestedKind {
	case xpb.CrossReferencesRequest_NO_DEFINITIONS:
		return false
	case xpb.CrossReferencesRequest_FULL_DEFINITIONS:
		return edgeKind == revDefinesEdge
	case xpb.CrossReferencesRequest_BINDING_DEFINITIONS:
		return edgeKind == revDefinesBindingEdge
Пример #17
0
		End   int    `json:"end"`
		Text  string `json:"text,omitempty"`
	} `json:"span"`
	Kind string `json:"kind"`

	Node struct {
		Ticket  string   `json:"ticket"`
		Names   []string `json:"names,omitempty"`
		Kind    string   `json:"kind,omitempty"`
		Subkind string   `json:"subkind,omitempty"`

		Definitions []*definition `json:"definitions,omitempty"`
	} `json:"node"`
}

var definedAtEdge = schema.MirrorEdge(schema.DefinesEdge)

func main() {
	flag.Parse()
	if *offset < 0 {
		flagutil.UsageError("non-negative --offset required")
	} else if *signature == "" && *path == "" {
		flagutil.UsageError("must provide at least --path or --signature")
	}

	if strings.HasPrefix(*remoteAPI, "http://") || strings.HasPrefix(*remoteAPI, "https://") {
		xs = xrefs.WebClient(*remoteAPI)
		idx = search.WebClient(*remoteAPI)
	} else {
		conn, err := grpc.Dial(*remoteAPI)
		if err != nil {
Пример #18
0
func TestSlowDocumentation(t *testing.T) {
	db := []struct {
		ticket, kind, documented, defines, completes, completed, childof, typed, text, format string
		params, definitionText                                                                []string
	}{
		{ticket: "kythe://test#a", kind: "etc", documented: "kythe://test#adoc", format: "asig"},
		{ticket: "kythe://test#adoc", kind: "doc", text: "atext"},
		{ticket: "kythe://test#fdoc", kind: "doc", text: "ftext"},
		{ticket: "kythe://test#fdecl", kind: "function", documented: "kythe://test#fdoc", format: "fsig"},
		{ticket: "kythe://test#fdefn", kind: "function", completed: "kythe://test#fbind", format: "fsig"},
		{ticket: "kythe://test#fbind", kind: "anchor", defines: "kythe://test#fdefn", completes: "kythe://test#fdecl"},
		{ticket: "kythe://test#l", kind: "etc", documented: "kythe://test#ldoc"},
		{ticket: "kythe://test#ldoc", kind: "doc", text: "ltext", params: []string{"kythe://test#l1", "kythe://test#l2"}},
		{ticket: "kythe://test#l1", kind: "etc", definitionText: []string{"deftext1"}},
		{ticket: "kythe://test#l2", kind: "etc", definitionText: []string{"deftext2"}},
		{ticket: "kythe://test#l", kind: "etc", documented: "kythe://test#ldoc", format: "lsig"},
	}
	mkPr := func(text string, linkTicket ...string) *xpb.Printable {
		links := make([]*xpb.Link, len(linkTicket))
		for i, link := range linkTicket {
			links[i] = &xpb.Link{Definition: []*xpb.Anchor{{Text: link}}}
		}
		return &xpb.Printable{RawText: text, Link: links}
	}
	tests := []struct {
		ticket string
		reply  *xpb.DocumentationReply_Document
	}{
		{ticket: "kythe://test#a", reply: &xpb.DocumentationReply_Document{Signature: mkPr("asig"), Kind: "etc", Text: mkPr("atext")}},
		{ticket: "kythe://test#fdecl", reply: &xpb.DocumentationReply_Document{Signature: mkPr("fsig"), Kind: "function", Text: mkPr("ftext")}},
		{ticket: "kythe://test#fdefn", reply: &xpb.DocumentationReply_Document{Signature: mkPr("fsig"), Kind: "function", Text: mkPr("ftext")}},
		{ticket: "kythe://test#l", reply: &xpb.DocumentationReply_Document{Signature: mkPr("lsig"), Kind: "etc", Text: mkPr("ltext", "deftext1", "deftext2")}},
	}
	nodes := make(map[string]*xpb.NodeInfo)
	edges := make(map[string]*xpb.EdgeSet)
	xrefs := make(map[string]*xpb.CrossReferencesReply_CrossReferenceSet)
	for _, node := range db {
		nodes[node.ticket] = &xpb.NodeInfo{
			Facts: map[string][]byte{
				schema.NodeKindFact: []byte(node.kind),
				schema.TextFact:     []byte(node.text),
				schema.FormatFact:   []byte(node.format),
			},
		}
		if node.definitionText != nil {
			set := &xpb.CrossReferencesReply_CrossReferenceSet{Ticket: node.ticket}
			for _, text := range node.definitionText {
				set.Definition = append(set.Definition, &xpb.CrossReferencesReply_RelatedAnchor{Anchor: &xpb.Anchor{Text: text}})
			}
			xrefs[node.ticket] = set
		}
		set := &xpb.EdgeSet{Groups: make(map[string]*xpb.EdgeSet_Group)}
		if node.typed != "" {
			set.Groups[schema.TypedEdge] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.typed}},
			}
		}
		if node.childof != "" {
			set.Groups[schema.ChildOfEdge] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.childof}},
			}
		}
		if node.documented != "" {
			set.Groups[schema.MirrorEdge(schema.DocumentsEdge)] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.documented}},
			}
		}
		if node.completes != "" {
			set.Groups[schema.CompletesEdge] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.completes}},
			}
		}
		if node.completed != "" {
			set.Groups[schema.MirrorEdge(schema.CompletesEdge)] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.completed}},
			}
		}
		if node.defines != "" {
			set.Groups[schema.DefinesBindingEdge] = &xpb.EdgeSet_Group{
				Edge: []*xpb.EdgeSet_Group_Edge{&xpb.EdgeSet_Group_Edge{TargetTicket: node.defines}},
			}
		}
		if node.params != nil {
			var edges []*xpb.EdgeSet_Group_Edge
			for i, p := range node.params {
				edges = append(edges, &xpb.EdgeSet_Group_Edge{TargetTicket: p, Ordinal: int32(i)})
			}
			set.Groups[schema.ParamEdge] = &xpb.EdgeSet_Group{
				Edge: edges,
			}
		}
		edges[node.ticket] = set
	}
	getNode := func(ticket string, facts []string) *xpb.NodeInfo {
		data, found := nodes[ticket]
		if !found {
			return nil
		}
		info := &xpb.NodeInfo{Facts: make(map[string][]byte)}
		for _, fact := range facts {
			info.Facts[fact] = data.Facts[fact]
		}
		return info
	}
	service := &mockService{
		NodesFn: func(req *xpb.NodesRequest) (*xpb.NodesReply, error) {
			if len(req.Ticket) != 1 {
				t.Fatalf("Unexpected Nodes request: %v", req)
				return nil, nil
			}
			reply := &xpb.NodesReply{Nodes: make(map[string]*xpb.NodeInfo)}
			for _, ticket := range req.Ticket {
				if info := getNode(ticket, req.Filter); info != nil {
					reply.Nodes[ticket] = info
				}
			}
			return reply, nil
		},
		EdgesFn: func(req *xpb.EdgesRequest) (*xpb.EdgesReply, error) {
			reply := &xpb.EdgesReply{
				EdgeSets: make(map[string]*xpb.EdgeSet),
				Nodes:    make(map[string]*xpb.NodeInfo),
			}
			for _, ticket := range req.Ticket {
				if data, found := edges[ticket]; found {
					set := &xpb.EdgeSet{Groups: make(map[string]*xpb.EdgeSet_Group)}
					reply.EdgeSets[ticket] = set
					nodes := make(map[string]bool)
					for groupKind, group := range data.Groups {
						for _, kind := range req.Kind {
							if groupKind == kind {
								set.Groups[kind] = group
								for _, edge := range group.Edge {
									if !nodes[edge.TargetTicket] {
										nodes[edge.TargetTicket] = true
										if node := getNode(edge.TargetTicket, req.Filter); node != nil {
											reply.Nodes[edge.TargetTicket] = node
										}
									}
								}
								break
							}
						}
					}
				}
			}
			return reply, nil
		},
		CrossReferencesFn: func(req *xpb.CrossReferencesRequest) (*xpb.CrossReferencesReply, error) {
			if req.DefinitionKind != xpb.CrossReferencesRequest_ALL_DEFINITIONS ||
				req.ReferenceKind != xpb.CrossReferencesRequest_NO_REFERENCES ||
				req.DocumentationKind != xpb.CrossReferencesRequest_NO_DOCUMENTATION ||
				req.AnchorText {
				t.Fatalf("Unexpected CrossReferences request: %v", req)
			}
			reply := &xpb.CrossReferencesReply{
				CrossReferences: make(map[string]*xpb.CrossReferencesReply_CrossReferenceSet),
			}
			for _, ticket := range req.Ticket {
				if set, found := xrefs[ticket]; found {
					reply.CrossReferences[ticket] = set
				}
			}
			return reply, nil
		},
	}
	for _, test := range tests {
		reply, err := SlowDocumentation(nil, service, &xpb.DocumentationRequest{Ticket: []string{test.ticket}})
		if err != nil {
			t.Fatalf("SlowDocumentation error for %s: %v", test.ticket, err)
		}
		test.reply.Ticket = test.ticket
		if err := testutil.DeepEqual(&xpb.DocumentationReply{Document: []*xpb.DocumentationReply_Document{test.reply}}, reply); err != nil {
			t.Fatal(err)
		}
	}
}
Пример #19
0
func displayEdgeGraph(reply *xpb.EdgesReply) error {
	nodes := xrefs.NodesMap(reply.Nodes)
	edges := make(map[string]map[string]stringset.Set)

	for source, es := range reply.EdgeSets {
		for gKind, g := range es.Groups {
			for _, edge := range g.Edge {
				tgt := edge.TargetTicket
				src, kind := source, gKind
				if schema.EdgeDirection(kind) == schema.Reverse {
					src, kind, tgt = tgt, schema.MirrorEdge(kind), src
				}
				groups, ok := edges[src]
				if !ok {
					groups = make(map[string]stringset.Set)
					edges[src] = groups
				}
				targets, ok := groups[kind]
				if !ok {
					targets = stringset.New()
					groups[kind] = targets
				}
				targets.Add(tgt)
			}
		}
	}
	if _, err := fmt.Println("digraph kythe {"); err != nil {
		return err
	}
	for ticket, node := range nodes {
		if _, err := fmt.Printf(`	%q [label=<<table><tr><td colspan="2">%s</td></tr>`, ticket, html.EscapeString(ticket)); err != nil {
			return err
		}
		var facts []string
		for fact := range node {
			facts = append(facts, fact)
		}
		sort.Strings(facts)
		for _, fact := range facts {
			if _, err := fmt.Printf("<tr><td>%s</td><td>%s</td></tr>", html.EscapeString(fact), html.EscapeString(string(node[fact]))); err != nil {
				return err
			}
		}
		if _, err := fmt.Println("</table>> shape=plaintext];"); err != nil {
			return err
		}
	}
	if _, err := fmt.Println(); err != nil {
		return err
	}

	for src, groups := range edges {
		for kind, targets := range groups {
			for tgt := range targets {
				if _, err := fmt.Printf("\t%q -> %q [label=%q];\n", src, tgt, kind); err != nil {
					return err
				}
			}
		}
	}
	if _, err := fmt.Println("}"); err != nil {
		return err
	}
	return nil
}
Пример #20
0
// Edges implements part of the xrefs.Service interface.
func (db *DB) Edges(req *xpb.EdgesRequest) (*xpb.EdgesReply, error) {
	if req.PageSize != 0 || req.PageToken != "" {
		return nil, errors.New("edge pages unimplemented for SQL DB")
	}
	reply := &xpb.EdgesReply{}

	allowedKinds := stringset.New(req.Kind...)

	nodeTickets := stringset.New()
	for _, t := range req.Ticket {
		uri, err := kytheuri.Parse(t)
		if err != nil {
			return nil, err
		}

		edges := make(map[string]stringset.Set)
		var (
			target kytheuri.URI
			kind   string
		)

		rows, err := db.edgesStmt.Query(uri.Signature, uri.Corpus, uri.Root, uri.Path, uri.Language)
		if err != nil {
			rows.Close()
			return nil, fmt.Errorf("forward edges query error: %v", err)
		}
		for rows.Next() {
			if err := rows.Scan(&target.Signature, &target.Corpus, &target.Root, &target.Path, &target.Language, &kind); err != nil {
				rows.Close()
				return nil, fmt.Errorf("forward edges scan error: %v", err)
			} else if len(allowedKinds) != 0 && !allowedKinds.Contains(kind) {
				continue
			}

			targets, ok := edges[kind]
			if !ok {
				targets = stringset.New()
				edges[kind] = targets
			}

			ticket := target.String()
			targets.Add(ticket)
			nodeTickets.Add(ticket)
		}
		if err := rows.Close(); err != nil {
			return nil, err
		}

		rows, err = db.revEdgesStmt.Query(uri.Signature, uri.Corpus, uri.Root, uri.Path, uri.Language)
		if err != nil {
			rows.Close()
			return nil, fmt.Errorf("reverse edges query error: %v", err)
		}
		for rows.Next() {
			if err := rows.Scan(&target.Signature, &target.Corpus, &target.Root, &target.Path, &target.Language, &kind); err != nil {
				rows.Close()
				return nil, fmt.Errorf("reverse edges scan error: %v", err)
			}

			kind = schema.MirrorEdge(kind)
			if len(allowedKinds) != 0 && !allowedKinds.Contains(kind) {
				continue
			}

			targets, ok := edges[kind]
			if !ok {
				targets = stringset.New()
				edges[kind] = targets
			}

			ticket := target.String()
			targets.Add(ticket)
			nodeTickets.Add(ticket)
		}
		if err := rows.Close(); err != nil {
			return nil, err
		}

		var g []*xpb.EdgeSet_Group
		for kind, targets := range edges {
			g = append(g, &xpb.EdgeSet_Group{
				Kind:         kind,
				TargetTicket: targets.Slice(),
			})
		}

		if len(g) != 0 {
			reply.EdgeSet = append(reply.EdgeSet, &xpb.EdgeSet{
				SourceTicket: t,
				Group:        g,
			})
		}
	}

	nodesReply, err := db.Nodes(&xpb.NodesRequest{
		Ticket: nodeTickets.Slice(),
		Filter: req.Filter,
	})
	if err != nil {
		return nil, fmt.Errorf("nodes request error: %v", err)
	}
	reply.Node = nodesReply.Node
	return reply, nil
}