Дополнительные параметры поиска
This commit is contained in:
parent
3e8cbd07ed
commit
b947b670b3
56
index.js
56
index.js
@ -295,17 +295,41 @@ io.on('connection', function(socket)
|
||||
let args = [text, index, limit];
|
||||
const orderBy = navigation.orderBy;
|
||||
let order = '';
|
||||
let where = '';
|
||||
if(orderBy && orderBy.length > 0)
|
||||
{
|
||||
const orderDesc = navigation.orderDesc ? 'DESC' : 'ASC';
|
||||
args.splice(1, 0, orderBy);
|
||||
order = 'ORDER BY ?? ' + orderDesc;
|
||||
}
|
||||
if(safeSearch)
|
||||
{
|
||||
where += " and contentcategory != 'xxx' ";
|
||||
}
|
||||
if(navigation.type && navigation.type.length > 0)
|
||||
{
|
||||
where += ' and contenttype = ' + mysqlPool.escape(navigation.type) + ' ';
|
||||
}
|
||||
if(navigation.size)
|
||||
{
|
||||
if(navigation.size.max > 0)
|
||||
where += ' and size < ' + mysqlPool.escape(navigation.size.max) + ' ';
|
||||
if(navigation.size.min > 0)
|
||||
where += ' and size > ' + mysqlPool.escape(navigation.size.min) + ' ';
|
||||
}
|
||||
if(navigation.files)
|
||||
{
|
||||
if(navigation.files.max > 0)
|
||||
where += ' and files < ' + mysqlPool.escape(navigation.files.max) + ' ';
|
||||
if(navigation.files.min > 0)
|
||||
where += ' and files > ' + mysqlPool.escape(navigation.files.min) + ' ';
|
||||
}
|
||||
console.log(where)
|
||||
|
||||
let searchList = [];
|
||||
//args.splice(orderBy && orderBy.length > 0 ? 1 : 0, 1);
|
||||
//mysqlPool.query('SELECT * FROM `torrents` WHERE `name` like \'%' + text + '%\' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
sphinx.query('SELECT * FROM `torrents_index`,`torrents_index_delta` WHERE MATCH(?) ' + (safeSearch ? "and contentcategory != 'xxx'" : '') + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
//mysqlPool.query('SELECT * FROM `torrents` WHERE `name` like \'%' + text + '%\' ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
sphinx.query('SELECT * FROM `torrents_index`,`torrents_index_delta` WHERE MATCH(?) ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
if(!rows) {
|
||||
console.log(error)
|
||||
callback(undefined)
|
||||
@ -335,18 +359,42 @@ io.on('connection', function(socket)
|
||||
let args = [text, index, limit];
|
||||
const orderBy = navigation.orderBy;
|
||||
let order = '';
|
||||
let where = '';
|
||||
|
||||
if(orderBy && orderBy.length > 0)
|
||||
{
|
||||
const orderDesc = navigation.orderDesc ? 'DESC' : 'ASC';
|
||||
args.splice(1, 0, orderBy);
|
||||
order = 'ORDER BY ?? ' + orderDesc;
|
||||
}
|
||||
if(safeSearch)
|
||||
{
|
||||
where += " and contentcategory != 'xxx' ";
|
||||
}
|
||||
if(navigation.type && navigation.type.length > 0)
|
||||
{
|
||||
where += ' and contenttype = ' + mysqlPool.escape(navigation.type) + ' ';
|
||||
}
|
||||
if(navigation.size)
|
||||
{
|
||||
if(navigation.size.max > 0)
|
||||
where += ' and size < ' + mysqlPool.escape(navigation.size.max) + ' ';
|
||||
if(navigation.size.min > 0)
|
||||
where += ' and size > ' + mysqlPool.escape(navigation.size.min) + ' ';
|
||||
}
|
||||
if(navigation.files)
|
||||
{
|
||||
if(navigation.files.max > 0)
|
||||
where += ' and files < ' + mysqlPool.escape(navigation.files.max) + ' ';
|
||||
if(navigation.files.min > 0)
|
||||
where += ' and files > ' + mysqlPool.escape(navigation.files.min) + ' ';
|
||||
}
|
||||
|
||||
let search = {};
|
||||
let searchList = [];
|
||||
//args.splice(orderBy && orderBy.length > 0 ? 1 : 0, 1);
|
||||
//mysqlPool.query('SELECT * FROM `files` inner join torrents on(torrents.hash = files.hash) WHERE files.path like \'%' + text + '%\' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
sphinx.query('SELECT * FROM `files_index`,`files_index_delta` WHERE MATCH(?) ' + (safeSearch ? "and contentcategory != 'xxx'" : '') + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
//mysqlPool.query('SELECT * FROM `files` inner join torrents on(torrents.hash = files.hash) WHERE files.path like \'%' + text + '%\' ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
sphinx.query('SELECT * FROM `files_index`,`files_index_delta` WHERE MATCH(?) ' + where + ' ' + order + ' LIMIT ?,?', args, function (error, rows, fields) {
|
||||
if(!rows) {
|
||||
console.log(error)
|
||||
callback(undefined)
|
||||
|
@ -62,6 +62,7 @@
|
||||
"phantomjs-prebuilt": "^2.1.15",
|
||||
"react": "^15.6.2",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-input-range": "^1.2.1",
|
||||
"react-tap-event-plugin": "^2.0.1",
|
||||
"sitemap": "^1.13.0",
|
||||
"socket.io": "^2.0.4"
|
||||
|
@ -59,3 +59,28 @@
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.filter-control-border {
|
||||
top: -10px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 530px)
|
||||
{
|
||||
.filter-control-row {
|
||||
flex-wrap: wrap;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
padding-top: 30px;
|
||||
align-items: inherit !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-control-border {
|
||||
padding-top: 5px;
|
||||
top: 6px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
72
src/input-files-filter.js
Normal file
72
src/input-files-filter.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React, { Component } from 'react';
|
||||
import InputRange from 'react-input-range';
|
||||
import 'react-input-range/lib/css/index.css';
|
||||
import formatBytes from './format-bytes'
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
|
||||
export default class InputFilesFilter extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
files: this.props.value || { min: 0, max: 50 },
|
||||
enabled: false || this.props.enabled,
|
||||
filesMax: this.props.filesMax || 100 // 1mb
|
||||
}
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
if(val.filesMax && this.state.files.max > val.filesMax)
|
||||
val.files = {min: this.state.files.min, max: val.filesMax};
|
||||
|
||||
if(val.filesMax && this.state.files.min > val.filesMax)
|
||||
val.files = {min: 0, max: val.files ? val.files.max || this.state.files.max : this.state.files.max };
|
||||
|
||||
super.setState(val, () => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange({
|
||||
enabled: this.state.enabled,
|
||||
filesMax: this.state.filesMax,
|
||||
files: !this.state.enabled ? {min: 0, max: 0} : this.state.files
|
||||
})
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='filter-row row inline w100p'>
|
||||
<Checkbox
|
||||
label="Files filter"
|
||||
checked={this.state.enabled}
|
||||
style={{width: 150}}
|
||||
onCheck={() => this.setState({enabled: !this.state.enabled})}
|
||||
/>
|
||||
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
|
||||
<InputRange
|
||||
maxValue={this.state.filesMax}
|
||||
minValue={0}
|
||||
value={this.state.files}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
onChange={files => this.setState({ files })}
|
||||
/>
|
||||
<SelectField
|
||||
floatingLabelText="Size type"
|
||||
value={this.state.filesMax}
|
||||
onChange={(event, index, value) => this.setState({filesMax: value})}
|
||||
className='filter-control-border'
|
||||
>
|
||||
<MenuItem value={10} primaryText="10 Files or less" />
|
||||
<MenuItem value={100} primaryText="100 Files or less" />
|
||||
<MenuItem value={1000} primaryText="1000 Files or less" />
|
||||
<MenuItem value={10000} primaryText="10000 Files or less" />
|
||||
<MenuItem value={100000} primaryText="100000 Files or less" />
|
||||
<MenuItem value={1000000} primaryText="1000000 Files or less" />
|
||||
</SelectField>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
73
src/input-size.js
Normal file
73
src/input-size.js
Normal file
@ -0,0 +1,73 @@
|
||||
import React, { Component } from 'react';
|
||||
import InputRange from 'react-input-range';
|
||||
import 'react-input-range/lib/css/index.css';
|
||||
import formatBytes from './format-bytes'
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
|
||||
export default class InputSize extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
size: this.props.value || { min: 0, max: 500 * 1024 },
|
||||
enabled: false || this.props.enabled,
|
||||
maxSize: this.props.maxSize || 1024 * 1024 // 1mb
|
||||
}
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
if(val.maxSize && this.state.size.max > val.maxSize)
|
||||
val.size = {min: this.state.size.min, max: val.maxSize};
|
||||
|
||||
if(val.maxSize && this.state.size.min > val.maxSize)
|
||||
val.size = {min: 0, max: val.size ? val.size.max || this.state.size.max : this.state.size.max };
|
||||
|
||||
super.setState(val, () => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange({
|
||||
enabled: this.state.enabled,
|
||||
maxSize: this.state.maxSize,
|
||||
size: !this.state.enabled ? {min: 0, max: 0} : this.state.size
|
||||
})
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='filter-row row inline w100p'>
|
||||
<Checkbox
|
||||
label="Size filter"
|
||||
checked={this.state.enabled}
|
||||
style={{width: 150}}
|
||||
onCheck={() => this.setState({enabled: !this.state.enabled})}
|
||||
/>
|
||||
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
|
||||
<InputRange
|
||||
maxValue={this.state.maxSize}
|
||||
minValue={0}
|
||||
value={this.state.size}
|
||||
formatLabel={size => formatBytes(size)}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
onChange={size => this.setState({ size })}
|
||||
/>
|
||||
<SelectField
|
||||
floatingLabelText="Size type"
|
||||
value={this.state.maxSize}
|
||||
onChange={(event, index, value) => this.setState({maxSize: value})}
|
||||
className='filter-control-border'
|
||||
>
|
||||
<MenuItem value={1024} primaryText="KB" />
|
||||
<MenuItem value={1024 * 1024} primaryText="MB" />
|
||||
<MenuItem value={1024 * 1024 * 1024} primaryText="GB" />
|
||||
<MenuItem value={10 * 1024 * 1024 * 1024} primaryText="10 GB" />
|
||||
<MenuItem value={100 * 1024 * 1024 * 1024} primaryText="100 GB" />
|
||||
<MenuItem value={1024 * 1024 * 1024 * 1024} primaryText="TB" />
|
||||
</SelectField>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
56
src/search-advanced-controls.js
Normal file
56
src/search-advanced-controls.js
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { Component } from 'react';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import InputSize from './input-size';
|
||||
import FilesFilterInput from './input-files-filter';
|
||||
|
||||
export default class AdvancedSearchControl extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
type: undefined,
|
||||
size: {min: 0, max: 500 * 1024},
|
||||
maxSize: 1024 * 1024,
|
||||
sizeEnabled: false,
|
||||
filesEnabled: false,
|
||||
files: {min: 0, max: 5},
|
||||
filesMax: 100,
|
||||
}
|
||||
if(this.props.state)
|
||||
this.state = Object.assign(this.state, this.props.state)
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
super.setState(val, (v) => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange(this.state)
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='column w100p' style={{maxWidth: 750, overflow: 'hidden', padding: '0px 18px 15px'}}>
|
||||
<SelectField
|
||||
floatingLabelText="Filter content type"
|
||||
value={this.state.type}
|
||||
onChange={(event, index, value) => this.setState({type: value})}
|
||||
>
|
||||
<MenuItem value={undefined} primaryText="" />
|
||||
<MenuItem value='video' primaryText="Video" />
|
||||
<MenuItem value='audio' primaryText="Audio" />
|
||||
<MenuItem value='pictures' primaryText="Pictures" />
|
||||
<MenuItem value='books' primaryText="Books" />
|
||||
<MenuItem value='application' primaryText="Applications" />
|
||||
<MenuItem value='archive' primaryText="Archives" />
|
||||
<MenuItem value='disc' primaryText="Disk Images" />
|
||||
</SelectField>
|
||||
<div className='w100p'>
|
||||
<InputSize value={this.state.size} enabled={this.state.sizeEnabled} maxSize={this.state.maxSize} onChange={({size, maxSize, enabled}) => this.setState({size, maxSize, sizeEnabled: enabled})} />
|
||||
</div>
|
||||
<div className='w100p'>
|
||||
<FilesFilterInput value={this.state.files} filesMax={this.state.filesMax} enabled={this.state.filesEnabled} onChange={({files, filesMax, enabled}) => this.setState({files, filesMax, filesEnabled: enabled})} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import SearchResults from './search-results'
|
||||
import AdvancedSearch from './search-advanced-controls'
|
||||
import TextField from 'material-ui/TextField';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import RefreshIndicator from 'material-ui/RefreshIndicator';
|
||||
@ -8,6 +9,8 @@ import RefreshIndicator from 'material-ui/RefreshIndicator';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import Visibility from 'material-ui/svg-icons/action/visibility';
|
||||
import VisibilityOff from 'material-ui/svg-icons/action/visibility-off';
|
||||
import AddIcon from 'material-ui/svg-icons/content/add';
|
||||
import RemoveIcon from 'material-ui/svg-icons/content/remove';
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
@ -28,8 +31,11 @@ export default class Search extends Component {
|
||||
moreFilesIndicator: false,
|
||||
orderBy: null,
|
||||
orderDesc: false,
|
||||
advancedSearch: false,
|
||||
}
|
||||
this.searchLimit = 10
|
||||
this.advanced = {}
|
||||
this.searchError = undefined;
|
||||
|
||||
if(session)
|
||||
{
|
||||
@ -42,6 +48,9 @@ export default class Search extends Component {
|
||||
Object.assign(this.state, this.setSafeSearch(session.notSafeSearch))
|
||||
this.state.orderBy = session.orderBy;
|
||||
this.state.orderDesc = session.orderDesc;
|
||||
this.state.advancedSearch = session.advancedSearch;
|
||||
this.advanced = session.advanced;
|
||||
this.searchError = session.searchError;
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,12 +64,16 @@ export default class Search extends Component {
|
||||
this.moreSearchFiles = true;
|
||||
this.currentSearch = this.searchValue;
|
||||
let queries = 2;
|
||||
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, {
|
||||
let searchTorrentsParams = {
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
}, window.customLoader((torrents) => {
|
||||
};
|
||||
if(this.state.advancedSearch && this.advanced)
|
||||
searchTorrentsParams = Object.assign(searchTorrentsParams, this.advanced);
|
||||
|
||||
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, searchTorrentsParams, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchTorrents = torrents;
|
||||
if(torrents.length != this.searchLimit)
|
||||
@ -78,12 +91,16 @@ export default class Search extends Component {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}));
|
||||
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, {
|
||||
let searchFilesParams = {
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
}, window.customLoader((torrents) => {
|
||||
};
|
||||
if(this.state.advancedSearch && this.advanced)
|
||||
searchFilesParams = Object.assign(searchFilesParams, this.advanced);
|
||||
|
||||
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, searchFilesParams, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchFiles = torrents;
|
||||
let files = 0;
|
||||
@ -181,6 +198,9 @@ export default class Search extends Component {
|
||||
notSafeSearch: this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
advancedSearch: this.state.advancedSearch,
|
||||
advanced: this.advanced,
|
||||
searchError: this.searchError,
|
||||
}
|
||||
}
|
||||
setSafeSearch(ch) {
|
||||
@ -221,31 +241,62 @@ export default class Search extends Component {
|
||||
fullWidth={true}
|
||||
ref='searchInput'
|
||||
defaultValue={this.searchValue}
|
||||
errorText={this.searchError}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.search();
|
||||
}
|
||||
}}
|
||||
onChange={e => {this.searchValue = e.target.value}}
|
||||
onChange={e => {
|
||||
this.searchValue = e.target.value
|
||||
if(this.searchValue.length < 3 && this.searchValue.length > 0)
|
||||
this.searchError = 'too short string for search';
|
||||
else
|
||||
this.searchError = undefined;
|
||||
this.forceUpdate()
|
||||
}}
|
||||
/>
|
||||
<RaisedButton style={{marginTop: '15px', marginLeft: '10px'}} label="Search" primary={true} onClick={() =>{
|
||||
this.search()
|
||||
}} />
|
||||
</div>
|
||||
<div className='row'>
|
||||
<Checkbox
|
||||
ref='safeSearch'
|
||||
checked={this.notSafeSearch ? true : false}
|
||||
checkedIcon={<Visibility />}
|
||||
uncheckedIcon={<VisibilityOff />}
|
||||
label={<span className='text-nowrap' style={{fontSize: '0.87em', transition: '0.1s', color: this.state.safeSearchColor}}>{this.state.safeSearchText}</span>}
|
||||
iconStyle={{fill: this.state.safeSearchColor}}
|
||||
onCheck={(ev, ch) => {
|
||||
this.setState(this.setSafeSearch(ch));
|
||||
}}
|
||||
style={{paddingBottom: '0.8em'}}
|
||||
/>
|
||||
<div className='row w100p center wrap' style={{padding: '0 8px'}}>
|
||||
<div style={{padding: '0px 17px'}}>
|
||||
<Checkbox
|
||||
ref='safeSearch'
|
||||
checked={this.notSafeSearch ? true : false}
|
||||
checkedIcon={<Visibility />}
|
||||
uncheckedIcon={<VisibilityOff />}
|
||||
label={<span className='text-nowrap' style={{fontSize: '0.87em', transition: '0.1s', color: this.state.safeSearchColor}}>{this.state.safeSearchText}</span>}
|
||||
iconStyle={{fill: this.state.safeSearchColor}}
|
||||
onCheck={(ev, ch) => {
|
||||
this.setState(this.setSafeSearch(ch));
|
||||
}}
|
||||
style={{paddingBottom: '0.8em'}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{padding: '0px 17px'}}>
|
||||
<Checkbox
|
||||
ref='advancedSearch'
|
||||
checked={this.state.advancedSearch}
|
||||
checkedIcon={<RemoveIcon />}
|
||||
uncheckedIcon={<AddIcon />}
|
||||
label={<span className='text-nowrap' style={{fontSize: '0.87em', transition: '0.1s', color: 'black'}}>advanced search</span>}
|
||||
iconStyle={{fill: 'black'}}
|
||||
onCheck={(ev, ch) => {
|
||||
this.setState({advancedSearch: ch});
|
||||
}}
|
||||
style={{paddingBottom: '0.8em'}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
this.state.advancedSearch
|
||||
&&
|
||||
<AdvancedSearch onChange={(state) => {
|
||||
this.advanced = state;
|
||||
}} state={this.advanced} />
|
||||
}
|
||||
{
|
||||
this.stats
|
||||
?
|
||||
|
Loading…
Reference in New Issue
Block a user