From: David Bremner <david@tethera.net>
To: notmuch@notmuchmail.org
Subject: [PATCH] lib: add sexp: prefix to Xapian (infix) query parser.
Date: Sat, 19 Mar 2022 09:50:22 -0300 [thread overview]
Message-ID: <20220319125022.1754951-1-david@tethera.net> (raw)
This is analogous to the "infix" prefix provided by the s-expression
based query parser.
---
One of the motivations for the s-expression parser is to provide some
features that the infix parser lacks. In particular the wildcard
support is more general in the s-expression parser. This patch allows
users to use those features from the s-expression parser without
converting their entire query.
doc/man7/notmuch-search-terms.rst | 17 ++++++++++++-
lib/Makefile.local | 3 ++-
lib/prefix.cc | 5 ++++
lib/sexp-fp.cc | 40 ++++++++++++++++++++++++++++++
lib/sexp-fp.h | 41 +++++++++++++++++++++++++++++++
test/T081-sexpr-search.sh | 32 ++++++++++++++++++++++++
6 files changed, 136 insertions(+), 2 deletions(-)
create mode 100644 lib/sexp-fp.cc
create mode 100644 lib/sexp-fp.h
diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst
index f8ad1edb..4f616b7e 100644
--- a/doc/man7/notmuch-search-terms.rst
+++ b/doc/man7/notmuch-search-terms.rst
@@ -169,6 +169,12 @@ property:<key>=<value>
can be present on a given message with several different values.
See :any:`notmuch-properties(7)` for more details.
+sexp:<subquery>
+ The **sexp:** prefix allows subqueries in the format
+ documented in :any:`notmuch-sexp-queries(7)`. Note that subqueries containing
+ spaces must be quoted, and any embedded double quotes must be escaped
+ (see :any:`quoting`).
+
User defined prefixes are also supported, see :any:`notmuch-config(1)` for
details.
@@ -257,7 +263,7 @@ Boolean
Probabilistic
**body:**, **to:**, **attachment:**, **mimetype:**
Special
- **from:**, **query:**, **subject:**
+ **from:**, **query:**, **subject:**, **sexp:**
Terms and phrases
-----------------
@@ -297,6 +303,8 @@ Both of these will match a subject "Free Delicious Pizza" while
will not.
+.. _quoting:
+
Quoting
-------
@@ -324,6 +332,13 @@ e.g.
% notmuch search 'folder:"/^.*/(Junk|Spam)$/"'
% notmuch search 'thread:"{from:mallory and date:2009}" and thread:{to:mallory}'
+Double quotes within query strings need to be doubled to escape them.
+
+::
+
+ % notmuch search 'tag:"""quoted tag"""'
+ % notmuch search 'sexp:"(or ""wizard"" ""php"")"'
+
DATE AND TIME SEARCH
====================
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 1378a74b..6d67a2a4 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -64,7 +64,8 @@ libnotmuch_cxx_srcs = \
$(dir)/prefix.cc \
$(dir)/open.cc \
$(dir)/init.cc \
- $(dir)/parse-sexp.cc
+ $(dir)/parse-sexp.cc \
+ $(dir)/sexp-fp.cc
libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
diff --git a/lib/prefix.cc b/lib/prefix.cc
index 857c05b9..06e2333a 100644
--- a/lib/prefix.cc
+++ b/lib/prefix.cc
@@ -3,6 +3,7 @@
#include "thread-fp.h"
#include "regexp-fields.h"
#include "parse-time-vrp.h"
+#include "sexp-fp.h"
typedef struct {
const char *name;
@@ -60,6 +61,8 @@ prefix_t prefix_table[] = {
NOTMUCH_FIELD_PROCESSOR },
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
+ { "sexp", NULL, NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROCESSOR },
{ "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROBABILISTIC |
NOTMUCH_FIELD_PROCESSOR },
@@ -138,6 +141,8 @@ _setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
+ else if (STRNCMP_LITERAL (prefix->name, "sexp") == 0)
+ fp = (new SexpFieldProcessor (notmuch))->release ();
else
fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
*notmuch->query_parser, notmuch))->release ();
diff --git a/lib/sexp-fp.cc b/lib/sexp-fp.cc
new file mode 100644
index 00000000..ed26f6ec
--- /dev/null
+++ b/lib/sexp-fp.cc
@@ -0,0 +1,40 @@
+/* sexp-fp.cc - "sexp:" field processor glue
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2022 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/ .
+ *
+ * Author: David Bremner <david@tethera.net>
+ */
+
+#include "database-private.h"
+#include "sexp-fp.h"
+#include <iostream>
+
+Xapian::Query
+SexpFieldProcessor::operator() (const std::string & query_string)
+{
+ notmuch_status_t status;
+ Xapian::Query output;
+
+ status = _notmuch_sexp_string_to_xapian_query (notmuch, query_string.c_str (), output);
+ if (status) {
+ throw Xapian::QueryParserError ("error parsing " + query_string);
+ }
+
+ return output;
+
+}
diff --git a/lib/sexp-fp.h b/lib/sexp-fp.h
new file mode 100644
index 00000000..341dfa7e
--- /dev/null
+++ b/lib/sexp-fp.h
@@ -0,0 +1,41 @@
+/* sexp-fp.h - sexp field processor glue
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2022 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/ .
+ *
+ * Author: David Bremner <david@tethera.net>
+ */
+
+#ifndef NOTMUCH_SEXP_FP_H
+#define NOTMUCH_SEXP_FP_H
+
+#include <xapian.h>
+#include "notmuch.h"
+
+class SexpFieldProcessor : public Xapian::FieldProcessor {
+protected:
+ notmuch_database_t *notmuch;
+
+public:
+ SexpFieldProcessor (notmuch_database_t *notmuch_) : notmuch (notmuch_)
+ {
+ };
+
+ Xapian::Query operator() (const std::string & query_string);
+};
+
+#endif /* NOTMUCH_SEXP_FP_H */
diff --git a/test/T081-sexpr-search.sh b/test/T081-sexpr-search.sh
index e2936cd7..da819190 100755
--- a/test/T081-sexpr-search.sh
+++ b/test/T081-sexpr-search.sh
@@ -31,6 +31,13 @@ thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "and of stemmed terms"
+notmuch search --query=sexp '(and wonderful wizard)' | notmuch_search_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "or of exact terms"
notmuch search --query=sexp '(or "php" "wizard")' | notmuch_search_sanitize > OUTPUT
cat <<EOF > EXPECTED
@@ -39,6 +46,14 @@ thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "or of exact terms via field processor"
+notmuch search 'sexp:"(or ""php"" ""wizard"")"' | notmuch_search_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+thread:XXX 2010-12-29 [1/1] François Boulogne; [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
+thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "single term in body"
notmuch search --query=sexp 'wizard' | notmuch_search_sanitize>OUTPUT
cat <<EOF > EXPECTED
@@ -707,6 +722,11 @@ notmuch search property:foo=bar > EXPECTED
notmuch search --query=sexp '(property (rx foo=.*))' > OUTPUT
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "regexp 'property' search via field processor"
+notmuch search property:foo=bar > EXPECTED
+notmuch search 'sexp:"(property (rx foo=.*))"' > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "anchored 'tag' search"
notmuch search tag:signed > EXPECTED
notmuch search --query=sexp '(tag (rx ^si))' > OUTPUT
@@ -743,6 +763,13 @@ thread:XXX 2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packa
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "Compound subquery via field processor"
+notmuch search 'sexp:"(thread (of (from keithp) (subject Maildir)))"' | notmuch_search_sanitize > OUTPUT
+cat<<EOF > EXPECTED
+thread:XXX 2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "empty subquery"
notmuch search --query=sexp '(thread (of))' 1>OUTPUT 2>&1
notmuch search '*' > EXPECTED
@@ -969,6 +996,11 @@ grep -Ril List-Id ${MAIL_DIR} | sort | notmuch_dir_sanitize > EXPECTED
notmuch search --output=files --query=sexp '(List *)' | sort | notmuch_dir_sanitize > OUTPUT
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "wildcard search for user header via field processor"
+grep -Ril List-Id ${MAIL_DIR} | sort | notmuch_dir_sanitize > EXPECTED
+notmuch search --output=files 'sexp:"(List *)"' | sort | notmuch_dir_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "wildcard search for user header 2"
grep -Ril List-Id ${MAIL_DIR} | sort | notmuch_dir_sanitize > EXPECTED
notmuch search --output=files --query=sexp '(List (starts-with not))' | sort | notmuch_dir_sanitize > OUTPUT
--
2.35.1
\r
reply other threads:[~2022-03-19 12:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://notmuchmail.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220319125022.1754951-1-david@tethera.net \
--to=david@tethera.net \
--cc=notmuch@notmuchmail.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://yhetil.org/notmuch.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).