replace zxq.co/ripple/hanayo
This commit is contained in:
158
vendor/github.com/osuripple/cheesegull/models/set_search.go
generated
vendored
Normal file
158
vendor/github.com/osuripple/cheesegull/models/set_search.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchOptions are options that can be passed to SearchSets for filtering
|
||||
// sets.
|
||||
type SearchOptions struct {
|
||||
// If len is 0, then it should be treated as if all statuses are good.
|
||||
Status []int
|
||||
Query string
|
||||
// Gamemodes to which limit the results. If len is 0, it means all modes
|
||||
// are ok.
|
||||
Mode []int
|
||||
|
||||
// Pagination options.
|
||||
Offset int
|
||||
Amount int
|
||||
}
|
||||
|
||||
func (o SearchOptions) setModes() (total uint8) {
|
||||
for _, m := range o.Mode {
|
||||
if m < 0 || m >= 4 {
|
||||
continue
|
||||
}
|
||||
total |= 1 << uint8(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var mysqlStringReplacer = strings.NewReplacer(
|
||||
`\`, `\\`,
|
||||
`"`, `\"`,
|
||||
`'`, `\'`,
|
||||
"\x00", `\0`,
|
||||
"\n", `\n`,
|
||||
"\r", `\r`,
|
||||
"\x1a", `\Z`,
|
||||
)
|
||||
|
||||
func sIntCommaSeparated(nums []int) string {
|
||||
b := bytes.Buffer{}
|
||||
for idx, num := range nums {
|
||||
b.WriteString(strconv.Itoa(num))
|
||||
if idx != len(nums)-1 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// SearchSets retrieves sets, filtering them using SearchOptions.
|
||||
func SearchSets(db, searchDB *sql.DB, opts SearchOptions) ([]Set, error) {
|
||||
sm := strconv.Itoa(int(opts.setModes()))
|
||||
setIDsQuery := "SELECT id, set_modes & " + sm + " AS valid_set_modes FROM cg WHERE "
|
||||
|
||||
// add filters to query
|
||||
// Yes. I know. Prepared statements. But Sphinx doesn't like them, so
|
||||
// bummer.
|
||||
setIDsQuery += "MATCH('" + mysqlStringReplacer.Replace(opts.Query) + "') "
|
||||
if len(opts.Status) != 0 {
|
||||
setIDsQuery += "AND ranked_status IN (" + sIntCommaSeparated(opts.Status) + ") "
|
||||
}
|
||||
if len(opts.Mode) != 0 {
|
||||
// This is a hack. Apparently, Sphinx does not support AND bitwise
|
||||
// operations in the WHERE clause, so we're placing that in the SELECT
|
||||
// clause and only making sure it's correct in this place.
|
||||
setIDsQuery += "AND valid_set_modes = " + sm + " "
|
||||
}
|
||||
|
||||
// set limit
|
||||
setIDsQuery += fmt.Sprintf("ORDER BY WEIGHT() DESC, id DESC LIMIT %d, %d OPTION ranker=sph04", opts.Offset, opts.Amount)
|
||||
|
||||
// fetch rows
|
||||
rows, err := searchDB.Query(setIDsQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// from the rows we will retrieve the IDs of all our sets.
|
||||
// we also pre-create the slices containing the sets we will fill later on
|
||||
// when we fetch the actual data.
|
||||
setIDs := make([]int, 0, opts.Amount)
|
||||
sets := make([]Set, 0, opts.Amount)
|
||||
// setMap, having an ID, points to a position of a set contained in sets.
|
||||
setMap := make(map[int]int, opts.Amount)
|
||||
for rows.Next() {
|
||||
var id int
|
||||
err = rows.Scan(&id, new(int))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setIDs = append(setIDs, id)
|
||||
sets = append(sets, Set{})
|
||||
setMap[id] = len(sets) - 1
|
||||
}
|
||||
|
||||
// short circuit: there are no sets
|
||||
if len(sets) == 0 {
|
||||
return []Set{}, nil
|
||||
}
|
||||
|
||||
setsQuery := "SELECT " + setFields + " FROM sets WHERE id IN (" + inClause(len(setIDs)) + ")"
|
||||
args := sIntToSInterface(setIDs)
|
||||
|
||||
rows, err = db.Query(setsQuery, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// find all beatmaps, but leave children aside for the moment.
|
||||
for rows.Next() {
|
||||
var s Set
|
||||
err = rows.Scan(
|
||||
&s.ID, &s.RankedStatus, &s.ApprovedDate, &s.LastUpdate, &s.LastChecked,
|
||||
&s.Artist, &s.Title, &s.Creator, &s.Source, &s.Tags, &s.HasVideo, &s.Genre,
|
||||
&s.Language, &s.Favourites,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sets[setMap[s.ID]] = s
|
||||
}
|
||||
|
||||
rows, err = db.Query(
|
||||
"SELECT "+beatmapFields+" FROM beatmaps WHERE parent_set_id IN ("+
|
||||
inClause(len(setIDs))+")",
|
||||
sIntToSInterface(setIDs)...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var b Beatmap
|
||||
err = rows.Scan(
|
||||
&b.ID, &b.ParentSetID, &b.DiffName, &b.FileMD5, &b.Mode, &b.BPM,
|
||||
&b.AR, &b.OD, &b.CS, &b.HP, &b.TotalLength, &b.HitLength,
|
||||
&b.Playcount, &b.Passcount, &b.MaxCombo, &b.DifficultyRating,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentSet, ok := setMap[b.ParentSetID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sets[parentSet].ChildrenBeatmaps = append(sets[parentSet].ChildrenBeatmaps, b)
|
||||
}
|
||||
|
||||
return sets, nil
|
||||
}
|
Reference in New Issue
Block a user