Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0
Ce dépôt a été archivé le 2022-05-07. Vous pouvez voir ses fichiers ou le cloner, mais pas ouvrir de ticket ou de demandes d'ajout, ni soumettre de changements.
nyaa-pantsu/util/search/search.go
2017-05-24 17:11:13 +10:00

229 lignes
6 Kio
Go

package search
import (
"net/http"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/NyaaPantsu/nyaa/cache"
"github.com/NyaaPantsu/nyaa/common"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/db"
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service"
"github.com/NyaaPantsu/nyaa/service/torrent"
"github.com/NyaaPantsu/nyaa/util/log"
)
var searchOperator string
var useTSQuery bool
func Configure(conf *config.SearchConfig) (err error) {
useTSQuery = false
// Postgres needs ILIKE for case-insensitivity
if db.ORM.Dialect().GetName() == "postgres" {
searchOperator = "ILIKE ?"
//useTSQuery = true
// !!DISABLED!! because this makes search a lot stricter
// (only matches at word borders)
} else {
searchOperator = "LIKE ?"
}
return
}
func stringIsAscii(input string) bool {
for _, char := range input {
if char > 127 {
return false
}
}
return true
}
func SearchByQuery(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(r, pagenum, true, false)
return
}
func SearchByQueryWithUser(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(r, pagenum, true, true)
return
}
func SearchByQueryNoCount(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, err error) {
search, tor, _, err = searchByQuery(r, pagenum, false, false)
return
}
func searchByQuery(r *http.Request, pagenum int, countAll bool, withUser bool) (
search common.SearchParam, tor []model.Torrent, count int, err error,
) {
max, err := strconv.ParseUint(r.URL.Query().Get("max"), 10, 32)
if err != nil {
max = 50 // default Value maxPerPage
} else if max > 300 {
max = 300
}
search.Max = uint(max)
search.Page = pagenum
search.Query = r.URL.Query().Get("q")
userID, _ := strconv.Atoi(r.URL.Query().Get("userID"))
search.UserID = uint(userID)
switch s := r.URL.Query().Get("s"); s {
case "1":
search.Status = common.FilterRemakes
case "2":
search.Status = common.Trusted
case "3":
search.Status = common.APlus
}
catString := r.URL.Query().Get("c")
if s := catString; len(s) > 1 && s != "_" {
var tmp uint64
tmp, err = strconv.ParseUint(string(s[0]), 10, 8)
if err != nil {
return
}
search.Category.Main = uint8(tmp)
if len(s) > 2 && len(s) < 5 {
tmp, err = strconv.ParseUint(s[2:], 10, 8)
if err != nil {
return
}
search.Category.Sub = uint8(tmp)
}
}
orderBy := ""
switch s := r.URL.Query().Get("sort"); s {
case "1":
search.Sort = common.Name
orderBy += "torrent_name"
break
case "2":
search.Sort = common.Date
orderBy += "date"
search.NotNull = "date IS NOT NULL"
break
case "3":
search.Sort = common.Downloads
orderBy += "downloads"
break
case "4":
search.Sort = common.Size
orderBy += "filesize"
// avoid sorting completely breaking on postgres
search.NotNull = ""
break
case "5":
search.Sort = common.Seeders
orderBy += "seeders"
search.NotNull = ""
break
case "6":
search.Sort = common.Leechers
orderBy += "leechers"
search.NotNull = ""
break
case "7":
search.Sort = common.Completed
orderBy += "completed"
search.NotNull = ""
break
default:
search.Sort = common.ID
orderBy += "torrent_id"
}
orderBy += " "
switch s := r.URL.Query().Get("order"); s {
case "true":
search.Order = true
orderBy += "asc"
if db.ORM.Dialect().GetName() == "postgres" {
orderBy += " NULLS FIRST"
}
default:
orderBy += "desc"
if db.ORM.Dialect().GetName() == "postgres" {
orderBy += " NULLS LAST"
}
}
parameters := serviceBase.WhereParams{
Params: make([]interface{}, 0, 64),
}
conditions := make([]string, 0, 64)
if search.Category.Main != 0 {
conditions = append(conditions, "category = ?")
parameters.Params = append(parameters.Params, search.Category.Main)
}
if search.UserID != 0 {
conditions = append(conditions, "uploader = ?")
parameters.Params = append(parameters.Params, search.UserID)
}
if search.Category.Sub != 0 {
conditions = append(conditions, "sub_category = ?")
parameters.Params = append(parameters.Params, search.Category.Sub)
}
if search.Status != 0 {
if search.Status == common.FilterRemakes {
conditions = append(conditions, "status <> ?")
} else {
conditions = append(conditions, "status >= ?")
}
parameters.Params = append(parameters.Params, strconv.Itoa(int(search.Status)+1))
}
if len(search.NotNull) > 0 {
conditions = append(conditions, search.NotNull)
}
searchQuerySplit := strings.Fields(search.Query)
for _, word := range searchQuerySplit {
firstRune, _ := utf8.DecodeRuneInString(word)
if len(word) == 1 && unicode.IsPunct(firstRune) {
// some queries have a single punctuation character
// which causes a full scan instead of using the index
// and yields no meaningful results.
// due to len() == 1 we're just looking at 1-byte/ascii
// punctuation characters.
continue
}
if useTSQuery && stringIsAscii(word) {
conditions = append(conditions, "torrent_name @@ plainto_tsquery(?)")
parameters.Params = append(parameters.Params, word)
} else {
// TODO: possible to make this faster?
conditions = append(conditions, "torrent_name "+searchOperator)
parameters.Params = append(parameters.Params, "%"+word+"%")
}
}
parameters.Conditions = strings.Join(conditions[:], " AND ")
log.Infof("SQL query is :: %s\n", parameters.Conditions)
tor, count, err = cache.Impl.Get(search, func() (tor []model.Torrent, count int, err error) {
if countAll && !withUser {
tor, count, err = torrentService.GetTorrentsOrderBy(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
} else if withUser {
tor, count, err = torrentService.GetTorrentsWithUserOrderBy(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
} else {
tor, err = torrentService.GetTorrentsOrderByNoCount(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
}
return
})
return
}