diff --git a/ui/src/cache.rs b/ui/src/cache.rs
index d2d64a95..ab2ef3d3 100644
--- a/ui/src/cache.rs
+++ b/ui/src/cache.rs
@@ -142,6 +142,8 @@ impl std::ops::BitOr for Query {
}
}
+pub use query_parser::query;
+
pub mod query_parser {
use super::Query::{self, *};
use melib::parsec::*;
@@ -173,7 +175,7 @@ pub mod query_parser {
pub fn not<'a>() -> impl Parser<'a, Query> {
move |input| {
whitespace_wrap(either(
- match_literal_anycase("or"),
+ match_literal_anycase("not"),
match_literal_anycase("!"),
))
.parse(input)
@@ -213,8 +215,8 @@ pub mod query_parser {
Ok(q)
} else if let Ok(q) = from().parse(input) {
Ok(q)
- } else if let Ok(q) = not().parse(input) {
- Ok(q)
+ } else if let Ok((rest, query_a)) = not().parse(input) {
+ Ok((rest, Not(Box::new(query_a))))
} else if let Ok((rest, query_a)) = {
let result = literal().parse(input);
if result.is_ok()
diff --git a/ui/src/sqlite3.rs b/ui/src/sqlite3.rs
index d545a8e7..ae75723f 100644
--- a/ui/src/sqlite3.rs
+++ b/ui/src/sqlite3.rs
@@ -19,6 +19,9 @@
* along with meli. If not, see .
*/
+use crate::cache::query;
+use crate::cache::Query::{self, *};
+use crate::melib::parsec::Parser;
use melib::{
backends::MailBackend,
email::{Envelope, EnvelopeHash},
@@ -299,13 +302,25 @@ pub fn search(
SortOrder::Desc => "DESC",
};
+ /*
debug!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order);
let mut stmt = conn.prepare(
format!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order).as_str())
- .map_err(|e| MeliError::new(e.to_string()))?;
+ */
+ let mut stmt = conn
+ .prepare(
+ debug!(format!(
+ "SELECT hash FROM envelopes WHERE {} ORDER BY {} {};",
+ query_to_sql(&query().parse(term)?.1),
+ sort_field,
+ sort_order
+ ))
+ .as_str(),
+ )
+ .map_err(|e| MeliError::new(e.to_string()))?;
let results = stmt
- .query_map(&[fts5_bareword(term)], |row| Ok(row.get(0)?))
+ .query_map(rusqlite::NO_PARAMS, |row| Ok(row.get(0)?))
.map_err(|e| MeliError::new(e.to_string()))?
.map(|r: std::result::Result, rusqlite::Error>| {
Ok(u64::from_be_bytes(
@@ -339,3 +354,75 @@ pub fn from(term: &str) -> Result> {
.collect::>>();
results
}
+
+pub fn query_to_sql(q: &Query) -> String {
+ fn rec(q: &Query, s: &mut String) {
+ match q {
+ Subject(t) => {
+ s.push_str(" subject LIKE \"%");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("%\"");
+ }
+ From(t) => {
+ s.push_str(" _from LIKE \"%");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("%\"");
+ }
+ AllText(t) => {
+ s.push_str(" body_text LIKE \"%");
+ s.extend(escape_double_quote(t).chars());
+ s.push_str("%\"");
+ }
+ And(q1, q2) => {
+ s.push_str(" (");
+ rec(q1, s);
+ s.push_str(") ");
+
+ s.push_str(" AND ");
+ s.push_str(" (");
+ rec(q2, s);
+ s.push_str(") ");
+ }
+ Or(q1, q2) => {
+ s.push_str(" (");
+ rec(q1, s);
+ s.push_str(") ");
+ s.push_str(" OR ");
+ s.push_str(" (");
+ rec(q2, s);
+ s.push_str(") ");
+ }
+ Not(q) => {
+ s.push_str(" NOT ");
+ s.push_str("(");
+ rec(q, s);
+ s.push_str(")");
+ }
+ _ => {}
+ }
+ }
+ let mut ret = String::new();
+ rec(q, &mut ret);
+ ret
+
+ //"SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};"
+}
+
+#[test]
+fn test_query_to_sql() {
+ assert_eq!(
+ " subject LIKE \"%test%\" AND body_text LIKE \"%i%\"",
+ &query_to_sql(&query().parse_complete("subject: test and i").unwrap().1)
+ );
+ assert_eq!(
+ " subject LIKE \"%github%\" OR ( _from LIKE \"%epilys%\" AND ( subject LIKE \"%lib%\" OR subject LIKE \"%meli%\") ) ",
+ &query_to_sql(
+ &query()
+ .parse_complete(
+ "subject: github or (from: epilys and (subject:lib or subject: meli))"
+ )
+ .unwrap()
+ .1
+ )
+ );
+}