134 lines
3.4 KiB
Go
134 lines
3.4 KiB
Go
package common
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strconv"
|
|
|
|
"github.com/Azareal/Gosora/query_gen"
|
|
)
|
|
|
|
var RepliesSearch Searcher
|
|
|
|
type Searcher interface {
|
|
Query(q string, zones []int) ([]int, error)
|
|
}
|
|
|
|
// TODO: Implement this
|
|
// Note: This is slow compared to something like ElasticSearch and very limited
|
|
type SQLSearcher struct {
|
|
queryReplies *sql.Stmt
|
|
queryTopics *sql.Stmt
|
|
queryZone *sql.Stmt
|
|
}
|
|
|
|
// TODO: Support things other than MySQL
|
|
// TODO: Use LIMIT?
|
|
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{
|
|
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` = ?;"),
|
|
}, acc.FirstError()
|
|
}
|
|
|
|
func (s *SQLSearcher) queryAll(q string) ([]int, error) {
|
|
var ids []int
|
|
run := func(stmt *sql.Stmt, q ...interface{}) error {
|
|
rows, err := stmt.Query(q...)
|
|
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()
|
|
}
|
|
|
|
err := run(s.queryReplies, q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = run(s.queryTopics, q, q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(ids) == 0 {
|
|
err = sql.ErrNoRows
|
|
}
|
|
return ids, err
|
|
}
|
|
|
|
func (s *SQLSearcher) Query(q string, zones []int) (ids []int, err error) {
|
|
if len(zones) == 0 {
|
|
return nil, nil
|
|
}
|
|
run := func(rows *sql.Rows, err error) error {
|
|
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 {
|
|
err = run(s.queryZone.Query(q, q, q, zones[0]))
|
|
} 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
|
|
}
|
|
|
|
// TODO: Implement this
|
|
type ElasticSearchSearcher struct {
|
|
}
|
|
|
|
func NewElasticSearchSearcher() (*ElasticSearchSearcher, error) {
|
|
return &ElasticSearchSearcher{}, nil
|
|
}
|
|
|
|
func (s *ElasticSearchSearcher) Query(q string, zones []int) ([]int, error) {
|
|
return nil, nil
|
|
}
|