ui: add query translation to SQL SELECTs
parent
7936aef476
commit
e396b2f72b
|
@ -142,6 +142,8 @@ impl std::ops::BitOr for Query {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use query_parser::query;
|
||||||
|
|
||||||
pub mod query_parser {
|
pub mod query_parser {
|
||||||
use super::Query::{self, *};
|
use super::Query::{self, *};
|
||||||
use melib::parsec::*;
|
use melib::parsec::*;
|
||||||
|
@ -173,7 +175,7 @@ pub mod query_parser {
|
||||||
pub fn not<'a>() -> impl Parser<'a, Query> {
|
pub fn not<'a>() -> impl Parser<'a, Query> {
|
||||||
move |input| {
|
move |input| {
|
||||||
whitespace_wrap(either(
|
whitespace_wrap(either(
|
||||||
match_literal_anycase("or"),
|
match_literal_anycase("not"),
|
||||||
match_literal_anycase("!"),
|
match_literal_anycase("!"),
|
||||||
))
|
))
|
||||||
.parse(input)
|
.parse(input)
|
||||||
|
@ -213,8 +215,8 @@ pub mod query_parser {
|
||||||
Ok(q)
|
Ok(q)
|
||||||
} else if let Ok(q) = from().parse(input) {
|
} else if let Ok(q) = from().parse(input) {
|
||||||
Ok(q)
|
Ok(q)
|
||||||
} else if let Ok(q) = not().parse(input) {
|
} else if let Ok((rest, query_a)) = not().parse(input) {
|
||||||
Ok(q)
|
Ok((rest, Not(Box::new(query_a))))
|
||||||
} else if let Ok((rest, query_a)) = {
|
} else if let Ok((rest, query_a)) = {
|
||||||
let result = literal().parse(input);
|
let result = literal().parse(input);
|
||||||
if result.is_ok()
|
if result.is_ok()
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::cache::query;
|
||||||
|
use crate::cache::Query::{self, *};
|
||||||
|
use crate::melib::parsec::Parser;
|
||||||
use melib::{
|
use melib::{
|
||||||
backends::MailBackend,
|
backends::MailBackend,
|
||||||
email::{Envelope, EnvelopeHash},
|
email::{Envelope, EnvelopeHash},
|
||||||
|
@ -299,13 +302,25 @@ pub fn search(
|
||||||
SortOrder::Desc => "DESC",
|
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);
|
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(
|
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())
|
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
|
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_err(|e| MeliError::new(e.to_string()))?
|
||||||
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
|
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
|
||||||
Ok(u64::from_be_bytes(
|
Ok(u64::from_be_bytes(
|
||||||
|
@ -339,3 +354,75 @@ pub fn from(term: &str) -> Result<StackVec<EnvelopeHash>> {
|
||||||
.collect::<Result<StackVec<EnvelopeHash>>>();
|
.collect::<Result<StackVec<EnvelopeHash>>>();
|
||||||
results
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue