From 9336fbf3cde0c3b1a5e70ee1af996a63496a43db Mon Sep 17 00:00:00 2001 From: Magnus Hoff Date: Tue, 24 Oct 2017 22:15:42 +0200 Subject: [PATCH] Expand search api --- src/resources/search_resource.rs | 36 ++++++++++++++++++++++---------- src/state.rs | 12 +++++++---- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/resources/search_resource.rs b/src/resources/search_resource.rs index f6ea5dc..60cb084 100644 --- a/src/resources/search_resource.rs +++ b/src/resources/search_resource.rs @@ -11,21 +11,23 @@ use site::Layout; use state::State; use web::{Resource, ResponseFuture}; -const DEFAULT_LIMIT: i32 = 30; +const DEFAULT_LIMIT: i32 = 10; +const DEFAULT_SNIPPET_SIZE: i32 = 8; type BoxResource = Box; #[derive(Serialize, Deserialize, Default)] pub struct QueryParameters { q: Option, - skip: Option, + offset: Option, limit: Option, + snippet_size: Option, } impl QueryParameters { - pub fn skip(self, skip: i32) -> Self { + pub fn offset(self, offset: i32) -> Self { Self { - skip: if skip != 0 { Some(skip) } else { None }, + offset: if offset != 0 { Some(offset) } else { None }, ..self } } @@ -60,18 +62,30 @@ impl SearchLookup { pub fn lookup(&self, query: Option<&str>) -> Result, ::web::Error> { let args: QueryParameters = serde_urlencoded::from_str(query.unwrap_or(""))?; - Ok(Some(Box::new(SearchResource::new(self.state.clone(), args)))) + Ok(Some(Box::new( + SearchResource::new( + self.state.clone(), + args.q, + args.limit.unwrap_or(DEFAULT_LIMIT), + args.offset.unwrap_or(0), + args.snippet_size.unwrap_or(DEFAULT_SNIPPET_SIZE), + ) + ))) } } pub struct SearchResource { state: State, - query_args: QueryParameters, + + query: Option, + limit: i32, + offset: i32, + snippet_size: i32, } impl SearchResource { - pub fn new(state: State, query_args: QueryParameters) -> Self { - Self { state, query_args } + pub fn new(state: State, query: Option, limit: i32, offset: i32, snippet_size: i32) -> Self { + Self { state, query, limit, offset, snippet_size } } } @@ -107,9 +121,9 @@ impl Resource for SearchResource { } // TODO: Show a search "front page" when no query is given: - let query = self.query_args.q.as_ref().map(|x| x.clone()).unwrap_or("".to_owned()); + let query = self.query.as_ref().map(|x| x.clone()).unwrap_or("".to_owned()); - let data = self.state.search_query(query); + let data = self.state.search_query(query, self.limit, self.offset, self.snippet_size); let head = self.head(); Box::new(data.join(head) @@ -119,7 +133,7 @@ impl Resource for SearchResource { base: None, // Hmm, should perhaps accept `base` as argument title: "Search", body: &Template { - query: self.query_args.q.as_ref().map(|x| &**x).unwrap_or(""), + query: self.query.as_ref().map(|x| &**x).unwrap_or(""), hits: data, }, style_css_checksum: StyleCss::checksum(), diff --git a/src/state.rs b/src/state.rs index 3a7e76d..f463056 100644 --- a/src/state.rs +++ b/src/state.rs @@ -305,12 +305,12 @@ impl State { }) } - pub fn search_query(&self, query_string: String) -> CpuFuture, Error> { + pub fn search_query(&self, query_string: String, limit: i32, offset: i32, snippet_size: i32) -> CpuFuture, Error> { let connection_pool = self.connection_pool.clone(); self.cpu_pool.spawn_fn(move || { use diesel::expression::sql_literal::sql; - use diesel::types::Text; + use diesel::types::{Integer, Text}; fn fts_quote(src: &str) -> String { format!("\"{}\"", src.replace('\"', "\"\"")) @@ -331,12 +331,16 @@ impl State { Ok( sql::<(Text, Text, Text)>( - "SELECT title, snippet(article_search, 1, '', '', '\u{2026}', 8), slug \ + "SELECT title, snippet(article_search, 1, '', '', '\u{2026}', ?), slug \ FROM article_search \ WHERE article_search MATCH ? \ - ORDER BY rank" + ORDER BY rank \ + LIMIT ? OFFSET ?" ) + .bind::(snippet_size) .bind::(query) + .bind::(limit) + .bind::(offset) .load(&*connection_pool.get()?)?) }) }