441 lines
14 KiB
Go
441 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// AnalyticsFilters represents the filter state for analytics queries
|
|
type AnalyticsFilters struct {
|
|
KillHour []uint8
|
|
KillDayOfWeek []uint8
|
|
KillDate []string
|
|
Month []string
|
|
SolarSystemID []int32
|
|
RegionName []string
|
|
ConstellationName []string
|
|
SecurityStatus []string
|
|
VictimShipTypeID []int32
|
|
VictimShipGroupName []string
|
|
VictimShipCategory []string
|
|
VictimCharacterName []string
|
|
VictimCorporation []string
|
|
VictimAlliance []string
|
|
AttackerShipType []string
|
|
AttackerShipGroup []string
|
|
AttackerCharacter []string
|
|
AttackerCorporation []string
|
|
AttackerAlliance []string
|
|
SlotType []string
|
|
HasModule *ModuleFilter
|
|
}
|
|
|
|
type ModuleFilter struct {
|
|
Slot string
|
|
ModuleID int32
|
|
}
|
|
|
|
// Time aggregation results
|
|
type TimeAggregationByHour struct {
|
|
KillHour uint8 `json:"kill_hour"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
SystemsHit uint64 `json:"systems_hit"`
|
|
}
|
|
|
|
type TimeAggregationByDay struct {
|
|
KillDayOfWeek uint8 `json:"kill_day_of_week"`
|
|
DayName string `json:"day_name"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
}
|
|
|
|
type TimeAggregationByDate struct {
|
|
KillDate string `json:"kill_date"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
SystemsHit uint64 `json:"systems_hit"`
|
|
}
|
|
|
|
type TimeAggregationByMonth struct {
|
|
Month string `json:"month"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
}
|
|
|
|
// Location aggregation results
|
|
type LocationAggregationBySystem struct {
|
|
SolarSystemID int32 `json:"solar_system_id"`
|
|
SolarSystemName string `json:"solar_system_name"`
|
|
RegionName string `json:"region_name"`
|
|
Security float32 `json:"security"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
ShipVariety uint64 `json:"ship_variety"`
|
|
}
|
|
|
|
type LocationAggregationByRegion struct {
|
|
RegionName string `json:"region_name"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
SystemsWithKills uint64 `json:"systems_with_kills"`
|
|
UniqueVictims uint64 `json:"unique_victims"`
|
|
}
|
|
|
|
type LocationAggregationByConstellation struct {
|
|
ConstellationName string `json:"constellation_name"`
|
|
RegionName string `json:"region_name"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
SystemsInConstellation uint64 `json:"systems_in_constellation"`
|
|
}
|
|
|
|
type LocationAggregationBySecurity struct {
|
|
SecStatus string `json:"sec_status"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
Systems uint64 `json:"systems"`
|
|
}
|
|
|
|
// Ship aggregation results
|
|
type ShipAggregationByVictimShip struct {
|
|
ShipTypeID int32 `json:"victim_ship_type_id"`
|
|
ShipTypeName string `json:"victim_ship_type_name"`
|
|
ShipGroupName string `json:"victim_ship_group_name"`
|
|
ShipCategoryName string `json:"victim_ship_category_name"`
|
|
KillCount uint64 `json:"kill_count"`
|
|
UniquePilots uint64 `json:"unique_pilots_killed"`
|
|
}
|
|
|
|
type ShipAggregationByAttackerShip struct {
|
|
ShipTypeName string `json:"ship_type_name"`
|
|
ShipGroupName string `json:"ship_group_name"`
|
|
TimesUsed uint64 `json:"times_used"`
|
|
KillsParticipated uint64 `json:"kills_participated"`
|
|
TotalDamage int64 `json:"total_damage"`
|
|
FinalBlows uint64 `json:"final_blows"`
|
|
}
|
|
|
|
// Player/Entity aggregation results
|
|
type PlayerAggregationByVictimCharacter struct {
|
|
CharacterName string `json:"victim_character_name"`
|
|
Corporation string `json:"victim_corporation_name"`
|
|
Alliance string `json:"victim_alliance_name"`
|
|
Deaths uint64 `json:"deaths"`
|
|
ShipsLost uint64 `json:"ships_lost"`
|
|
}
|
|
|
|
type PlayerAggregationByVictimCorporation struct {
|
|
Corporation string `json:"victim_corporation_name"`
|
|
Alliance string `json:"victim_alliance_name"`
|
|
Losses uint64 `json:"losses"`
|
|
MembersKilled uint64 `json:"members_killed"`
|
|
}
|
|
|
|
type PlayerAggregationByVictimAlliance struct {
|
|
Alliance string `json:"victim_alliance_name"`
|
|
Losses uint64 `json:"losses"`
|
|
CorpsHit uint64 `json:"corps_hit"`
|
|
MembersKilled uint64 `json:"members_killed"`
|
|
}
|
|
|
|
type PlayerAggregationByAttackerCharacter struct {
|
|
CharacterName string `json:"character_name"`
|
|
Corporation string `json:"corporation_name"`
|
|
Alliance string `json:"alliance_name"`
|
|
KillsParticipated uint64 `json:"kills_participated"`
|
|
FinalBlows uint64 `json:"final_blows"`
|
|
TotalDamage int64 `json:"total_damage"`
|
|
}
|
|
|
|
type PlayerAggregationByAttackerCorporation struct {
|
|
Corporation string `json:"corporation_name"`
|
|
Alliance string `json:"alliance_name"`
|
|
KillsParticipated uint64 `json:"kills_participated"`
|
|
MembersInvolved uint64 `json:"members_involved"`
|
|
}
|
|
|
|
type PlayerAggregationByAttackerAlliance struct {
|
|
Alliance string `json:"alliance_name"`
|
|
KillsParticipated uint64 `json:"kills_participated"`
|
|
CorpsInvolved uint64 `json:"corps_involved"`
|
|
MembersInvolved uint64 `json:"members_involved"`
|
|
}
|
|
|
|
// Module/Item aggregation results
|
|
type ModuleAggregationBySlotType struct {
|
|
SlotType string `json:"slot_type"`
|
|
ItemsFitted uint64 `json:"items_fitted"`
|
|
ShipsWithSlot uint64 `json:"ships_with_slot"`
|
|
ModuleVariety uint64 `json:"module_variety"`
|
|
}
|
|
|
|
type ModuleAggregationByModule struct {
|
|
ItemTypeID int32 `json:"item_type_id"`
|
|
ItemTypeName string `json:"item_type_name"`
|
|
ItemGroupName string `json:"item_group_name"`
|
|
ItemCategoryName string `json:"item_category_name"`
|
|
TimesFitted uint64 `json:"times_fitted"`
|
|
ShipsWithModule uint64 `json:"ships_with_module"`
|
|
}
|
|
|
|
type ModuleCoOccurrence struct {
|
|
ItemTypeName string `json:"item_type_name"`
|
|
SlotType string `json:"slot_type"`
|
|
TimesTogether uint64 `json:"times_fitted_together"`
|
|
Percentage float64 `json:"percentage"`
|
|
}
|
|
|
|
// buildWhereClause builds a WHERE clause from filters
|
|
func buildWhereClause(filters AnalyticsFilters) (string, []interface{}) {
|
|
var conditions []string
|
|
var args []interface{}
|
|
|
|
if len(filters.KillHour) > 0 {
|
|
placeholders := make([]string, len(filters.KillHour))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "kill_hour IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, h := range filters.KillHour {
|
|
args = append(args, h)
|
|
}
|
|
}
|
|
|
|
if len(filters.KillDayOfWeek) > 0 {
|
|
placeholders := make([]string, len(filters.KillDayOfWeek))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "kill_day_of_week IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, d := range filters.KillDayOfWeek {
|
|
args = append(args, d)
|
|
}
|
|
}
|
|
|
|
if len(filters.KillDate) > 0 {
|
|
placeholders := make([]string, len(filters.KillDate))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "kill_date IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, d := range filters.KillDate {
|
|
args = append(args, d)
|
|
}
|
|
}
|
|
|
|
if len(filters.Month) > 0 {
|
|
placeholders := make([]string, len(filters.Month))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "toYYYYMM(killmail_time) IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, m := range filters.Month {
|
|
args = append(args, m)
|
|
}
|
|
}
|
|
|
|
if len(filters.SolarSystemID) > 0 {
|
|
placeholders := make([]string, len(filters.SolarSystemID))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "solar_system_id IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, id := range filters.SolarSystemID {
|
|
args = append(args, id)
|
|
}
|
|
}
|
|
|
|
if len(filters.RegionName) > 0 {
|
|
placeholders := make([]string, len(filters.RegionName))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "region_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, r := range filters.RegionName {
|
|
args = append(args, r)
|
|
}
|
|
}
|
|
|
|
if len(filters.ConstellationName) > 0 {
|
|
placeholders := make([]string, len(filters.ConstellationName))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "constellation_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.ConstellationName {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.SecurityStatus) > 0 {
|
|
var secConditions []string
|
|
for _, s := range filters.SecurityStatus {
|
|
switch s {
|
|
case "High Sec":
|
|
secConditions = append(secConditions, "security >= 0.5")
|
|
case "Low Sec":
|
|
secConditions = append(secConditions, "security > 0.0 AND security < 0.5")
|
|
case "Null Sec":
|
|
secConditions = append(secConditions, "security <= 0.0")
|
|
}
|
|
}
|
|
if len(secConditions) > 0 {
|
|
conditions = append(conditions, "("+strings.Join(secConditions, " OR ")+")")
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimShipTypeID) > 0 {
|
|
placeholders := make([]string, len(filters.VictimShipTypeID))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_ship_type_id IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, id := range filters.VictimShipTypeID {
|
|
args = append(args, id)
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimShipGroupName) > 0 {
|
|
placeholders := make([]string, len(filters.VictimShipGroupName))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_ship_group_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, g := range filters.VictimShipGroupName {
|
|
args = append(args, g)
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimShipCategory) > 0 {
|
|
placeholders := make([]string, len(filters.VictimShipCategory))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_ship_category_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.VictimShipCategory {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimCharacterName) > 0 {
|
|
placeholders := make([]string, len(filters.VictimCharacterName))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_character_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.VictimCharacterName {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimCorporation) > 0 {
|
|
placeholders := make([]string, len(filters.VictimCorporation))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_corporation_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.VictimCorporation {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.VictimAlliance) > 0 {
|
|
placeholders := make([]string, len(filters.VictimAlliance))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "victim_alliance_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, a := range filters.VictimAlliance {
|
|
args = append(args, a)
|
|
}
|
|
}
|
|
|
|
if filters.HasModule != nil {
|
|
subquery := `SELECT DISTINCT killmail_id FROM zkill.killmail_items WHERE slot_type = ? AND item_type_id = ?`
|
|
conditions = append(conditions, fmt.Sprintf("killmail_id IN (%s)", subquery))
|
|
args = append(args, filters.HasModule.Slot, filters.HasModule.ModuleID)
|
|
}
|
|
|
|
whereClause := ""
|
|
if len(conditions) > 0 {
|
|
whereClause = "WHERE " + strings.Join(conditions, " AND ")
|
|
}
|
|
|
|
return whereClause, args
|
|
}
|
|
|
|
// buildAttackerWhereClause builds WHERE clause for attacker queries
|
|
func buildAttackerWhereClause(filters AnalyticsFilters) (string, []interface{}) {
|
|
var conditions []string
|
|
var args []interface{}
|
|
|
|
if len(filters.AttackerShipType) > 0 {
|
|
placeholders := make([]string, len(filters.AttackerShipType))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "ship_type_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, s := range filters.AttackerShipType {
|
|
args = append(args, s)
|
|
}
|
|
}
|
|
|
|
if len(filters.AttackerShipGroup) > 0 {
|
|
placeholders := make([]string, len(filters.AttackerShipGroup))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "ship_group_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, g := range filters.AttackerShipGroup {
|
|
args = append(args, g)
|
|
}
|
|
}
|
|
|
|
if len(filters.AttackerCharacter) > 0 {
|
|
placeholders := make([]string, len(filters.AttackerCharacter))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "character_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.AttackerCharacter {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.AttackerCorporation) > 0 {
|
|
placeholders := make([]string, len(filters.AttackerCorporation))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "corporation_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, c := range filters.AttackerCorporation {
|
|
args = append(args, c)
|
|
}
|
|
}
|
|
|
|
if len(filters.AttackerAlliance) > 0 {
|
|
placeholders := make([]string, len(filters.AttackerAlliance))
|
|
for i := range placeholders {
|
|
placeholders[i] = "?"
|
|
}
|
|
conditions = append(conditions, "alliance_name IN ("+strings.Join(placeholders, ",")+")")
|
|
for _, a := range filters.AttackerAlliance {
|
|
args = append(args, a)
|
|
}
|
|
}
|
|
|
|
whereClause := ""
|
|
if len(conditions) > 0 {
|
|
whereClause = "AND " + strings.Join(conditions, " AND ")
|
|
}
|
|
|
|
return whereClause, args
|
|
}
|
|
|
|
// getKillmailIDSubquery returns a subquery to filter by killmail_id from killmails table
|
|
func getKillmailIDSubquery(filters AnalyticsFilters) (string, []interface{}) {
|
|
whereClause, args := buildWhereClause(filters)
|
|
if whereClause == "" {
|
|
return "(SELECT killmail_id FROM zkill.killmails)", args
|
|
}
|
|
return fmt.Sprintf("(SELECT killmail_id FROM zkill.killmails %s)", whereClause), args
|
|
}
|