statping/database/grouping.go

223 lines
4.2 KiB
Go
Raw Normal View History

2020-02-24 16:26:01 +00:00
package database
import (
2020-03-11 04:37:52 +00:00
"errors"
2020-02-24 16:26:01 +00:00
"fmt"
2020-03-09 18:17:55 +00:00
"github.com/statping/statping/types"
"github.com/statping/statping/utils"
2020-02-24 16:26:01 +00:00
"net/http"
"net/url"
"strconv"
"time"
)
type GroupBy struct {
db Database
query *GroupQuery
}
type GroupByer interface {
2020-02-26 05:38:03 +00:00
ToTimeValue() (*TimeVar, error)
2020-02-24 16:26:01 +00:00
}
type By string
func (b By) String() string {
return string(b)
}
type GroupQuery struct {
Start time.Time
End time.Time
2020-03-06 09:33:46 +00:00
Group time.Duration
2020-02-24 16:26:01 +00:00
Order string
Limit int
Offset int
FillEmpty bool
2020-02-26 05:38:03 +00:00
db Database
2020-02-24 16:26:01 +00:00
}
2020-02-25 07:41:28 +00:00
func (b GroupQuery) Find(data interface{}) error {
2020-03-04 10:29:00 +00:00
return b.db.Find(data).Error()
2020-02-25 07:41:28 +00:00
}
2020-02-24 16:26:01 +00:00
func (b GroupQuery) Database() Database {
return b.db
}
var (
2020-02-25 07:41:28 +00:00
ByCount = By("COUNT(id) as amount")
2020-03-06 09:33:46 +00:00
ByAverage = func(column string, multiplier int) By {
switch database.DbType() {
case "mysql":
2020-03-10 05:24:35 +00:00
return By(fmt.Sprintf("CAST(AVG(%s) as UNSIGNED) as amount", column))
2020-03-06 09:33:46 +00:00
case "postgres":
2020-03-10 05:24:35 +00:00
return By(fmt.Sprintf("cast(AVG(%s) as int) as amount", column))
2020-03-06 09:33:46 +00:00
default:
2020-03-10 05:24:35 +00:00
return By(fmt.Sprintf("cast(AVG(%s) as int) as amount", column))
2020-03-06 09:33:46 +00:00
}
2020-02-24 16:26:01 +00:00
}
)
type TimeVar struct {
2020-02-26 05:38:03 +00:00
g *GroupQuery
2020-02-24 16:26:01 +00:00
data []*TimeValue
}
2020-02-26 05:38:03 +00:00
func (t *TimeVar) ToValues() ([]*TimeValue, error) {
return t.data, nil
2020-02-24 16:26:01 +00:00
}
2020-02-26 05:38:03 +00:00
// GraphData will return all hits or failures
func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
dbQuery := g.db.MultipleSelects(
g.db.SelectByTime(g.Group),
by.String(),
2020-03-06 22:18:06 +00:00
).Group("timeframe")
2020-02-26 05:38:03 +00:00
g.db = dbQuery
caller, err := g.ToTimeValue()
if err != nil {
return nil, err
}
if g.FillEmpty {
return caller.FillMissing(g.Start, g.End)
}
return caller.ToValues()
}
func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
2020-02-24 16:26:01 +00:00
rows, err := g.db.Rows()
if err != nil {
return nil, err
}
var data []*TimeValue
for rows.Next() {
2020-02-26 05:38:03 +00:00
var timeframe string
2020-03-06 22:18:06 +00:00
amount := int64(0)
2020-02-26 05:38:03 +00:00
if err := rows.Scan(&timeframe, &amount); err != nil {
log.Error(err, timeframe)
}
trueTime, _ := g.db.ParseTime(timeframe)
2020-03-06 09:33:46 +00:00
newTs := types.FixedTime(trueTime, g.Group)
2020-02-24 16:26:01 +00:00
data = append(data, &TimeValue{
Timeframe: newTs,
Amount: amount,
})
}
return &TimeVar{g, data}, nil
}
2020-02-26 05:38:03 +00:00
func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
2020-03-06 22:18:06 +00:00
timeMap := make(map[string]int64)
2020-02-24 16:26:01 +00:00
var validSet []*TimeValue
2020-03-06 09:33:46 +00:00
dur := t.g.Group
2020-02-24 16:26:01 +00:00
for _, v := range t.data {
timeMap[v.Timeframe] = v.Amount
}
2020-03-06 09:33:46 +00:00
currentStr := types.FixedTime(current, t.g.Group)
2020-02-24 16:26:01 +00:00
for {
2020-03-06 22:18:06 +00:00
var amount int64
2020-02-24 16:26:01 +00:00
if timeMap[currentStr] != 0 {
amount = timeMap[currentStr]
}
validSet = append(validSet, &TimeValue{
Timeframe: currentStr,
Amount: amount,
})
if current.After(end) {
break
}
current = current.Add(dur)
2020-03-06 09:33:46 +00:00
currentStr = types.FixedTime(current, t.g.Group)
2020-02-24 16:26:01 +00:00
}
2020-02-26 05:38:03 +00:00
return validSet, nil
2020-02-24 16:26:01 +00:00
}
2020-03-04 10:29:00 +00:00
type isObject interface {
Db() Database
}
2020-03-11 04:37:52 +00:00
func ParseQueries(r *http.Request, o isObject) (*GroupQuery, error) {
2020-02-24 16:26:01 +00:00
fields := parseGet(r)
grouping := fields.Get("group")
2020-02-27 10:04:27 +00:00
startField := utils.ToInt(fields.Get("start"))
endField := utils.ToInt(fields.Get("end"))
limit := utils.ToInt(fields.Get("limit"))
offset := utils.ToInt(fields.Get("offset"))
2020-02-24 16:26:01 +00:00
fill, _ := strconv.ParseBool(fields.Get("fill"))
orderBy := fields.Get("order")
if limit == 0 {
limit = 10000
}
2020-03-10 05:24:35 +00:00
q := o.Db()
2020-02-26 05:38:03 +00:00
2020-03-06 09:33:46 +00:00
if grouping == "" {
grouping = "1h"
}
groupDur, err := time.ParseDuration(grouping)
if err != nil {
log.Errorln(err)
groupDur = 1 * time.Hour
}
2020-02-24 16:26:01 +00:00
query := &GroupQuery{
Start: time.Unix(startField, 0).UTC(),
End: time.Unix(endField, 0).UTC(),
2020-03-06 09:33:46 +00:00
Group: groupDur,
2020-02-24 16:26:01 +00:00
Order: orderBy,
Limit: int(limit),
Offset: int(offset),
FillEmpty: fill,
2020-03-10 05:24:35 +00:00
db: q,
2020-02-24 16:26:01 +00:00
}
2020-03-11 04:37:52 +00:00
if query.Start.After(query.End) {
return nil, errors.New("start time is after ending time")
}
2020-03-04 10:29:00 +00:00
if startField == 0 {
2020-03-11 04:37:52 +00:00
query.Start = utils.Now().Add(-7 * types.Day)
2020-03-04 10:29:00 +00:00
}
if endField == 0 {
2020-03-11 04:37:52 +00:00
query.End = utils.Now()
2020-03-04 10:29:00 +00:00
}
2020-03-06 09:33:46 +00:00
if query.End.After(utils.Now()) {
query.End = utils.Now()
}
2020-03-04 10:29:00 +00:00
2020-02-24 16:26:01 +00:00
if query.Limit != 0 {
2020-03-10 05:24:35 +00:00
q = q.Limit(query.Limit)
2020-02-24 16:26:01 +00:00
}
if query.Offset > 0 {
2020-03-10 05:24:35 +00:00
q = q.Offset(query.Offset)
2020-02-24 16:26:01 +00:00
}
2020-03-04 10:29:00 +00:00
2020-03-10 05:24:35 +00:00
q = q.Where("created_at BETWEEN ? AND ?", q.FormatTime(query.Start), q.FormatTime(query.End))
2020-03-04 10:29:00 +00:00
2020-02-24 16:26:01 +00:00
if query.Order != "" {
2020-03-10 05:24:35 +00:00
q = q.Order(query.Order)
2020-02-24 16:26:01 +00:00
}
2020-03-10 05:24:35 +00:00
query.db = q
2020-02-24 16:26:01 +00:00
2020-03-11 04:37:52 +00:00
return query, nil
2020-02-24 16:26:01 +00:00
}
func parseForm(r *http.Request) url.Values {
r.ParseForm()
return r.PostForm
}
func parseGet(r *http.Request) url.Values {
r.ParseForm()
return r.Form
}