Дополнительные параметры поиска

This commit is contained in:
Alexey Kasyanchuk 2017-11-09 12:30:06 +03:00
parent 3e8cbd07ed
commit b947b670b3
7 changed files with 348 additions and 22 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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
View 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
View 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>
);
}
}

View 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>
);
}
}

View File

@ -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,18 +241,27 @@ 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'>
<div className='row w100p center wrap' style={{padding: '0 8px'}}>
<div style={{padding: '0px 17px'}}>
<Checkbox
ref='safeSearch'
checked={this.notSafeSearch ? true : false}
@ -246,6 +275,28 @@ export default class Search extends Component {
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
?