Exclude a tag with the --tag filtering option

pull/7/head
Mickaël Menu 3 years ago
parent f1391b5a63
commit bae1fab567
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -7,7 +7,9 @@ All notable changes to this project will be documented in this file.
### Added ### Added
* Support for tags. * Support for tags.
* Filter notes by their tags using `--tag "history, europe"`. To match notes associated with either tags, use a pipe `|` or `OR` (all caps), e.g. `--tag "inbox OR todo"`. * Filter notes by their tags using `--tag "history, europe"`.
* To match notes associated with either tags, use a pipe `|` or `OR` (all caps), e.g. `--tag "inbox OR todo"`.
* If you want to exclude notes having a particular tag, prefix it with `-` or `NOT` (all caps), e.g. `--tag "NOT done"`
* Many tag flavors are supported: `#hashtags`, `:colon:separated:tags:` and even Bear's [`#multi-word tags#`](https://blog.bear.app/2017/11/bear-tips-how-to-create-multi-word-tags/). If you prefer to use a YAML frontmatter, list your tags with the key `tags` or `keywords`. * Many tag flavors are supported: `#hashtags`, `:colon:separated:tags:` and even Bear's [`#multi-word tags#`](https://blog.bear.app/2017/11/bear-tips-how-to-create-multi-word-tags/). If you prefer to use a YAML frontmatter, list your tags with the key `tags` or `keywords`.
### Changed ### Changed

@ -442,11 +442,22 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
break break
} }
separatorRegex := regexp.MustCompile(`(\ OR\ )|\|`) separatorRegex := regexp.MustCompile(`(\ OR\ )|\|`)
for _, tags := range filter { for _, tagsArg := range filter {
tags := separatorRegex.Split(tags, -1) tags := separatorRegex.Split(tagsArg, -1)
negate := false
globs := make([]string, 0) globs := make([]string, 0)
for _, tag := range tags { for _, tag := range tags {
tag = strings.TrimSpace(tag)
if strings.HasPrefix(tag, "-") {
negate = true
tag = strings.TrimPrefix(tag, "-")
} else if strings.HasPrefix(tag, "NOT") {
negate = true
tag = strings.TrimPrefix(tag, "NOT")
}
tag = strings.TrimSpace(tag) tag = strings.TrimSpace(tag)
if len(tag) == 0 { if len(tag) == 0 {
continue continue
@ -455,13 +466,25 @@ func (d *NoteDAO) findRows(opts note.FinderOpts) (*sql.Rows, error) {
args = append(args, tag) args = append(args, tag)
} }
whereExprs = append(whereExprs, fmt.Sprintf(`n.id IN ( if len(globs) == 0 {
continue
}
if negate && len(globs) > 1 {
return nil, fmt.Errorf("cannot negate a tag in a OR group: %s", tagsArg)
}
expr := "n.id"
if negate {
expr += " NOT"
}
expr += fmt.Sprintf(` IN (
SELECT note_id FROM notes_collections SELECT note_id FROM notes_collections
WHERE collection_id IN (SELECT id FROM collections t WHERE kind = '%s' AND (%s)) WHERE collection_id IN (SELECT id FROM collections t WHERE kind = '%s' AND (%s))
)`, )`,
note.CollectionKindTag, note.CollectionKindTag,
strings.Join(globs, " OR "), strings.Join(globs, " OR "),
)) )
whereExprs = append(whereExprs, expr)
} }
case note.ExcludePathFilter: case note.ExcludePathFilter:

@ -418,6 +418,9 @@ func TestNoteDAOFindTag(t *testing.T) {
test([]string{"fiction | adventure | fantasy"}, []string{"ref/test/b.md", "f39c8.md", "log/2021-01-03.md"}) test([]string{"fiction | adventure | fantasy"}, []string{"ref/test/b.md", "f39c8.md", "log/2021-01-03.md"})
test([]string{"fiction | history", "adventure"}, []string{"ref/test/b.md", "log/2021-01-03.md"}) test([]string{"fiction | history", "adventure"}, []string{"ref/test/b.md", "log/2021-01-03.md"})
test([]string{"fiction", "unknown"}, []string{}) test([]string{"fiction", "unknown"}, []string{})
test([]string{"-fiction"}, []string{"ref/test/b.md", "f39c8.md", "ref/test/a.md", "log/2021-02-04.md", "index.md", "log/2021-01-04.md"})
test([]string{"NOT fiction"}, []string{"ref/test/b.md", "f39c8.md", "ref/test/a.md", "log/2021-02-04.md", "index.md", "log/2021-01-04.md"})
test([]string{"NOTfiction"}, []string{"ref/test/b.md", "f39c8.md", "ref/test/a.md", "log/2021-02-04.md", "index.md", "log/2021-01-04.md"})
} }
func TestNoteDAOFindMatch(t *testing.T) { func TestNoteDAOFindMatch(t *testing.T) {

Loading…
Cancel
Save