mirror of
https://github.com/mickael-menu/zk
synced 2024-11-03 23:15:49 +00:00
Limit the number of results
This commit is contained in:
parent
f2c41f35fc
commit
a7b85eeb1e
@ -146,14 +146,14 @@ func (d *NoteDAO) exists(path string) (bool, error) {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (d *NoteDAO) Find(callback func(note.Match) error, filters ...note.Filter) (int, error) {
|
||||
func (d *NoteDAO) Find(opts note.FinderOpts, callback func(note.Match) error) (int, error) {
|
||||
rows, err := func() (*sql.Rows, error) {
|
||||
snippetCol := `""`
|
||||
orderTerm := `n.title ASC`
|
||||
whereExprs := make([]string, 0)
|
||||
args := make([]interface{}, 0)
|
||||
|
||||
for _, filter := range filters {
|
||||
for _, filter := range opts.Filters {
|
||||
switch filter := filter.(type) {
|
||||
|
||||
case note.MatchFilter:
|
||||
@ -181,16 +181,19 @@ func (d *NoteDAO) Find(callback func(note.Match) error, filters ...note.Filter)
|
||||
query := "SELECT n.id, n.path, n.title, n.body, n.word_count, n.created, n.modified, n.checksum, " + snippetCol
|
||||
|
||||
query += `
|
||||
FROM notes n
|
||||
JOIN notes_fts
|
||||
ON n.id = notes_fts.rowid
|
||||
`
|
||||
FROM notes n
|
||||
JOIN notes_fts
|
||||
ON n.id = notes_fts.rowid`
|
||||
|
||||
if len(whereExprs) > 0 {
|
||||
query += " WHERE " + strings.Join(whereExprs, " AND ")
|
||||
query += "\nWHERE " + strings.Join(whereExprs, "\nAND ")
|
||||
}
|
||||
|
||||
query += " ORDER BY " + orderTerm
|
||||
query += "\nORDER BY " + orderTerm
|
||||
|
||||
if opts.Limit > 0 {
|
||||
query += fmt.Sprintf("\nLIMIT %d", opts.Limit)
|
||||
}
|
||||
|
||||
return d.tx.Query(query, args...)
|
||||
}()
|
||||
|
@ -142,7 +142,7 @@ func TestNoteDAORemoveUnknown(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoteDAOFindAll(t *testing.T) {
|
||||
testNoteDAOFind(t, []note.Match{
|
||||
testNoteDAOFind(t, note.FinderOpts{}, []note.Match{
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
@ -230,94 +230,8 @@ func TestNoteDAOFindAll(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestNoteDAOFindMatch(t *testing.T) {
|
||||
expected := []note.Match{
|
||||
{
|
||||
Snippet: "<zk:match>Index</zk:match> of the Zettelkasten",
|
||||
Metadata: note.Metadata{
|
||||
Path: "index.md",
|
||||
Title: "Index",
|
||||
Body: "Index of the Zettelkasten",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2019, 12, 4, 11, 59, 11, 0, time.Local),
|
||||
Modified: time.Date(2019, 12, 4, 12, 17, 21, 0, time.Local),
|
||||
Checksum: "iaefhv",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-03.md",
|
||||
Title: "January 3, 2021",
|
||||
Body: "A daily note",
|
||||
WordCount: 3,
|
||||
Created: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Checksum: "qwfpgj",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A second <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-04.md",
|
||||
Title: "January 4, 2021",
|
||||
Body: "A second daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "arstde",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A third <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-02-04.md",
|
||||
Title: "February 4, 2021",
|
||||
Body: "A third daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "earkte",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testNoteDAOFind(t, expected, note.MatchFilter("daily | index"))
|
||||
}
|
||||
|
||||
func TestNoteDAOFindInPath(t *testing.T) {
|
||||
expected := []note.Match{
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-03.md",
|
||||
Title: "January 3, 2021",
|
||||
Body: "A daily note",
|
||||
WordCount: 3,
|
||||
Created: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Checksum: "qwfpgj",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-04.md",
|
||||
Title: "January 4, 2021",
|
||||
Body: "A second daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "arstde",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testNoteDAOFind(t, expected, note.PathFilter([]string{"log/2021-01-*"}))
|
||||
}
|
||||
|
||||
func TestNoteDAOFindInMultiplePath(t *testing.T) {
|
||||
expected := []note.Match{
|
||||
func TestNoteDAOFindLimit(t *testing.T) {
|
||||
testNoteDAOFind(t, note.FinderOpts{Limit: 2}, []note.Match{
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
@ -333,39 +247,163 @@ func TestNoteDAOFindInMultiplePath(t *testing.T) {
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "ref/test/a.md",
|
||||
Title: "Another nested note",
|
||||
Body: "It shall appear before b.md",
|
||||
Path: "f39c8.md",
|
||||
Title: "An interesting note",
|
||||
Body: "Its content will surprise you",
|
||||
WordCount: 5,
|
||||
Created: time.Date(2019, 11, 20, 20, 32, 56, 0, time.Local),
|
||||
Modified: time.Date(2019, 11, 20, 20, 34, 6, 0, time.Local),
|
||||
Checksum: "iecywst",
|
||||
Created: time.Date(2020, 1, 19, 10, 58, 41, 0, time.Local),
|
||||
Modified: time.Date(2020, 1, 20, 8, 52, 42, 0, time.Local),
|
||||
Checksum: "irkwyc",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "index.md",
|
||||
Title: "Index",
|
||||
Body: "Index of the Zettelkasten",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2019, 12, 4, 11, 59, 11, 0, time.Local),
|
||||
Modified: time.Date(2019, 12, 4, 12, 17, 21, 0, time.Local),
|
||||
Checksum: "iaefhv",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testNoteDAOFind(t, expected, note.PathFilter([]string{"ref", "index.md"}))
|
||||
})
|
||||
}
|
||||
|
||||
func testNoteDAOFind(t *testing.T, expected []note.Match, filters ...note.Filter) {
|
||||
func TestNoteDAOFindMatch(t *testing.T) {
|
||||
testNoteDAOFind(t,
|
||||
note.FinderOpts{
|
||||
Filters: []note.Filter{note.MatchFilter("daily | index")},
|
||||
},
|
||||
[]note.Match{
|
||||
{
|
||||
Snippet: "<zk:match>Index</zk:match> of the Zettelkasten",
|
||||
Metadata: note.Metadata{
|
||||
Path: "index.md",
|
||||
Title: "Index",
|
||||
Body: "Index of the Zettelkasten",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2019, 12, 4, 11, 59, 11, 0, time.Local),
|
||||
Modified: time.Date(2019, 12, 4, 12, 17, 21, 0, time.Local),
|
||||
Checksum: "iaefhv",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-03.md",
|
||||
Title: "January 3, 2021",
|
||||
Body: "A daily note",
|
||||
WordCount: 3,
|
||||
Created: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Checksum: "qwfpgj",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A second <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-04.md",
|
||||
Title: "January 4, 2021",
|
||||
Body: "A second daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "arstde",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "A third <zk:match>daily</zk:match> note",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-02-04.md",
|
||||
Title: "February 4, 2021",
|
||||
Body: "A third daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "earkte",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestNoteDAOFindInPath(t *testing.T) {
|
||||
testNoteDAOFind(t,
|
||||
note.FinderOpts{
|
||||
Filters: []note.Filter{note.PathFilter([]string{"log/2021-01-*"})},
|
||||
},
|
||||
[]note.Match{
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-03.md",
|
||||
Title: "January 3, 2021",
|
||||
Body: "A daily note",
|
||||
WordCount: 3,
|
||||
Created: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 22, 16, 27, 45, 0, time.Local),
|
||||
Checksum: "qwfpgj",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "log/2021-01-04.md",
|
||||
Title: "January 4, 2021",
|
||||
Body: "A second daily note",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Modified: time.Date(2020, 11, 29, 8, 20, 18, 0, time.Local),
|
||||
Checksum: "arstde",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestNoteDAOFindInMultiplePath(t *testing.T) {
|
||||
testNoteDAOFind(t,
|
||||
note.FinderOpts{
|
||||
Filters: []note.Filter{note.PathFilter([]string{"ref", "index.md"})},
|
||||
},
|
||||
[]note.Match{
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "ref/test/b.md",
|
||||
Title: "A nested note",
|
||||
Body: "This one is in a sub sub directory",
|
||||
WordCount: 8,
|
||||
Created: time.Date(2019, 11, 20, 20, 32, 56, 0, time.Local),
|
||||
Modified: time.Date(2019, 11, 20, 20, 34, 6, 0, time.Local),
|
||||
Checksum: "yvwbae",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "ref/test/a.md",
|
||||
Title: "Another nested note",
|
||||
Body: "It shall appear before b.md",
|
||||
WordCount: 5,
|
||||
Created: time.Date(2019, 11, 20, 20, 32, 56, 0, time.Local),
|
||||
Modified: time.Date(2019, 11, 20, 20, 34, 6, 0, time.Local),
|
||||
Checksum: "iecywst",
|
||||
},
|
||||
},
|
||||
{
|
||||
Snippet: "",
|
||||
Metadata: note.Metadata{
|
||||
Path: "index.md",
|
||||
Title: "Index",
|
||||
Body: "Index of the Zettelkasten",
|
||||
WordCount: 4,
|
||||
Created: time.Date(2019, 12, 4, 11, 59, 11, 0, time.Local),
|
||||
Modified: time.Date(2019, 12, 4, 12, 17, 21, 0, time.Local),
|
||||
Checksum: "iaefhv",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func testNoteDAOFind(t *testing.T, opts note.FinderOpts, expected []note.Match) {
|
||||
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
|
||||
actual := make([]note.Match, 0)
|
||||
count, err := dao.Find(func(m note.Match) error {
|
||||
count, err := dao.Find(opts, func(m note.Match) error {
|
||||
actual = append(actual, m)
|
||||
return nil
|
||||
}, filters...)
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, count, len(expected))
|
||||
assert.Equal(t, actual, expected)
|
||||
|
10
cmd/list.go
10
cmd/list.go
@ -12,8 +12,9 @@ import (
|
||||
// List displays notes matching a set of criteria.
|
||||
type List struct {
|
||||
Paths []string `arg optional placeholder:"PATHS"`
|
||||
Match string `help:"Terms to search for in the notes" placeholder:"TERMS"`
|
||||
Format string `help:"Pretty prints the list using the given format" placeholder:"TEMPLATE"`
|
||||
Match string `help:"Terms to search for in the notes" placeholder:"TERMS"`
|
||||
Limit int `help:"Limit the number of results" placeholder:"MAX"`
|
||||
}
|
||||
|
||||
func (cmd *List) Run(container *Container) error {
|
||||
@ -49,8 +50,11 @@ func (cmd *List) Run(container *Container) error {
|
||||
|
||||
count, err := note.List(
|
||||
note.ListOpts{
|
||||
Format: opt.NewNotEmptyString(cmd.Format),
|
||||
Filters: filters,
|
||||
Format: opt.NewNotEmptyString(cmd.Format),
|
||||
FinderOpts: note.FinderOpts{
|
||||
Filters: filters,
|
||||
Limit: cmd.Limit,
|
||||
},
|
||||
},
|
||||
note.ListDeps{
|
||||
BasePath: zk.Path,
|
||||
|
@ -35,12 +35,17 @@ func (m Match) String() string {
|
||||
// Finder retrieves notes matching the given Filter.
|
||||
// Returns the number of matches.
|
||||
type Finder interface {
|
||||
Find(callback func(Match) error, filters ...Filter) (int, error)
|
||||
Find(opts FinderOpts, callback func(Match) error) (int, error)
|
||||
}
|
||||
|
||||
type FinderOpts struct {
|
||||
Filters []Filter
|
||||
Limit int
|
||||
}
|
||||
|
||||
type ListOpts struct {
|
||||
Format opt.String
|
||||
Filters []Filter
|
||||
Format opt.String
|
||||
FinderOpts
|
||||
}
|
||||
|
||||
type ListDeps struct {
|
||||
@ -58,7 +63,7 @@ func List(opts ListOpts, deps ListDeps, callback func(formattedNote string) erro
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return deps.Finder.Find(func(note Match) error {
|
||||
return deps.Finder.Find(opts.FinderOpts, func(note Match) error {
|
||||
ft, err := format(note, deps.BasePath, deps.Templates)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -68,7 +73,7 @@ func List(opts ListOpts, deps ListDeps, callback func(formattedNote string) erro
|
||||
return err
|
||||
}
|
||||
return callback(res)
|
||||
}, opts.Filters...)
|
||||
})
|
||||
}
|
||||
|
||||
var templates = map[string]string{
|
||||
|
Loading…
Reference in New Issue
Block a user