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.
This commit is contained in:
		
							parent
							
								
									2d8db52010
								
							
						
					
					
						commit
						31b0a550f2
					
				
					 2 changed files with 61 additions and 1 deletions
				
			
		
							
								
								
									
										41
									
								
								src/db.rs
									
										
									
									
									
								
							
							
						
						
									
										41
									
								
								src/db.rs
									
										
									
									
									
								
							| 
						 | 
					@ -17,7 +17,8 @@
 | 
				
			||||||
//! This module implements the database connection actor.
 | 
					//! This module implements the database connection actor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use actix::prelude::*;
 | 
					use actix::prelude::*;
 | 
				
			||||||
use diesel;
 | 
					use diesel::{self, sql_query};
 | 
				
			||||||
 | 
					use diesel::sql_types::Text;
 | 
				
			||||||
use diesel::prelude::*;
 | 
					use diesel::prelude::*;
 | 
				
			||||||
use diesel::r2d2::{Pool, ConnectionManager};
 | 
					use diesel::r2d2::{Pool, ConnectionManager};
 | 
				
			||||||
use models::*;
 | 
					use models::*;
 | 
				
			||||||
| 
						 | 
					@ -138,3 +139,41 @@ impl Handler<CreatePost> for DbExecutor {
 | 
				
			||||||
           .get_result(&conn)?)
 | 
					           .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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use chrono::prelude::{DateTime, Utc};
 | 
					use chrono::prelude::{DateTime, Utc};
 | 
				
			||||||
use schema::{threads, posts};
 | 
					use schema::{threads, posts};
 | 
				
			||||||
 | 
					use diesel::sql_types::{Text, Integer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Identifiable, Queryable, Serialize)]
 | 
					#[derive(Identifiable, Queryable, Serialize)]
 | 
				
			||||||
pub struct Thread {
 | 
					pub struct Thread {
 | 
				
			||||||
| 
						 | 
					@ -69,3 +70,23 @@ pub struct NewPost {
 | 
				
			||||||
    pub author_name: String,
 | 
					    pub author_name: String,
 | 
				
			||||||
    pub author_email: String,
 | 
					    pub author_email: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// This struct models the response of a full-text search query. It
 | 
				
			||||||
 | 
					/// does not use a table/schema definition struct like the other
 | 
				
			||||||
 | 
					/// tables, as no table of this type actually exists.
 | 
				
			||||||
 | 
					#[derive(QueryableByName, Debug)]
 | 
				
			||||||
 | 
					pub struct SearchResult {
 | 
				
			||||||
 | 
					    #[sql_type = "Integer"]
 | 
				
			||||||
 | 
					    pub post_id: i32,
 | 
				
			||||||
 | 
					    #[sql_type = "Integer"]
 | 
				
			||||||
 | 
					    pub thread_id: i32,
 | 
				
			||||||
 | 
					    #[sql_type = "Text"]
 | 
				
			||||||
 | 
					    pub author: String,
 | 
				
			||||||
 | 
					    #[sql_type = "Text"]
 | 
				
			||||||
 | 
					    pub title: String,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Headline represents the result of Postgres' ts_headline()
 | 
				
			||||||
 | 
					    /// function, which highlights search terms in the search results.
 | 
				
			||||||
 | 
					    #[sql_type = "Text"]
 | 
				
			||||||
 | 
					    pub headline: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue