Expand search api
This commit is contained in:
parent
59c11897b2
commit
9336fbf3cd
2 changed files with 33 additions and 15 deletions
|
@ -11,21 +11,23 @@ use site::Layout;
|
||||||
use state::State;
|
use state::State;
|
||||||
use web::{Resource, ResponseFuture};
|
use web::{Resource, ResponseFuture};
|
||||||
|
|
||||||
const DEFAULT_LIMIT: i32 = 30;
|
const DEFAULT_LIMIT: i32 = 10;
|
||||||
|
const DEFAULT_SNIPPET_SIZE: i32 = 8;
|
||||||
|
|
||||||
type BoxResource = Box<Resource + Sync + Send>;
|
type BoxResource = Box<Resource + Sync + Send>;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub struct QueryParameters {
|
pub struct QueryParameters {
|
||||||
q: Option<String>,
|
q: Option<String>,
|
||||||
skip: Option<i32>,
|
offset: Option<i32>,
|
||||||
limit: Option<i32>,
|
limit: Option<i32>,
|
||||||
|
snippet_size: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueryParameters {
|
impl QueryParameters {
|
||||||
pub fn skip(self, skip: i32) -> Self {
|
pub fn offset(self, offset: i32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
skip: if skip != 0 { Some(skip) } else { None },
|
offset: if offset != 0 { Some(offset) } else { None },
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,18 +62,30 @@ impl SearchLookup {
|
||||||
pub fn lookup(&self, query: Option<&str>) -> Result<Option<BoxResource>, ::web::Error> {
|
pub fn lookup(&self, query: Option<&str>) -> Result<Option<BoxResource>, ::web::Error> {
|
||||||
let args: QueryParameters = serde_urlencoded::from_str(query.unwrap_or(""))?;
|
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 {
|
pub struct SearchResource {
|
||||||
state: State,
|
state: State,
|
||||||
query_args: QueryParameters,
|
|
||||||
|
query: Option<String>,
|
||||||
|
limit: i32,
|
||||||
|
offset: i32,
|
||||||
|
snippet_size: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchResource {
|
impl SearchResource {
|
||||||
pub fn new(state: State, query_args: QueryParameters) -> Self {
|
pub fn new(state: State, query: Option<String>, limit: i32, offset: i32, snippet_size: i32) -> Self {
|
||||||
Self { state, query_args }
|
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:
|
// 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();
|
let head = self.head();
|
||||||
|
|
||||||
Box::new(data.join(head)
|
Box::new(data.join(head)
|
||||||
|
@ -119,7 +133,7 @@ impl Resource for SearchResource {
|
||||||
base: None, // Hmm, should perhaps accept `base` as argument
|
base: None, // Hmm, should perhaps accept `base` as argument
|
||||||
title: "Search",
|
title: "Search",
|
||||||
body: &Template {
|
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,
|
hits: data,
|
||||||
},
|
},
|
||||||
style_css_checksum: StyleCss::checksum(),
|
style_css_checksum: StyleCss::checksum(),
|
||||||
|
|
12
src/state.rs
12
src/state.rs
|
@ -305,12 +305,12 @@ impl State {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_query(&self, query_string: String) -> CpuFuture<Vec<models::SearchResult>, Error> {
|
pub fn search_query(&self, query_string: String, limit: i32, offset: i32, snippet_size: i32) -> CpuFuture<Vec<models::SearchResult>, Error> {
|
||||||
let connection_pool = self.connection_pool.clone();
|
let connection_pool = self.connection_pool.clone();
|
||||||
|
|
||||||
self.cpu_pool.spawn_fn(move || {
|
self.cpu_pool.spawn_fn(move || {
|
||||||
use diesel::expression::sql_literal::sql;
|
use diesel::expression::sql_literal::sql;
|
||||||
use diesel::types::Text;
|
use diesel::types::{Integer, Text};
|
||||||
|
|
||||||
fn fts_quote(src: &str) -> String {
|
fn fts_quote(src: &str) -> String {
|
||||||
format!("\"{}\"", src.replace('\"', "\"\""))
|
format!("\"{}\"", src.replace('\"', "\"\""))
|
||||||
|
@ -331,12 +331,16 @@ impl State {
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
sql::<(Text, Text, Text)>(
|
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 \
|
FROM article_search \
|
||||||
WHERE article_search MATCH ? \
|
WHERE article_search MATCH ? \
|
||||||
ORDER BY rank"
|
ORDER BY rank \
|
||||||
|
LIMIT ? OFFSET ?"
|
||||||
)
|
)
|
||||||
|
.bind::<Integer, _>(snippet_size)
|
||||||
.bind::<Text, _>(query)
|
.bind::<Text, _>(query)
|
||||||
|
.bind::<Integer, _>(limit)
|
||||||
|
.bind::<Integer, _>(offset)
|
||||||
.load(&*connection_pool.get()?)?)
|
.load(&*connection_pool.get()?)?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue