about summary refs log tree commit diff
path: root/src/db.rs
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@gmail.com>2018-04-14T18·28+0200
committerVincent Ambo <github@tazj.in>2018-04-14T20·21+0200
commit31b0a550f2b96a1de5de65308420788c9a6aa5df (patch)
tree27f6396644f753aef6a8ebf48ba0bd6438e71029 /src/db.rs
parent2d8db520107c25c5bc7d1ba861047fa9b7eaea5f (diff)
feat(db): Implement handling of 'SearchPosts' message
Adds support for executing full-text search across a forum instance by
sending the `SearchPosts` message with a search query to the DB actor.

The struct used for results is mapped manually to the expected query
result as the query is embedded via raw SQL.
Diffstat (limited to 'src/db.rs')
-rw-r--r--src/db.rs41
1 files changed, 40 insertions, 1 deletions
diff --git a/src/db.rs b/src/db.rs
index 5a66fbb0fc..416e3fdd0f 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -17,7 +17,8 @@
 //! This module implements the database connection actor.
 
 use actix::prelude::*;
-use diesel;
+use diesel::{self, sql_query};
+use diesel::sql_types::Text;
 use diesel::prelude::*;
 use diesel::r2d2::{Pool, ConnectionManager};
 use models::*;
@@ -138,3 +139,41 @@ impl Handler<CreatePost> for DbExecutor {
            .get_result(&conn)?)
     }
 }
+
+/// Message used to search for posts
+#[derive(Deserialize)]
+pub struct SearchPosts { pub query: String }
+
+impl Message for SearchPosts {
+    type Result = Result<Vec<SearchResult>>;
+}
+
+/// Raw PostgreSQL query used to perform full-text search on posts
+/// with a supplied phrase. For now, the query language is hardcoded
+/// to English and only "plain" queries (i.e. no searches for exact
+/// matches or more advanced query syntax) are supported.
+const SEARCH_QUERY: &'static str = r#"
+WITH search_query (query) AS (VALUES (plainto_tsquery('english', $1)))
+SELECT post_id,
+       thread_id,
+       author,
+       title,
+       ts_headline('english', body, query) AS headline
+  FROM search_index, search_query
+  WHERE document @@ query
+  ORDER BY ts_rank(document, query) DESC
+"#;
+
+impl Handler<SearchPosts> for DbExecutor {
+    type Result = <SearchPosts as Message>::Result;
+
+    fn handle(&mut self, msg: SearchPosts, _: &mut Self::Context) -> Self::Result {
+        let conn = self.0.get()?;
+
+        let search_results = sql_query(SEARCH_QUERY)
+            .bind::<Text, _>(msg.query)
+            .get_results::<SearchResult>(&conn)?;
+
+        Ok(search_results)
+    }
+}