Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

traversal: track seen links, and revisit only if configured to do so. #252

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions traversal/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ func (prog *Progress) init() {
prog.Cfg = &Config{}
}
prog.Cfg.init()
if prog.Cfg.LinkVisitOnlyOnce {
prog.SeenLinks = make(map[datamodel.Link]struct{})
}
}

// asPathSegment figures out how to coerce a node into a PathSegment.
Expand Down
4 changes: 3 additions & 1 deletion traversal/fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ type Progress struct {
Path datamodel.Path
Link datamodel.Link
}
Budget *Budget // If present, tracks "budgets" for how many more steps we're willing to take before we should halt.
Budget *Budget // If present, tracks "budgets" for how many more steps we're willing to take before we should halt.
SeenLinks map[datamodel.Link]struct{} // Set used to remember which links have been visited before, if Cfg.LinkVisitOnlyOnce is true.
}

type Config struct {
Ctx context.Context // Context carried through a traversal. Optional; use it if you need cancellation.
LinkSystem linking.LinkSystem // LinkSystem used for automatic link loading, and also any storing if mutation features (e.g. traversal.Transform) are used.
LinkTargetNodePrototypeChooser LinkTargetNodePrototypeChooser // Chooser for Node implementations to produce during automatic link traversal.
LinkVisitOnlyOnce bool // By default, we visit across links wherever we see them again, even if we've visited them before, because the reason for visiting might be different than it was before since we got to it via a different path. If set to true, track links we've seen before in Progress.SeenLinks and do not visit them again. Note that sufficiently complex selectors may require valid revisiting of some links, so setting this to true can change behavior noticably and should be done with care.
}

type Budget struct {
Expand Down
12 changes: 12 additions & 0 deletions traversal/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ func (prog Progress) walkAdv_iterateAll(n datamodel.Node, s selector.Selector, f
progNext.Path = prog.Path.AppendSegment(ps)
if v.Kind() == datamodel.Kind_Link {
lnk, _ := v.AsLink()
if prog.Cfg.LinkVisitOnlyOnce {
if _, seen := prog.SeenLinks[lnk]; seen {
continue
}
prog.SeenLinks[lnk] = struct{}{}
}
progNext.LastBlock.Path = progNext.Path
progNext.LastBlock.Link = lnk
v, err = progNext.loadLink(v, n)
Expand Down Expand Up @@ -169,6 +175,12 @@ func (prog Progress) walkAdv_iterateSelective(n datamodel.Node, attn []datamodel
progNext.Path = prog.Path.AppendSegment(ps)
if v.Kind() == datamodel.Kind_Link {
lnk, _ := v.AsLink()
if prog.Cfg.LinkVisitOnlyOnce {
if _, seen := prog.SeenLinks[lnk]; seen {
continue
}
prog.SeenLinks[lnk] = struct{}{}
}
progNext.LastBlock.Path = progNext.Path
progNext.LastBlock.Link = lnk
v, err = progNext.loadLink(v, n)
Expand Down