diff --git a/.sqlx/query-1f1b8289dc5079b3b4516f69436d403400bd6d70a26e2a73e2f60999c45b09d9.json b/.sqlx/query-1f1b8289dc5079b3b4516f69436d403400bd6d70a26e2a73e2f60999c45b09d9.json new file mode 100644 index 0000000..c252420 --- /dev/null +++ b/.sqlx/query-1f1b8289dc5079b3b4516f69436d403400bd6d70a26e2a73e2f60999c45b09d9.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from members where similarity(realname,$1) > 0.3 order by similarity(realname,$1) desc limit $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "shortcode", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "nickname", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "realname", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "fresher", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "1f1b8289dc5079b3b4516f69436d403400bd6d70a26e2a73e2f60999c45b09d9" +} diff --git a/.sqlx/query-b8d6ddf5d4f4f009ac27e78005c1fe898de981a7c148d692dc812bfe21dc4558.json b/.sqlx/query-b8d6ddf5d4f4f009ac27e78005c1fe898de981a7c148d692dc812bfe21dc4558.json new file mode 100644 index 0000000..d2a39fe --- /dev/null +++ b/.sqlx/query-b8d6ddf5d4f4f009ac27e78005c1fe898de981a7c148d692dc812bfe21dc4558.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from members where similarity(nickname,$1) > 0.3 order by similarity(nickname,$1) desc limit $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "discord_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "shortcode", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "nickname", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "realname", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "fresher", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "b8d6ddf5d4f4f009ac27e78005c1fe898de981a7c148d692dc812bfe21dc4558" +} diff --git a/migrations/20230925132804_pg_trgm.sql b/migrations/20230925132804_pg_trgm.sql new file mode 100644 index 0000000..b2b1f68 --- /dev/null +++ b/migrations/20230925132804_pg_trgm.sql @@ -0,0 +1 @@ +CREATE EXTENSION pg_trgm; \ No newline at end of file diff --git a/src/cmds.rs b/src/cmds.rs index fef270c..d4aaed8 100644 --- a/src/cmds.rs +++ b/src/cmds.rs @@ -572,20 +572,36 @@ pub(crate) async fn whois_by_id(ctx: ACtx<'_>, id: serenity::User) -> Result<(), #[poise::command(slash_command, rename = "nick")] pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> { println!("Cmd: ({}) whois_by_nickname {nickname}", ctx.author().name); - // TODO: fuzzy finding match db::get_member_by_nickname(&ctx.data().db, &nickname).await? { Some(m) => { ctx.send(|c| { c.content(format!("{nickname}: <@{}>", m.discord_id)) .ephemeral(true) }) - .await? + .await?; } None => { - ctx.say(format!("No member entry found for nickname {nickname}",)) - .await? + let members = db::get_member_by_nickname_fuzzy(&ctx.data().db, &nickname, 3).await?; + if members.is_empty() { + ctx.send(|c| { + c.content(format!("No member entry found for nickname {nickname}")) + .ephemeral(true) + }) + .await?; + } else { + ctx.send(|c| { + c.ephemeral(true).content(format!( + "Possible matches for {nickname}: {}", + members + .iter() + .map(|m| format!(" <@{}>", m.discord_id)) + .collect::() + )) + }) + .await?; + } } - }; + } Ok(()) } @@ -593,18 +609,34 @@ pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result #[poise::command(slash_command, rename = "name")] pub(crate) async fn whois_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> { println!("Cmd: ({}) whois_by_realname {realname}", ctx.author().name); - // TODO: fuzzy finding match db::get_member_by_realname(&ctx.data().db, &realname).await? { Some(m) => { ctx.send(|c| { c.content(format!("{realname}: <@{}>", m.discord_id)) .ephemeral(true) }) - .await? + .await?; } None => { - ctx.say(format!("No member entry found for realname {realname}",)) - .await? + let members = db::get_member_by_realname_fuzzy(&ctx.data().db, &realname, 3).await?; + if members.is_empty() { + ctx.send(|c| { + c.content(format!("No member entry found for realname {realname}")) + .ephemeral(true) + }) + .await?; + } else { + ctx.send(|c| { + c.ephemeral(true).content(format!( + "Possible matches for {realname}: {}", + members + .iter() + .map(|m| format!(" <@{}>", m.discord_id)) + .collect::() + )) + }) + .await?; + } } }; Ok(()) diff --git a/src/db/members.rs b/src/db/members.rs index 1e8d5e9..95b7037 100644 --- a/src/db/members.rs +++ b/src/db/members.rs @@ -64,6 +64,22 @@ pub(crate) async fn get_member_by_nickname( .await?) } +/// Get member entry by Nickname (Fuzzy) +pub(crate) async fn get_member_by_nickname_fuzzy( + pool: &sqlx::PgPool, + nickname: &str, + limit: i64, +) -> Result, Error> { + Ok(sqlx::query_as!( + Member, + "select * from members where similarity(nickname,$1) > 0.3 order by similarity(nickname,$1) desc limit $2", + nickname, + limit, + ) + .fetch_all(pool) + .await?) +} + /// Get member entry by Real Name pub(crate) async fn get_member_by_realname( pool: &sqlx::PgPool, @@ -78,6 +94,22 @@ pub(crate) async fn get_member_by_realname( .await?) } +/// Get member entry by Real Name (Fuzzy) +pub(crate) async fn get_member_by_realname_fuzzy( + pool: &sqlx::PgPool, + realname: &str, + limit: i64, +) -> Result, Error> { + Ok(sqlx::query_as!( + Member, + "select * from members where similarity(realname,$1) > 0.3 order by similarity(realname,$1) desc limit $2", + realname, + limit, + ) + .fetch_all(pool) + .await?) +} + /// Add member entry to members table pub(crate) async fn insert_member(pool: &sqlx::PgPool, m: Member) -> Result<(), Error> { sqlx::query!(