diff options
author | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-12-04 18:46:29 -0700 |
---|---|---|
committer | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-12-04 18:47:24 -0700 |
commit | 1b0b1e5de009d2e5638b6dc6da68153fc41c84f2 (patch) | |
tree | d2a4c70fc2d8732bcca3bfbb86222e30eeee1fbe | |
parent | d19fd96718bb0474d0310b8bed40a28466f74ac9 (diff) |
Add community search (#127)
-rw-r--r-- | migrations/20201205011714_community-search/down.sql | 4 | ||||
-rw-r--r-- | migrations/20201205011714_community-search/up.sql | 9 | ||||
-rw-r--r-- | openapi/openapi.json | 6 | ||||
-rw-r--r-- | src/routes/api/communities.rs | 45 | ||||
-rw-r--r-- | src/routes/api/posts.rs | 2 |
5 files changed, 54 insertions, 12 deletions
diff --git a/migrations/20201205011714_community-search/down.sql b/migrations/20201205011714_community-search/down.sql new file mode 100644 index 0000000..1ea08f6 --- /dev/null +++ b/migrations/20201205011714_community-search/down.sql @@ -0,0 +1,4 @@ +BEGIN; + DROP INDEX community_fts; + DROP FUNCTION community_fts; +COMMIT; diff --git a/migrations/20201205011714_community-search/up.sql b/migrations/20201205011714_community-search/up.sql new file mode 100644 index 0000000..8c20efa --- /dev/null +++ b/migrations/20201205011714_community-search/up.sql @@ -0,0 +1,9 @@ +BEGIN; + CREATE FUNCTION community_fts(community community) RETURNS tsvector AS $$ + BEGIN + RETURN setweight(to_tsvector('english', community.name), 'A') || + setweight(to_tsvector('english', community.description), 'D'); + END; + $$ LANGUAGE plpgsql IMMUTABLE; + CREATE INDEX community_fts ON community USING gin(community_fts(community)); +COMMIT; diff --git a/openapi/openapi.json b/openapi/openapi.json index 12c698c..8f0d2fe 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -472,6 +472,12 @@ "required": false, "schema": {"type": "boolean"}, "description": "If true, will include `you_are_moderator` and `your_follow` in list. Requires login." + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": {"type": "string"} } ], "responses": { diff --git a/src/routes/api/communities.rs b/src/routes/api/communities.rs index aa0d80f..4392983 100644 --- a/src/routes/api/communities.rs +++ b/src/routes/api/communities.rs @@ -30,23 +30,46 @@ async fn route_unstable_communities_list( ctx: Arc<crate::RouteContext>, req: hyper::Request<hyper::Body>, ) -> Result<hyper::Response<hyper::Body>, crate::Error> { - let query: MaybeIncludeYour = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?; + use std::fmt::Write; + + #[derive(Deserialize)] + struct CommunitiesListQuery<'a> { + #[serde(default)] + search: Option<Cow<'a, str>>, + + #[serde(default)] + include_your: bool, + } + + let query: CommunitiesListQuery = serde_urlencoded::from_str(req.uri().query().unwrap_or(""))?; + + let mut sql = String::from("SELECT id, name, local, ap_id, description"); + let mut values: Vec<&(dyn tokio_postgres::types::ToSql + Sync)> = Vec::new(); let db = ctx.db_pool.get().await?; - let rows = if query.include_your { + + let include_your_for = if query.include_your { let user = crate::require_login(&req, &db).await?; - db.query( - "SELECT id, name, local, ap_id, description, (SELECT accepted FROM community_follow WHERE community=community.id AND follower=$1), EXISTS(SELECT 1 FROM community_moderator WHERE community=community.id AND person=$1) FROM community", - &[&user.raw()], - ).await? + Some(user) } else { - db.query( - "SELECT id, name, local, ap_id, description FROM community", - &[], - ) - .await? + None }; + if let Some(user) = &include_your_for { + values.push(user); + sql.push_str(", (SELECT accepted FROM community_follow WHERE community=community.id AND follower=$1), EXISTS(SELECT 1 FROM community_moderator WHERE community=community.id AND person=$1)"); + } + + sql.push_str(" FROM community"); + + if let Some(search) = &query.search { + values.push(search); + write!(sql, " WHERE community_fts(community) @@ plainto_tsquery('english', ${0}) ORDER BY ts_rank_cd(community_fts(community), plainto_tsquery('english', ${0})) DESC", values.len()).unwrap(); + } + + let sql: &str = &sql; + let rows = db.query(sql, &values).await?; + let output: Vec<_> = rows .iter() .map(|row| { diff --git a/src/routes/api/posts.rs b/src/routes/api/posts.rs index 8aacb9e..2f64c5b 100644 --- a/src/routes/api/posts.rs +++ b/src/routes/api/posts.rs @@ -183,7 +183,7 @@ async fn route_unstable_posts_list( PostsListSortType::Normal(ty) => sql.push_str(ty.post_sort_sql()), PostsListSortType::Extra(PostsListExtraSortType::Relevant) => { if let Some(search_value_idx) = search_value_idx { - write!(sql, "ts_rank_cd(to_tsvector('english', title || ' ' || COALESCE(content_text, content_markdown, content_html, '')), plainto_tsquery('english', ${}))", search_value_idx).unwrap(); + write!(sql, "ts_rank_cd(to_tsvector('english', title || ' ' || COALESCE(content_text, content_markdown, content_html, '')), plainto_tsquery('english', ${})) DESC", search_value_idx).unwrap(); } else { return Err(crate::Error::UserError(crate::simple_response( hyper::StatusCode::BAD_REQUEST, |