2019-02-10 05:52:26 +00:00
package common
import (
"database/sql"
"errors"
2019-02-23 06:29:19 +00:00
"strconv"
2019-02-10 05:52:26 +00:00
"github.com/Azareal/Gosora/query_gen"
)
2019-02-23 06:29:19 +00:00
var RepliesSearch Searcher
2019-02-10 05:52:26 +00:00
type Searcher interface {
2019-02-23 06:29:19 +00:00
Query ( q string , zones [ ] int ) ( [ ] int , error )
2019-02-10 05:52:26 +00:00
}
// TODO: Implement this
// Note: This is slow compared to something like ElasticSearch and very limited
type SQLSearcher struct {
2019-02-23 06:29:19 +00:00
queryReplies * sql . Stmt
queryTopics * sql . Stmt
queryZone * sql . Stmt
2019-02-10 05:52:26 +00:00
}
// TODO: Support things other than MySQL
2019-02-23 06:29:19 +00:00
// TODO: Use LIMIT?
2019-02-10 05:52:26 +00:00
func NewSQLSearcher ( acc * qgen . Accumulator ) ( * SQLSearcher , error ) {
if acc . GetAdapter ( ) . GetName ( ) != "mysql" {
return nil , errors . New ( "SQLSearcher only supports MySQL at this time" )
}
return & SQLSearcher {
2019-02-23 06:29:19 +00:00
queryReplies : acc . RawPrepare ( "SELECT `tid` FROM `replies` WHERE MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE);" ) ,
queryTopics : acc . RawPrepare ( "SELECT `tid` FROM `topics` WHERE MATCH(title) AGAINST (? IN NATURAL LANGUAGE MODE) OR MATCH(content) AGAINST (? IN NATURAL LANGUAGE MODE);" ) ,
queryZone : acc . RawPrepare ( "SELECT `topics`.`tid` FROM `topics` INNER JOIN `replies` ON `topics`.`tid` = `replies`.`tid` WHERE (MATCH(`topics`.`title`) AGAINST (? IN NATURAL LANGUAGE MODE) OR MATCH(`topics`.`content`) AGAINST (? IN NATURAL LANGUAGE MODE) OR MATCH(`replies`.`content`) AGAINST (? IN NATURAL LANGUAGE MODE)) AND `topics`.`parentID` = ?;" ) ,
2019-02-10 05:52:26 +00:00
} , acc . FirstError ( )
}
2019-10-01 21:06:22 +00:00
func ( s * SQLSearcher ) queryAll ( q string ) ( [ ] int , error ) {
2019-02-23 06:29:19 +00:00
var ids [ ] int
2019-10-01 21:06:22 +00:00
run := func ( stmt * sql . Stmt , q ... interface { } ) error {
2019-02-23 06:29:19 +00:00
rows , err := stmt . Query ( q ... )
if err == sql . ErrNoRows {
return nil
} else if err != nil {
return err
2019-02-10 05:52:26 +00:00
}
defer rows . Close ( )
2019-02-23 06:29:19 +00:00
for rows . Next ( ) {
var id int
err := rows . Scan ( & id )
if err != nil {
return err
}
ids = append ( ids , id )
}
return rows . Err ( )
}
2019-10-01 21:06:22 +00:00
err := run ( s . queryReplies , q )
2019-02-23 06:29:19 +00:00
if err != nil {
return nil , err
}
2019-10-01 21:06:22 +00:00
err = run ( s . queryTopics , q , q )
2019-02-23 06:29:19 +00:00
if err != nil {
return nil , err
}
if len ( ids ) == 0 {
err = sql . ErrNoRows
}
return ids , err
2019-02-10 05:52:26 +00:00
}
2019-10-01 21:06:22 +00:00
func ( s * SQLSearcher ) Query ( q string , zones [ ] int ) ( ids [ ] int , err error ) {
2019-02-23 06:29:19 +00:00
if len ( zones ) == 0 {
return nil , nil
}
2019-10-01 21:06:22 +00:00
run := func ( rows * sql . Rows , err error ) error {
2019-02-23 06:29:19 +00:00
if err == sql . ErrNoRows {
return nil
} else if err != nil {
return err
}
defer rows . Close ( )
for rows . Next ( ) {
var id int
err := rows . Scan ( & id )
if err != nil {
return err
}
ids = append ( ids , id )
}
return rows . Err ( )
}
if len ( zones ) == 1 {
2019-10-01 21:06:22 +00:00
err = run ( s . queryZone . Query ( q , q , q , zones [ 0 ] ) )
2019-02-23 06:29:19 +00:00
} else {
var zList string
for _ , zone := range zones {
zList += strconv . Itoa ( zone ) + ","
}
zList = zList [ : len ( zList ) - 1 ]
acc := qgen . NewAcc ( )
stmt := acc . RawPrepare ( "SELECT `topics`.`tid` FROM `topics` INNER JOIN `replies` ON `topics`.`tid` = `replies`.`tid` WHERE (MATCH(`topics`.`title`) AGAINST (? IN NATURAL LANGUAGE MODE) OR MATCH(`topics`.`content`) AGAINST (? IN NATURAL LANGUAGE MODE) OR MATCH(`replies`.`content`) AGAINST (? IN NATURAL LANGUAGE MODE)) AND `topics`.`parentID` IN(" + zList + ");" )
err := acc . FirstError ( )
if err != nil {
return nil , err
}
err = run ( stmt . Query ( q , q , q ) )
}
if err != nil {
return nil , err
}
if len ( ids ) == 0 {
err = sql . ErrNoRows
}
return ids , err
2019-02-10 05:52:26 +00:00
}
// TODO: Implement this
type ElasticSearchSearcher struct {
}
2019-02-23 06:29:19 +00:00
func NewElasticSearchSearcher ( ) ( * ElasticSearchSearcher , error ) {
return & ElasticSearchSearcher { } , nil
2019-02-10 05:52:26 +00:00
}
2019-10-01 21:06:22 +00:00
func ( s * ElasticSearchSearcher ) Query ( q string , zones [ ] int ) ( [ ] int , error ) {
2019-02-10 05:52:26 +00:00
return nil , nil
}