WIP: improve search #1

Closed
rob wants to merge 26 commits from improve-search into master
4 changed files with 54 additions and 57 deletions
Showing only changes of commit f571ec261c - Show all commits

View File

@ -74,15 +74,6 @@ impl fmt::Display for Direction {
write!(f, "{}", s) write!(f, "{}", s)
} }
} }
#[derive(Copy, Clone, Debug, Default, Deserialize)]
struct Sort(Key, Direction);
impl fmt::Display for Sort {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = format!("{} {}", self.0, self.1);
write!(f, "{}", s)
}
}
#[actix_rt::main] #[actix_rt::main]
async fn main() -> io::Result<()> { async fn main() -> io::Result<()> {
@ -156,7 +147,8 @@ async fn search(
struct NewQuery { struct NewQuery {
page: Option<usize>, page: Option<usize>,
size: Option<usize>, size: Option<usize>,
sort: Option<Sort>, sort_key: Option<Key>,
sort_dir: Option<Direction>,
type_: Option<String>, type_: Option<String>,
} }
@ -191,21 +183,20 @@ fn search_query(
let page = query.page.unwrap_or(1); let page = query.page.unwrap_or(1);
let key = query.sort_key.unwrap_or_default(); let key = query.sort_key.unwrap_or_default();
let direction = query.sort_dir.unwrap_or_default(); let direction = query.sort_dir.unwrap_or_default();
let sort = Sort(key, direction);
let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE));
let type_ = query.type_.as_ref().map_or("torrent", String::deref); let type_ = query.type_.as_ref().map_or("torrent", String::deref);
let offset = size * (page - 1); let offset = size * (page - 1);
dbg!( println!(
"query = {}, type = {}, page = {}, size = {}, sort = {:?}", "query = {}, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}",
q, type_, page, size, sort q, type_, page, size, key, direction
); );
let res = if type_ == "file" { let res = if type_ == "file" {
let results = torrent_file_search(conn, q, size, offset)?; let results = torrent_file_search(conn, q, size, offset)?;
serde_json::to_value(&results).unwrap() serde_json::to_value(&results).unwrap()
} else { } else {
let results = torrent_search(conn, q, sort, size, offset)?; let results = torrent_search(conn, q, key, direction, size, offset)?;
serde_json::to_value(&results).unwrap() serde_json::to_value(&results).unwrap()
}; };
@ -219,13 +210,14 @@ fn new_query(
let page = query.page.unwrap_or(1); let page = query.page.unwrap_or(1);
let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE));
let sort = query.sort.unwrap_or_default(); let key = query.sort_key.unwrap_or_default();
let direction = query.sort_dir.unwrap_or_default();
let type_ = query.type_.as_ref().map_or("torrent", String::deref); let type_ = query.type_.as_ref().map_or("torrent", String::deref);
let offset = size * (page - 1); let offset = size * (page - 1);
dbg!( dbg!(
"new, type = {}, page = {}, size = {}, sort = {:?}", "new, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}",
type_, page, size, sort type_, page, size, key, direction
); );
let res = if type_ == "file" { let res = if type_ == "file" {
@ -254,33 +246,34 @@ struct Torrent {
fn torrent_search( fn torrent_search(
conn: r2d2::PooledConnection<SqliteConnectionManager>, conn: r2d2::PooledConnection<SqliteConnectionManager>,
query: &str, query: &str,
sort: Sort, key: Key,
direction: Direction,
size: usize, size: usize,
offset: usize, offset: usize,
) -> Result<Vec<Torrent>, Error> { ) -> Result<Vec<Torrent>, Error> {
let stmt_str = format!("select * from torrents where name like '%' || :query || '%' order by {} limit :offset, :size", sort); // `key` and `direction` are already sanitized and should not be escaped:
let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", key, direction);
let mut stmt = conn.prepare(&stmt_str)?; let mut stmt = conn.prepare(&stmt_str)?;
let torrent_iter = stmt.query_map(params![ let torrent_iter = stmt.query_map(params!{
query.replace(" ", "%"), query.replace(" ", "%"),
offset.to_string(), offset.to_string(),
size.to_string(), size.to_string(),
], },
|row| {
|row| {
let size: isize = row.get(2)?; let size: isize = row.get(2)?;
println!("got size {:?}", size); println!("got size {:?}", size);
Ok(Torrent { Ok(Torrent {
infohash: row.get(0)?, infohash: row.get(0)?,
name: row.get(1)?, name: row.get(1)?,
size_bytes: row.get(2)?, size_bytes: row.get(2)?,
created_unix: row.get(3)?, created_unix: row.get(3)?,
seeders: row.get(4)?, seeders: row.get(4)?,
leechers: row.get(5)?, leechers: row.get(5)?,
completed: row.get(6)?, completed: row.get(6)?,
scraped_date: row.get(7)?, scraped_date: row.get(7)?,
}) })
}, },
)?; )?;
let mut torrents = Vec::new(); let mut torrents = Vec::new();

View File

@ -41,7 +41,8 @@ Sparky.task('config', _ => {
}); });
app = fuse.bundle('app').instructions('>index.tsx'); app = fuse.bundle('app').instructions('>index.tsx');
}); });
Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); //Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/'));
Sparky.task('clean', _ => {});
Sparky.task('env', _ => (isProduction = true)); Sparky.task('env', _ => (isProduction = true));
Sparky.task('copy-assets', () => Sparky.src('assets/*.ico').dest('dist/')); Sparky.task('copy-assets', () => Sparky.src('assets/*.ico').dest('dist/'));
Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => {

View File

@ -76,6 +76,7 @@ export class Navbar extends Component<any, State> {
} }
searchChange(i: Navbar, event) { searchChange(i: Navbar, event) {
console.log("in searchChange");
let searchParams: SearchParams = { let searchParams: SearchParams = {
q: event.target.value, q: event.target.value,
page: 1, page: 1,
@ -87,6 +88,7 @@ export class Navbar extends Component<any, State> {
} }
searchTypeChange(i: Navbar, event) { searchTypeChange(i: Navbar, event) {
console.log("in searchTypeChange");
let searchParams: SearchParams = { let searchParams: SearchParams = {
q: i.state.searchParams.q, q: i.state.searchParams.q,
page: 1, page: 1,

View File

@ -11,27 +11,19 @@ interface State {
searching: Boolean; searching: Boolean;
} }
function buildSearchURL({state: { searchParams }}, key) {
function buildSearchURL(sort_key, {state: { searchParams }) { let page, direction;
console.log("got search URL sort_key", sort_key, "searchParams", searchParams, "thing", searchParams); if (key === searchParams.sort_key) {
page = searchParams.page;
//let searchParams = thing.state.searchParams; direction = searchParams.sort_dir === "Asc" ? "Desc" : "Asc";
if (sort_key === searchParams.sort_key) {
const sort_dir = searchParams.sort_dir === "asc" ? "desc" : "asc";
searchParams.sort_dir = sort_dir;
console.log("no change in sort key from", sort_key, "so switch direction instead.")
} else { } else {
console.log("change sort key from", searchParams.sort_key, "to", sort_key); page = 1;
searchParams.sort_key = sort_key; direction = "Desc";
} }
const url = `/#/search/${searchParams.type_}/${searchParams.q}/${searchParams.page}/${searchParams.sort_key}/${searchParams.sort_dir}` return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${key}/${direction}`;
return url;
} }
export class Search extends Component<any, State> { export class Search extends Component<any, State> {
state: State = { state: State = {
results: { results: {
torrents: [] torrents: []
@ -51,7 +43,7 @@ export class Search extends Component<any, State> {
} }
componentDidMount() { componentDidMount() {
console.log("got props", this.props); console.log("loading page, got match", this.props.match.params);
this.state.searchParams = { this.state.searchParams = {
page: Number(this.props.match.params.page), page: Number(this.props.match.params.page),
q: this.props.match.params.q, q: this.props.match.params.q,
@ -65,6 +57,7 @@ export class Search extends Component<any, State> {
// Re-do search if the props have changed // Re-do search if the props have changed
componentDidUpdate(lastProps: any) { componentDidUpdate(lastProps: any) {
if (lastProps.match && lastProps.match.params !== this.props.match.params) { if (lastProps.match && lastProps.match.params !== this.props.match.params) {
console.log("updating component with sort_key", this.props.match.params.sort_key);
this.state.searchParams = { this.state.searchParams = {
page: Number(this.props.match.params.page), page: Number(this.props.match.params.page),
q: this.props.match.params.q, q: this.props.match.params.q,
@ -78,7 +71,6 @@ export class Search extends Component<any, State> {
} }
search() { search() {
console.log("in search, state", this.state);
if (!!this.state.searchParams.q) { if (!!this.state.searchParams.q) {
this.setState({ searching: true, results: { torrents: [] } }); this.setState({ searching: true, results: { torrents: [] } });
this.fetchData(this.state.searchParams) this.fetchData(this.state.searchParams)
@ -141,24 +133,33 @@ export class Search extends Component<any, State> {
<tr> <tr>
<th class="search-name-col"> <th class="search-name-col">
<a <a
href={buildSearchURL('Name', this)}> href={buildSearchURL(this, 'Name')}>
Name Name
</a> </a>
</th> </th>
<th class="text-right"> <th class="text-right">
<a <a
href={buildSearchURL('Size', this)}> href={buildSearchURL(this, 'Size')}>
Size Size
</a> </a>
</th> </th>
<th class="text-right"> <th class="text-right">
<a href="#">Seeds</a> <a
href={buildSearchURL(this, 'Seeders')}>
Seeds
</a>
</th> </th>
<th class="text-right d-none d-md-table-cell"> <th class="text-right d-none d-md-table-cell">
<a href="#">Leeches</a> <a
href={buildSearchURL(this, 'Leechers')}>
Leechers
</a>
</th> </th>
<th class="text-right d-none d-md-table-cell"> <th class="text-right d-none d-md-table-cell">
<a href="#">Scraped</a> <a
href={buildSearchURL(this, 'Date')}>
Scraped
</a>
</th> </th>
<th></th> <th></th>
</tr> </tr>