~vpzom/lotide

1b0b1e5de009d2e5638b6dc6da68153fc41c84f2 — Colin Reeder 10 months ago d19fd96
Add community search (#127)
A migrations/20201205011714_community-search/down.sql => migrations/20201205011714_community-search/down.sql +4 -0
@@ 0,0 1,4 @@
BEGIN;
	DROP INDEX community_fts;
	DROP FUNCTION community_fts;
COMMIT;

A migrations/20201205011714_community-search/up.sql => migrations/20201205011714_community-search/up.sql +9 -0
@@ 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;

M openapi/openapi.json => openapi/openapi.json +6 -0
@@ 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": {

M src/routes/api/communities.rs => src/routes/api/communities.rs +34 -11
@@ 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| {

M src/routes/api/posts.rs => src/routes/api/posts.rs +1 -1
@@ 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,