From e396b2f72bc322519148c4cbd8756dc7d93272ef Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 7 Nov 2019 19:14:38 +0200 Subject: [PATCH] ui: add query translation to SQL SELECTs --- ui/src/cache.rs | 8 +++-- ui/src/sqlite3.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) 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 + ) + ); +}