WIP: improve search #1
|
@ -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();
|
||||||
|
|
|
@ -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'], _ => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue