Add fuzzy text search mode (#142)

This commit is contained in:
Frank Hamand 2021-06-01 08:38:19 +01:00 committed by GitHub
parent f0130571a6
commit 0b9dc6696b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 3 deletions

35
Cargo.lock generated
View File

@ -37,6 +37,27 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-stream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625"
dependencies = [
"async-stream-impl",
"futures-core",
]
[[package]]
name = "async-stream-impl"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.50" version = "0.1.50"
@ -129,6 +150,7 @@ dependencies = [
"sodiumoxide", "sodiumoxide",
"sqlx", "sqlx",
"tokio", "tokio",
"tokio-test",
"urlencoding", "urlencoding",
"uuid", "uuid",
"whoami", "whoami",
@ -2323,6 +2345,19 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-test"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3"
dependencies = [
"async-stream",
"bytes",
"futures-core",
"tokio",
"tokio-stream",
]
[[package]] [[package]]
name = "tokio-tungstenite" name = "tokio-tungstenite"
version = "0.13.0" version = "0.13.0"

View File

@ -40,3 +40,6 @@ humantime = "2.1.0"
itertools = "0.10.0" itertools = "0.10.0"
shellexpand = "2" shellexpand = "2"
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "uuid", "chrono", "sqlite" ] } sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "uuid", "chrono", "sqlite" ] }
[dev-dependencies]
tokio-test = "*"

View File

@ -24,5 +24,5 @@
# sync_address = "https://api.atuin.sh" # sync_address = "https://api.atuin.sh"
## which search mode to use ## which search mode to use
## possible values: prefix, fulltext ## possible values: prefix, fulltext, fuzzy
# search_mode = "prefix" # search_mode = "prefix"

View File

@ -6,6 +6,7 @@ use chrono::prelude::*;
use chrono::Utc; use chrono::Utc;
use eyre::Result; use eyre::Result;
use itertools::Itertools;
use sqlx::sqlite::{ use sqlx::sqlite::{
SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow, SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow,
@ -286,6 +287,7 @@ impl Database for Sqlite {
let query = match search_mode { let query = match search_mode {
SearchMode::Prefix => query, SearchMode::Prefix => query,
SearchMode::FullText => format!("%{}", query), SearchMode::FullText => format!("%{}", query),
SearchMode::Fuzzy => query.split("").join("%"),
}; };
let res = sqlx::query( let res = sqlx::query(
@ -318,3 +320,89 @@ impl Database for Sqlite {
Ok(res) Ok(res)
} }
} }
#[cfg(test)]
mod test {
use super::*;
async fn new_history_item(db: &mut impl Database, cmd: &str) -> Result<()> {
let history = History::new(
chrono::Utc::now(),
cmd.to_string(),
"/home/ellie".to_string(),
0,
1,
Some("beep boop".to_string()),
Some("booop".to_string()),
);
return db.save(&history).await;
}
#[tokio::test(flavor = "multi_thread")]
async fn test_search_prefix() {
let mut db = Sqlite::new("sqlite::memory:").await.unwrap();
new_history_item(&mut db, "ls /home/ellie").await.unwrap();
let mut results = db.search(None, SearchMode::Prefix, "ls").await.unwrap();
assert_eq!(results.len(), 1);
results = db.search(None, SearchMode::Prefix, "/home").await.unwrap();
assert_eq!(results.len(), 0);
results = db.search(None, SearchMode::Prefix, "ls ").await.unwrap();
assert_eq!(results.len(), 0);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_search_fulltext() {
let mut db = Sqlite::new("sqlite::memory:").await.unwrap();
new_history_item(&mut db, "ls /home/ellie").await.unwrap();
let mut results = db.search(None, SearchMode::FullText, "ls").await.unwrap();
assert_eq!(results.len(), 1);
results = db
.search(None, SearchMode::FullText, "/home")
.await
.unwrap();
assert_eq!(results.len(), 1);
results = db.search(None, SearchMode::FullText, "ls ").await.unwrap();
assert_eq!(results.len(), 0);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_search_fuzzy() {
let mut db = Sqlite::new("sqlite::memory:").await.unwrap();
new_history_item(&mut db, "ls /home/ellie").await.unwrap();
new_history_item(&mut db, "ls /home/frank").await.unwrap();
new_history_item(&mut db, "cd /home/ellie").await.unwrap();
new_history_item(&mut db, "/home/ellie/.bin/rustup")
.await
.unwrap();
let mut results = db.search(None, SearchMode::Fuzzy, "ls /").await.unwrap();
assert_eq!(results.len(), 2);
results = db.search(None, SearchMode::Fuzzy, "l/h/").await.unwrap();
assert_eq!(results.len(), 2);
results = db.search(None, SearchMode::Fuzzy, "/h/e").await.unwrap();
assert_eq!(results.len(), 3);
results = db.search(None, SearchMode::Fuzzy, "/hmoe/").await.unwrap();
assert_eq!(results.len(), 0);
results = db
.search(None, SearchMode::Fuzzy, "ellie/home")
.await
.unwrap();
assert_eq!(results.len(), 0);
results = db.search(None, SearchMode::Fuzzy, "lsellie").await.unwrap();
assert_eq!(results.len(), 1);
results = db.search(None, SearchMode::Fuzzy, " ").await.unwrap();
assert_eq!(results.len(), 3);
}
}

View File

@ -17,6 +17,9 @@ pub enum SearchMode {
#[serde(rename = "fulltext")] #[serde(rename = "fulltext")]
FullText, FullText,
#[serde(rename = "fuzzy")]
Fuzzy,
} }
// FIXME: Can use upstream Dialect enum if https://github.com/stevedonovan/chrono-english/pull/16 is merged // FIXME: Can use upstream Dialect enum if https://github.com/stevedonovan/chrono-english/pull/16 is merged

View File

@ -96,8 +96,8 @@ key = "~/.atuin-session"
### `search_mode` ### `search_mode`
Which search mode to use. Atuin supports both "prefix" and full text search Which search mode to use. Atuin supports "prefix", full text and "fuzzy" search
modes. The former will essentially search for "query*", and the latter "*query\*" modes. The prefix search for "query\*", fulltext "\*query\*", and fuzzy "\*q\*u\*e\*r\*y\*"
Defaults to "prefix" Defaults to "prefix"