Add --max-distance for recursive link filtering options

pull/6/head
Mickaël Menu 3 years ago
parent 2111deb88d
commit d9d5fe1730
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -342,7 +342,7 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
groupById := false groupById := false
args := make([]interface{}, 0) args := make([]interface{}, 0)
setupLinkFilter := func(paths []string, forward, negate bool, recursive bool) error { setupLinkFilter := func(paths []string, forward, negate, recursive bool, maxDistance int) error {
ids, err := d.findIdsByPathPrefixes(paths) ids, err := d.findIdsByPathPrefixes(paths)
if err != nil { if err != nil {
return err return err
@ -371,7 +371,12 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
// Credit to https://inviqa.com/blog/storing-graphs-database-sql-meets-social-network // Credit to https://inviqa.com/blog/storing-graphs-database-sql-meets-social-network
links_src = "links_transitive_closure" links_src = "links_transitive_closure"
orderTerms = append(orderTerms, alias+".distance") orderTerms = append(orderTerms, alias+".distance")
cteClauses = append(cteClauses, `WITH RECURSIVE links_transitive_closure(source_id, target_id, title, snippet, distance, path) AS (
if maxDistance == 0 {
maxDistance = 1000
}
cteClauses = append(cteClauses, fmt.Sprintf(`WITH RECURSIVE links_transitive_closure(source_id, target_id, title, snippet, distance, path) AS (
SELECT source_id, target_id, title, snippet, SELECT source_id, target_id, title, snippet,
1 AS distance, 1 AS distance,
source_id || '.' || target_id || '.' AS path source_id || '.' || target_id || '.' AS path
@ -385,8 +390,8 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
FROM links AS l FROM links AS l
JOIN links_transitive_closure AS tc JOIN links_transitive_closure AS tc
ON l.source_id = tc.target_id ON l.source_id = tc.target_id
WHERE tc.path NOT LIKE '%' || l.target_id || '.%' WHERE tc.distance < %d AND tc.path NOT LIKE '%%' || l.target_id || '.%%'
)`) )`, maxDistance))
} }
if !negate { if !negate {
@ -439,13 +444,13 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
whereExprs = append(whereExprs, strings.Join(globs, " AND ")) whereExprs = append(whereExprs, strings.Join(globs, " AND "))
case note.LinkedByFilter: case note.LinkedByFilter:
err := setupLinkFilter(filter.Paths, false, filter.Negate, filter.Recursive) err := setupLinkFilter(filter.Paths, false, filter.Negate, filter.Recursive, filter.MaxDistance)
if err != nil { if err != nil {
return nil, err return nil, err
} }
case note.LinkingToFilter: case note.LinkingToFilter:
err := setupLinkFilter(filter.Paths, true, filter.Negate, filter.Recursive) err := setupLinkFilter(filter.Paths, true, filter.Negate, filter.Recursive, filter.MaxDistance)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -528,6 +528,20 @@ func TestNoteDAOFindLinkedByRecursive(t *testing.T) {
) )
} }
func TestNoteDAOFindLinkedByRecursiveWithMaxDistance(t *testing.T) {
testNoteDAOFindPaths(t,
note.FinderOpts{
Filters: []note.Filter{note.LinkedByFilter{
Paths: []string{"log/2021-01-04.md"},
Negate: false,
Recursive: true,
MaxDistance: 2,
}},
},
[]string{"index.md", "f39c8.md"},
)
}
func TestNoteDAOFindLinkedByWithSnippets(t *testing.T) { func TestNoteDAOFindLinkedByWithSnippets(t *testing.T) {
testNoteDAOFind(t, testNoteDAOFind(t,
note.FinderOpts{ note.FinderOpts{
@ -612,6 +626,20 @@ func TestNoteDAOFindLinkingToRecursive(t *testing.T) {
) )
} }
func TestNoteDAOFindLinkingToRecursiveWithMaxDistance(t *testing.T) {
testNoteDAOFindPaths(t,
note.FinderOpts{
Filters: []note.Filter{note.LinkingToFilter{
Paths: []string{"log/2021-01-04.md"},
Negate: false,
Recursive: true,
MaxDistance: 2,
}},
},
[]string{"log/2021-01-03.md", "f39c8.md"},
)
}
func TestNoteDAOFindNotLinkingTo(t *testing.T) { func TestNoteDAOFindNotLinkingTo(t *testing.T) {
testNoteDAOFindPaths(t, testNoteDAOFindPaths(t,
note.FinderOpts{ note.FinderOpts{

@ -24,6 +24,7 @@ type Filtering struct {
LinkingTo []string `help:"Only the notes linking to the given notes" placeholder:"<path>" short:"L"` LinkingTo []string `help:"Only the notes linking to the given notes" placeholder:"<path>" short:"L"`
NotLinkedBy []string `help:"Only the notes not linked by the given notes" placeholder:"<path>"` NotLinkedBy []string `help:"Only the notes not linked by the given notes" placeholder:"<path>"`
NotLinkingTo []string `help:"Only the notes not linking to the given notes" placeholder:"<path>"` NotLinkingTo []string `help:"Only the notes not linking to the given notes" placeholder:"<path>"`
MaxDistance int `help:"Maximum distance between two linked notes"`
Orphan bool `help:"Only the notes which don't have any other note linking to them"` Orphan bool `help:"Only the notes which don't have any other note linking to them"`
Exclude []string `help:"Excludes notes matching the given file path pattern from the list" short:"x" placeholder:"<glob>"` Exclude []string `help:"Excludes notes matching the given file path pattern from the list" short:"x" placeholder:"<glob>"`
Recursive bool `help:"Follow links recursively" short:"r"` Recursive bool `help:"Follow links recursively" short:"r"`
@ -128,18 +129,20 @@ func NewFinderOpts(zk *zk.Zk, filtering Filtering, sorting Sorting) (*note.Finde
linkedByPaths, ok := relPaths(zk, filtering.LinkedBy) linkedByPaths, ok := relPaths(zk, filtering.LinkedBy)
if ok { if ok {
filters = append(filters, note.LinkedByFilter{ filters = append(filters, note.LinkedByFilter{
Paths: linkedByPaths, Paths: linkedByPaths,
Negate: false, Negate: false,
Recursive: filtering.Recursive, Recursive: filtering.Recursive,
MaxDistance: filtering.MaxDistance,
}) })
} }
linkingToPaths, ok := relPaths(zk, filtering.LinkingTo) linkingToPaths, ok := relPaths(zk, filtering.LinkingTo)
if ok { if ok {
filters = append(filters, note.LinkingToFilter{ filters = append(filters, note.LinkingToFilter{
Paths: linkingToPaths, Paths: linkingToPaths,
Negate: false, Negate: false,
Recursive: filtering.Recursive, Recursive: filtering.Recursive,
MaxDistance: filtering.MaxDistance,
}) })
} }

@ -42,16 +42,18 @@ type ExcludePathFilter []string
// LinkedByFilter is a note filter used to select notes being linked by another one. // LinkedByFilter is a note filter used to select notes being linked by another one.
type LinkedByFilter struct { type LinkedByFilter struct {
Paths []string Paths []string
Negate bool Negate bool
Recursive bool Recursive bool
MaxDistance int
} }
// LinkingToFilter is a note filter used to select notes being linked by another one. // LinkingToFilter is a note filter used to select notes being linked by another one.
type LinkingToFilter struct { type LinkingToFilter struct {
Paths []string Paths []string
Negate bool Negate bool
Recursive bool Recursive bool
MaxDistance int
} }
// OrphanFilter is a note filter used to select notes having no other notes linking to them. // OrphanFilter is a note filter used to select notes having no other notes linking to them.

Loading…
Cancel
Save