Support regular expressions with `--match` (#222)

pull/221/head
Mickaël Menu 2 years ago committed by GitHub
parent 1a05a04432
commit 4b76fbadf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
* Use regular expressions when searching for notes with `--match`.
```sh
# Find notes containing emails.
$ zk list --match-strategy re --match ".+@.+"
$ zk list -Mr -m ".+@.+"
```
### Changed
* The flags `--exact-match`/`-e` are deprecated in favor of `--match-strategy exact`/`-Me`.
### Deprecated
* The LSP server does not support resolving a wiki link to a note title anymore.

@ -45,11 +45,35 @@ $ zk list --linked-by "`zk inline journal`"
Use `--match <query>` (or `-m`) to search through the title and body of notes.
The search is powered by a [full-text search](https://en.wikipedia.org/wiki/Full-text_search) database enabling near-instant results. Queries are not case-sensitive and terms are tokenized, which means that searching for `create` will also match `created` and `creating`.
The search is powered by different strategies to answer various use cases:
* `fts` (default) uses a [full-text search](https://en.wikipedia.org/wiki/Full-text_search) database to offer near-instant results and advanced search operators.
* `exact` is useful if you need to find patterns containing special characters.
* `re` enables regular expression for advanced use cases.
Change the currently used strategy with `--match-strategy <strategy>` (or `-M`). To set the default strategy, you can declare a [custom alias](config-alias.md):
```toml
[alias]
list = "zk list --match-strategy re $@"
```
### Full-text search (`fts`)
The default match strategy is powered by a [full-text search](https://en.wikipedia.org/wiki/Full-text_search) database enabling near-instant results. Queries are not case-sensitive and terms are tokenized, which means that searching for `create` will also match `created` and `creating`.
A syntax similar to Google Search is available for advanced search queries.
### Combining terms
```sh
# FTS is the default match strategy
$ zk list --match "tesla OR edison"
# ...but you can enable it explicitly.
$ zk list --match-strategy fts --match "tesla OR edison"
$ zk list -Mf -m "tesla OR edison"
```
#### Combining terms
By default, the search engine will find the notes containing all the terms in the query, in any order.
@ -83,7 +107,7 @@ Finally, you can filter out results by excluding a term with `NOT` (all caps) or
"tesla -car"
```
### Search in specific fields
#### Search in specific fields
If you want to search only in the title or body of notes, prefix a query with `title:` or `body:`.
@ -92,7 +116,7 @@ If you want to search only in the title or body of notes, prefix a query with `t
"body: (tesla OR edison)"
```
### Prefix terms
#### Prefix terms
Match any term beginning with the given prefix with a wildcard `*`.
@ -106,13 +130,25 @@ Prefixing a query with `^` will match notes whose title or body start with the f
"title: ^journal"
```
### Search for special characters
### Exact matches (`exact`)
If you need to find patterns containing special characters, such as an `email@addre.ss` or a `[[wiki-link]]`, use the `--exact-match` / `-e` option. The search will be case-insensitive.
If you need to find patterns containing special characters, such as an `email@addre.ss` or a `[[wiki-link]]`, use the `exact` match strategy. The search will be case-insensitive.
```sh
$ zk list --match-strategy exact --match "[[link]]"
$ zk list -Me -m "[[link]]"
```
$ zk list --exact-match --match "[[link]]"
$ zk list -em "[[link]]"
### Regular expressions (`re`)
For advanced use cases, you can use the `re` match strategy to search the notebook using regular expressions. The supported syntax is similar to the one used by Python or Perl. [See the full reference](https://golang.org/s/re2syntax).
:warning: Make sure to use quotes to prevent your shell from expanding wildcards.
```sh
# Find notes containing emails.
$ zk list --match-strategy re --match ".+@.+"
$ zk list -Mr -m ".+@.+"
```
## Filter by tags

@ -380,8 +380,8 @@ func (d *NoteDAO) expandMentionsIntoMatch(opts core.NoteFindOpts) (core.NoteFind
if opts.Mention == nil {
return opts, nil
}
if opts.ExactMatch {
return opts, fmt.Errorf("--exact-match and --mention cannot be used together")
if opts.MatchStrategy != core.MatchStrategyFts {
return opts, fmt.Errorf("--mention can only be used with --match-strategy=fts")
}
// Find the IDs for the mentioned paths.
@ -518,15 +518,20 @@ func (d *NoteDAO) findRows(opts core.NoteFindOpts, selection noteSelection) (*sq
}
if !opts.Match.IsNull() {
if opts.ExactMatch {
switch opts.MatchStrategy {
case core.MatchStrategyExact:
whereExprs = append(whereExprs, `n.raw_content LIKE '%' || ? || '%' ESCAPE '\'`)
args = append(args, escapeLikeTerm(opts.Match.String(), '\\'))
} else {
case core.MatchStrategyFts:
snippetCol = `snippet(fts_match.notes_fts, 2, '<zk:match>', '</zk:match>', '…', 20)`
joinClauses = append(joinClauses, "JOIN notes_fts fts_match ON n.id = fts_match.rowid")
additionalOrderTerms = append(additionalOrderTerms, `bm25(fts_match.notes_fts, 1000.0, 500.0, 1.0)`)
whereExprs = append(whereExprs, "fts_match.notes_fts MATCH ?")
args = append(args, fts5.ConvertQuery(opts.Match.String()))
case core.MatchStrategyRe:
whereExprs = append(whereExprs, "n.raw_content REGEXP ?")
args = append(args, opts.Match.String())
break
}
}

@ -312,9 +312,10 @@ func TestNoteDAOFindMinimalAll(t *testing.T) {
func TestNoteDAOFindMinimalWithFilter(t *testing.T) {
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
notes, err := dao.FindMinimal(core.NoteFindOpts{
Match: opt.NewString("daily | index"),
Sorters: []core.NoteSorter{{Field: core.NoteSortWordCount, Ascending: true}},
Limit: 3,
Match: opt.NewString("daily | index"),
MatchStrategy: core.MatchStrategyFts,
Sorters: []core.NoteSorter{{Field: core.NoteSortWordCount, Ascending: true}},
Limit: 3,
})
assert.Nil(t, err)
@ -366,7 +367,10 @@ func TestNoteDAOFindTag(t *testing.T) {
func TestNoteDAOFindMatch(t *testing.T) {
testNoteDAOFind(t,
core.NoteFindOpts{Match: opt.NewString("daily | index")},
core.NoteFindOpts{
Match: opt.NewString("daily | index"),
MatchStrategy: core.MatchStrategyFts,
},
[]core.ContextualNote{
{
Note: core.Note{
@ -451,7 +455,8 @@ func TestNoteDAOFindMatch(t *testing.T) {
func TestNoteDAOFindMatchWithSort(t *testing.T) {
testNoteDAOFindPaths(t,
core.NoteFindOpts{
Match: opt.NewString("daily | index"),
Match: opt.NewString("daily | index"),
MatchStrategy: core.MatchStrategyFts,
Sorters: []core.NoteSorter{
{Field: core.NoteSortPath, Ascending: false},
},
@ -469,8 +474,8 @@ func TestNoteDAOFindExactMatch(t *testing.T) {
test := func(match string, expected []string) {
testNoteDAOFindPaths(t,
core.NoteFindOpts{
Match: opt.NewString(match),
ExactMatch: true,
Match: opt.NewString(match),
MatchStrategy: core.MatchStrategyExact,
},
expected,
)
@ -482,13 +487,27 @@ func TestNoteDAOFindExactMatch(t *testing.T) {
test(`[exact% ch\ar_acters]`, []string{"ref/test/a.md"})
}
func TestNoteDAOFindExactMatchCannotBeUsedWithMention(t *testing.T) {
func TestNoteDAOFindMentionRequiresFtsMatchStrategy(t *testing.T) {
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
_, err := dao.Find(core.NoteFindOpts{
MatchStrategy: core.MatchStrategyExact,
Mention: []string{"mention"},
})
assert.Err(t, err, "--mention can only be used with --match-strategy=fts")
})
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
_, err := dao.Find(core.NoteFindOpts{
MatchStrategy: core.MatchStrategyRe,
Mention: []string{"mention"},
})
assert.Err(t, err, "--mention can only be used with --match-strategy=fts")
})
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
_, err := dao.Find(core.NoteFindOpts{
ExactMatch: true,
Mention: []string{"mention"},
MatchStrategy: core.MatchStrategyFts,
Mention: []string{"mention"},
})
assert.Err(t, err, "--exact-match and --mention cannot be used together")
assert.Err(t, err, "could not find notes at: mention")
})
}
@ -559,7 +578,10 @@ func TestNoteDAOFindExcludingMultiplePaths(t *testing.T) {
func TestNoteDAOFindMentions(t *testing.T) {
testNoteDAOFind(t,
core.NoteFindOpts{Mention: []string{"log/2021-01-03.md", "index.md"}},
core.NoteFindOpts{
MatchStrategy: core.MatchStrategyFts,
Mention: []string{"log/2021-01-03.md", "index.md"},
},
[]core.ContextualNote{
{
Note: core.Note{
@ -623,7 +645,8 @@ func TestNoteDAOFindMentions(t *testing.T) {
func TestNoteDAOFindUnlinkedMentions(t *testing.T) {
testNoteDAOFindPaths(t,
core.NoteFindOpts{
Mention: []string{"log/2021-01-03.md", "index.md"},
MatchStrategy: core.MatchStrategyFts,
Mention: []string{"log/2021-01-03.md", "index.md"},
LinkTo: &core.LinkFilter{
Hrefs: []string{"log/2021-01-03.md", "index.md"},
Negate: true,
@ -636,7 +659,8 @@ func TestNoteDAOFindUnlinkedMentions(t *testing.T) {
func TestNoteDAOFindMentionUnknown(t *testing.T) {
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
opts := core.NoteFindOpts{
Mention: []string{"will-not-be-found"},
MatchStrategy: core.MatchStrategyFts,
Mention: []string{"will-not-be-found"},
}
_, err := dao.Find(opts)
assert.Err(t, err, "could not find notes at: will-not-be-found")
@ -645,7 +669,10 @@ func TestNoteDAOFindMentionUnknown(t *testing.T) {
func TestNoteDAOFindMentionedBy(t *testing.T) {
testNoteDAOFind(t,
core.NoteFindOpts{MentionedBy: []string{"ref/test/b.md", "log/2021-01-04.md"}},
core.NoteFindOpts{
MatchStrategy: core.MatchStrategyFts,
MentionedBy: []string{"ref/test/b.md", "log/2021-01-04.md"},
},
[]core.ContextualNote{
{
Note: core.Note{
@ -697,7 +724,8 @@ func TestNoteDAOFindMentionedBy(t *testing.T) {
func TestNoteDAOFindUnlinkedMentionedBy(t *testing.T) {
testNoteDAOFindPaths(t,
core.NoteFindOpts{
MentionedBy: []string{"ref/test/b.md", "log/2021-01-04.md"},
MatchStrategy: core.MatchStrategyFts,
MentionedBy: []string{"ref/test/b.md", "log/2021-01-04.md"},
LinkedBy: &core.LinkFilter{
Hrefs: []string{"ref/test/b.md", "log/2021-01-04.md"},
Negate: true,
@ -710,7 +738,8 @@ func TestNoteDAOFindUnlinkedMentionedBy(t *testing.T) {
func TestNoteDAOFindMentionedByUnknown(t *testing.T) {
testNoteDAO(t, func(tx Transaction, dao *NoteDAO) {
opts := core.NoteFindOpts{
MentionedBy: []string{"will-not-be-found"},
MatchStrategy: core.MatchStrategyFts,
MentionedBy: []string{"will-not-be-found"},
}
_, err := dao.Find(opts)
assert.Err(t, err, "could not find notes at: will-not-be-found")

@ -139,6 +139,10 @@ func NewContainer(version string) (*Container, error) {
// XDG Base Directory specification
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
func locateGlobalConfig() (string, error) {
if _, ok := os.LookupEnv("RUNNING_TESH"); ok {
return "", nil
}
configPath := filepath.Join(globalConfigDir(), "config.toml")
exists, err := paths.Exists(configPath)
switch {

@ -20,7 +20,7 @@ type Filtering struct {
Interactive bool `kong:"group='filter',short='i',help='Select notes interactively with fzf.'" json:"-"`
Limit int `kong:"group='filter',short='n',placeholder='COUNT',help='Limit the number of notes found.'" json:"limit"`
Match string `kong:"group='filter',short='m',placeholder='QUERY',help='Terms to search for in the notes.'" json:"match"`
ExactMatch bool `kong:"group='filter',short='e',help='Search for exact occurrences of the --match argument (case insensitive).'" json:"exactMatch"`
MatchStrategy string `kong:"group='filter',short='M',default='fts',placeholder='STRATEGY',help='Text matching strategy among: fts, re, exact.'" json:"matchStrategy"`
Exclude []string `kong:"group='filter',short='x',placeholder='PATH',help='Ignore notes matching the given path, including its descendants.'" json:"excludeHrefs"`
Tag []string `kong:"group='filter',short='t',help='Find notes tagged with the given tags.'" json:"tags"`
Mention []string `kong:"group='filter',placeholder='PATH',help='Find notes mentioning the title of the given ones.'" json:"mention"`
@ -41,6 +41,9 @@ type Filtering struct {
ModifiedAfter string `kong:"group='filter',placeholder='DATE',help='Find notes modified after the given date.'" json:"modifiedAfter"`
Sort []string `kong:"group='sort',short='s',placeholder='TERM',help='Order the notes by the given criterion.'" json:"sort"`
// Deprecated
ExactMatch bool `kong:"hidden,short='e'" json:"exactMatch"`
}
// ExpandNamedFilters expands recursively any named filter found in the Path field.
@ -119,6 +122,9 @@ func (f Filtering) ExpandNamedFilters(filters map[string]string, expandedFilters
} else if parsedFilter.Match != "" {
f.Match = fmt.Sprintf("(%s) AND (%s)", f.Match, parsedFilter.Match)
}
if f.MatchStrategy == "" {
f.MatchStrategy = parsedFilter.MatchStrategy
}
} else {
actualPaths = append(actualPaths, path)
@ -138,8 +144,15 @@ func (f Filtering) NewNoteFindOpts(notebook *core.Notebook) (core.NoteFindOpts,
return opts, err
}
if f.ExactMatch {
return opts, fmt.Errorf("the --exact-match (-e) option is deprecated, use --match-strategy=exact (-Me) instead")
}
opts.Match = opt.NewNotEmptyString(f.Match)
opts.ExactMatch = f.ExactMatch
opts.MatchStrategy, err = core.MatchStrategyFromString(f.MatchStrategy)
if err != nil {
return opts, err
}
if paths, ok := relPaths(notebook, f.Path); ok {
opts.IncludeHrefs = paths

@ -11,10 +11,10 @@ import (
// NoteFindOpts holds a set of filtering options used to find notes.
type NoteFindOpts struct {
// Filter used to match the notes with FTS predicates.
// Filter used to match the notes with the given MatchStrategy.
Match opt.String
// Search for exact occurrences of the Match string.
ExactMatch bool
// Text matching strategy used with Match.
MatchStrategy MatchStrategy
// Filter by note hrefs.
IncludeHrefs []string
// Filter excluding notes at the given hrefs.
@ -161,3 +161,29 @@ func NoteSorterFromString(str string) (NoteSorter, error) {
return sorter, nil
}
// MatchStrategy represents a text matching strategy used when filtering notes with `--match`.
type MatchStrategy int
const (
// Full text search.
MatchStrategyFts MatchStrategy = iota + 1
// Exact text matching.
MatchStrategyExact
// Regular expression.
MatchStrategyRe
)
// MatchStrategyFromString returns a MatchStrategy from its string representation.
func MatchStrategyFromString(str string) (MatchStrategy, error) {
switch str {
case "fts", "f", "":
return MatchStrategyFts, nil
case "re", "grep", "r":
return MatchStrategyRe, nil
case "exact", "e":
return MatchStrategyExact, nil
default:
return 0, fmt.Errorf("%s: unknown match strategy\ntry fts (full-text search), re (regular expression) or exact", str)
}
}

@ -67,3 +67,24 @@ func TestSortersFromStrings(t *testing.T) {
_, err := NoteSortersFromStrings([]string{"c", "foobar"})
assert.Err(t, err, "foobar: unknown sorting term")
}
func TestMatchStrategyFromString(t *testing.T) {
test := func(str string, expected MatchStrategy) {
actual, err := MatchStrategyFromString(str)
assert.Nil(t, err)
assert.Equal(t, actual, expected)
}
test("f", MatchStrategyFts)
test("fts", MatchStrategyFts)
test("r", MatchStrategyRe)
test("re", MatchStrategyRe)
test("grep", MatchStrategyRe)
test("e", MatchStrategyExact)
test("exact", MatchStrategyExact)
_, err := MatchStrategyFromString("foobar")
assert.Err(t, err, "foobar: unknown match strategy\ntry fts (full-text search), re (regular expression) or exact")
}

@ -25,8 +25,7 @@ $ zk graph --help
> -i, --interactive Select notes interactively with fzf.
> -n, --limit=COUNT Limit the number of notes found.
> -m, --match=QUERY Terms to search for in the notes.
> -e, --exact-match Search for exact occurrences of the --match
> argument (case insensitive).
> -M, --match-strategy="fts" Text matching strategy among: fts, re, exact.
> -x, --exclude=PATH,... Ignore notes matching the given path, including
> its descendants.
> -t, --tag=TAG,... Find notes tagged with the given tags.

@ -0,0 +1,17 @@
# Exact search match strategy.
$ cd full-sample
# Long flag.
$ zk list -q --debug-style --match-strategy exact --match '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>
# Short flag.
$ zk list -q --debug-style -Me --match '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>

@ -0,0 +1,113 @@
# Full-text search match strategy.
$ cd full-sample
# Search for a multi-word term.
$ zk list -q --debug-style --match '"green thread"'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green threads</term>, and languages that use these <term>green threads</term> will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …so it doesn't support [<term>green threads</term>](inbox/my59).
> * Crates exist to add support for <term>green threads</term> if needed.
> * Instead, Rust relies…
>
# Search for two terms (defaults to AND).
$ zk list -q --debug-style --match 'green channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with explicit AND.
$ zk list -q --debug-style --match 'green AND channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with OR.
$ zk list -q --debug-style --match 'green OR channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - * <term>Channels</term> are a great approach for safe concurrency.
> * It's an implementation of the [message passing](4oma) pattern.
>
> :programming:
>
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - * A popular approach for safe concurrency is to use *message passing* instead of shared state.
> * <term>Channels</term> are an example of…
>
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - …with a *locking system*.
> * Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
> * The main risk is to…
>
# Exclude a term.
$ zk list -q --debug-style --match 'green -channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
# Search in the `title` field.
$ zk list -q --debug-style --match 'title:(green thread)'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - > Many operating systems provide an API for creating new threads. This model where a language calls the operating system APIs…
>
# Search with a prefix in `title`.
$ zk list -q --debug-style --match 'title:^do*'
><title>Do not communicate by sharing memory; instead, share memory by communicating</title> <path>ref/7fto.md</path> (just now)
>
> - * Advocates for the use of [message passing](4oma) instead of shared state.
> * A slogan initially coined by Rob Pike ([Effective…
>
><title>Don't speculate</title> <path>pywo.md</path> (just now)
>
> - You have more to loose by doing market timing (jumping in and out of the stocks market). Remember, [the less…
>
# Search with a prefix.
$ zk list -q --debug-style --match 'mut*'
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - * Abbreviation of *<term>mutual</term> exclusion*.
> * An approach to manage safely shared state by allowing only a single thread to access a…
>
><title>Data race error</title> <path>3403.md</path> (just now)
>
> - …Rust prevents *data races* by allowing only a single <term>mutable</term> reference of a value per scope.
>
> :programming:
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …data between threads:
> * [Channel](fwsj) for a safe [message passing](4oma) approach.
> * [<term>Mutex</term>](inbox/er4k) for managing shared state.
>
> :rust:programming:
>

@ -0,0 +1,17 @@
# Regular expression match strategy.
$ cd full-sample
# Long flag.
$ zk list -q --debug-style --match-strategy re --match 'न.*ते'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>
# Short flag.
$ zk list -q --debug-style -Mr --match 'न.*ते'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>

@ -1,126 +1,9 @@
$ cd full-sample
$ cd blank
# Search for a multi-word term.
$ zk list -q --debug-style --match '"green thread"'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green threads</term>, and languages that use these <term>green threads</term> will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …so it doesn't support [<term>green threads</term>](inbox/my59).
> * Crates exist to add support for <term>green threads</term> if needed.
> * Instead, Rust relies…
>
# Search for two terms (defaults to AND).
$ zk list -q --debug-style --match 'green channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with explicit AND.
$ zk list -q --debug-style --match 'green AND channel'
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
# Search for two terms with OR.
$ zk list -q --debug-style --match 'green OR channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …runtime, so it doesn't support [<term>green</term> threads](inbox/my59).
> * Crates exist to add support for <term>green</term> threads if needed.
> * Instead, Rust…
>
><title>Channel</title> <path>fwsj.md</path> (just now)
>
> - * <term>Channels</term> are a great approach for safe concurrency.
> * It's an implementation of the [message passing](4oma) pattern.
>
> :programming:
>
><title>Message passing</title> <path>4oma.md</path> (just now)
>
> - * A popular approach for safe concurrency is to use *message passing* instead of shared state.
> * <term>Channels</term> are an example of…
>
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - …with a *locking system*.
> * Managing mutexes is tricky, using [<term>channels</term>](../fwsj) is an easier alternative.
> * The main risk is to…
>
# Exclude a term.
$ zk list -q --debug-style --match 'green -channel'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - …Programming language-provided threads are known as <term>green</term> threads, and languages that use these <term>green</term> threads will execute them in…
>
# Search in the `title` field.
$ zk list -q --debug-style --match 'title:(green thread)'
><title>Green threads</title> <path>inbox/my59.md</path> (just now)
>
> - > Many operating systems provide an API for creating new threads. This model where a language calls the operating system APIs…
>
# Search with a prefix in `title`.
$ zk list -q --debug-style --match 'title:^do*'
><title>Do not communicate by sharing memory; instead, share memory by communicating</title> <path>ref/7fto.md</path> (just now)
>
> - * Advocates for the use of [message passing](4oma) instead of shared state.
> * A slogan initially coined by Rob Pike ([Effective…
>
><title>Don't speculate</title> <path>pywo.md</path> (just now)
>
> - You have more to loose by doing market timing (jumping in and out of the stocks market). Remember, [the less…
>
# Search with a prefix.
$ zk list -q --debug-style --match 'mut*'
><title>Mutex</title> <path>inbox/er4k.md</path> (just now)
>
> - * Abbreviation of *<term>mutual</term> exclusion*.
> * An approach to manage safely shared state by allowing only a single thread to access a…
>
><title>Data race error</title> <path>3403.md</path> (just now)
>
> - …Rust prevents *data races* by allowing only a single <term>mutable</term> reference of a value per scope.
>
> :programming:
>
><title>Concurrency in Rust</title> <path>g7qa.md</path> (just now)
>
> - …data between threads:
> * [Channel](fwsj) for a safe [message passing](4oma) approach.
> * [<term>Mutex</term>](inbox/er4k) for managing shared state.
>
> :rust:programming:
>
# Search for an exact match.
$ zk list -q --debug-style --exact-match --match '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>
# Search for an exact match (short flag).
$ zk list -q --debug-style -em '["न", "म", "स्", "ते"]'
><title>Strings are a complicated data structure</title> <path>oumc.md</path> (just now)
>
> - Given the Hindi word "नमस्ते":
>
# --exact-match is deprecated.
1$ zk list -q --exact-match
2>zk: error: incorrect criteria: the --exact-match (-e) option is deprecated, use --match-strategy=exact (-Me) instead
# --exact-match is deprecated (short flag).
1$ zk list -q -e
2>zk: error: incorrect criteria: the --exact-match (-e) option is deprecated, use --match-strategy=exact (-Me) instead

@ -33,8 +33,7 @@ $ zk list --help
> -i, --interactive Select notes interactively with fzf.
> -n, --limit=COUNT Limit the number of notes found.
> -m, --match=QUERY Terms to search for in the notes.
> -e, --exact-match Search for exact occurrences of the --match
> argument (case insensitive).
> -M, --match-strategy="fts" Text matching strategy among: fts, re, exact.
> -x, --exclude=PATH,... Ignore notes matching the given path, including
> its descendants.
> -t, --tag=TAG,... Find notes tagged with the given tags.

Loading…
Cancel
Save