From 0ca4ad2670b22e975a018f9f662ea3a762840583 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 24 Aug 2021 08:17:25 -0700 Subject: [PATCH] lib/parse-sexp: add '*' as syntactic sugar for '(starts-with "")' Users that insist on using a literal '*' as a tag, can continue to do so by quoting it when searching. --- doc/man7/notmuch-sexp-queries.rst | 19 ++++++++++-- lib/parse-sexp.cc | 5 ++++ test/T081-sexpr-search.sh | 48 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/doc/man7/notmuch-sexp-queries.rst b/doc/man7/notmuch-sexp-queries.rst index c83ce3d0..f32bab9c 100644 --- a/doc/man7/notmuch-sexp-queries.rst +++ b/doc/man7/notmuch-sexp-queries.rst @@ -36,8 +36,11 @@ An s-expression query is either an atom, the empty list, or a a *field*, *logical operation*, or *modifier*, and 0 or more subqueries. -``*`` ``()`` - Match all messages. +``*`` + "*" matches any non-empty string in the current field. + +``()`` + The empty list matches all messages *term* @@ -138,6 +141,15 @@ from terms, operators, and modifiers, but not other fields. MODIFIERS ````````` +*Modifiers* refer to any prefixes (first elements of compound queries) +that are neither operators nor fields. + +``(starts-with`` *subword* ``)`` + Matches any term starting with *subword*. This applies in either + phrase or term :any:`fields `, or outside of fields [#not-body]_. Note that + a ``starts-with`` query cannot be part of a phrase. The + atom ``*`` is a synonym for ``(starts-with "")``. + EXAMPLES ======== @@ -181,6 +193,9 @@ EXAMPLES "mallory@example.org", and also "bob@example.com.au" since it contains the adjacent triple "bob", "example", "com". +``(not (to *))`` + Match messages with an empty or invalid 'To' and 'Cc' field. + NOTES ===== diff --git a/lib/parse-sexp.cc b/lib/parse-sexp.cc index 692b3849..ffb00148 100644 --- a/lib/parse-sexp.cc +++ b/lib/parse-sexp.cc @@ -176,6 +176,11 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent std::string term = Xapian::Unicode::tolower (sx->val); Xapian::Stem stem = *(notmuch->stemmer); std::string term_prefix = parent ? _find_prefix (parent->name) : ""; + + if (sx->aty == SEXP_BASIC && strcmp (sx->val, "*") == 0) { + return _sexp_parse_wildcard (notmuch, parent, "", output); + } + if (parent && (parent->flags & SEXP_FLAG_BOOLEAN)) { output = Xapian::Query (term_prefix + sx->val); return NOTMUCH_STATUS_SUCCESS; diff --git a/test/T081-sexpr-search.sh b/test/T081-sexpr-search.sh index 24c6edd1..df502dc5 100755 --- a/test/T081-sexpr-search.sh +++ b/test/T081-sexpr-search.sh @@ -386,6 +386,46 @@ thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unr EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "wildcard search for 'is'" +notmuch search not id:${notag_mid} > EXPECTED +notmuch search --query=sexp '(is *)' > OUTPUT +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "negated wildcard search for 'is'" +notmuch search id:${notag_mid} > EXPECTED +notmuch search --query=sexp '(not (is *))' > OUTPUT +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "wildcard search for 'property'" +notmuch search property:foo=bar > EXPECTED +notmuch search --query=sexp '(property *)' > OUTPUT +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "wildcard search for 'tag'" +notmuch search not id:${notag_mid} > EXPECTED +notmuch search --query=sexp '(tag *)' > OUTPUT +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "negated wildcard search for 'tag'" +notmuch search id:${notag_mid} > EXPECTED +notmuch search --query=sexp '(not (tag *))' > OUTPUT +test_expect_equal_file EXPECTED OUTPUT + +add_message '[subject]="message with tag \"*\""' +notmuch tag '+*' id:${gen_msg_id} + +test_begin_subtest "search for 'tag' \"*\"" +output=$(notmuch search --query=sexp --output=messages '(tag "*")') +test_expect_equal "$output" "id:$gen_msg_id" + +test_begin_subtest "search for missing / empty to" +add_message [to]="undisclosed-recipients:" +notmuch search --query=sexp '(not (to *))' | notmuch_search_sanitize > OUTPUT +cat < EXPECTED +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; search for missing / empty to (inbox unread) +EOF +test_expect_equal_file EXPECTED OUTPUT + test_begin_subtest "Unbalanced parens" # A code 1 indicates the error was handled (a crash will return e.g. 139). test_expect_code 1 "notmuch search --query=sexp '('" @@ -454,4 +494,12 @@ notmuch search: Syntax error in query EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "wildcard, illegal field" +notmuch search --query=sexp '(body *)' >OUTPUT 2>&1 +cat < EXPECTED +notmuch search: Syntax error in query +'body' does not support wildcard queries +EOF +test_expect_equal_file EXPECTED OUTPUT + test_done