Add fuzzy_search_users to Db trait, PostgresDb

This commit is contained in:
Nathan Sobo 2022-05-05 09:58:18 -06:00
parent 079e514379
commit 6050e0ead7
2 changed files with 47 additions and 0 deletions

View File

@ -0,0 +1,2 @@
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX trigram_index_users_on_github_login ON users USING GIN(github_login gin_trgm_ops);

View File

@ -10,6 +10,7 @@ use time::OffsetDateTime;
pub trait Db: Send + Sync {
async fn create_user(&self, github_login: &str, admin: bool) -> Result<UserId>;
async fn get_all_users(&self) -> Result<Vec<User>>;
async fn fuzzy_search_users(&self, query: &str, limit: u32) -> Result<Vec<User>>;
async fn get_user_by_id(&self, id: UserId) -> Result<Option<User>>;
async fn get_users_by_ids(&self, ids: Vec<UserId>) -> Result<Vec<User>>;
async fn get_user_by_github_login(&self, github_login: &str) -> Result<Option<User>>;
@ -99,6 +100,21 @@ impl Db for PostgresDb {
Ok(sqlx::query_as(query).fetch_all(&self.pool).await?)
}
async fn fuzzy_search_users(&self, name_query: &str, limit: u32) -> Result<Vec<User>> {
let query = "
SELECT users.*
FROM users
WHERE github_login % $1
ORDER BY github_login <-> $1
LIMIT $2
";
Ok(sqlx::query_as(query)
.bind(name_query)
.bind(limit)
.fetch_all(&self.pool)
.await?)
}
async fn get_user_by_id(&self, id: UserId) -> Result<Option<User>> {
let users = self.get_users_by_ids(vec![id]).await?;
Ok(users.into_iter().next())
@ -640,6 +656,31 @@ pub mod tests {
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_fuzzy_search_users() {
let test_db = TestDb::postgres().await;
let db = test_db.db();
for github_login in [
"nathansobo",
"nathansobot",
"nathanszabo",
"maxbrunsfeld",
"as-cii",
] {
db.create_user(github_login, false).await.unwrap();
}
let results = db
.fuzzy_search_users("nathasbo", 10)
.await
.unwrap()
.into_iter()
.map(|user| user.github_login)
.collect::<Vec<_>>();
assert_eq!(results, &["nathansobo", "nathanszabo", "nathansobot"]);
}
pub struct TestDb {
pub db: Option<Arc<dyn Db>>,
pub url: String,
@ -749,6 +790,10 @@ pub mod tests {
unimplemented!()
}
async fn fuzzy_search_users(&self, _: &str, _: u32) -> Result<Vec<User>> {
unimplemented!()
}
async fn get_user_by_id(&self, id: UserId) -> Result<Option<User>> {
Ok(self.get_users_by_ids(vec![id]).await?.into_iter().next())
}