mirror of
https://codeberg.org/icewind/log-normalizer.git
synced 2026-06-03 13:54:11 +02:00
426 lines
No EOL
16 KiB
PL/PgSQL
426 lines
No EOL
16 KiB
PL/PgSQL
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public;
|
|
|
|
CREATE TYPE team AS ENUM ('blue', 'red', 'other');
|
|
|
|
CREATE TYPE class_type AS ENUM ('scout', 'soldier', 'pyro', 'demoman', 'heavyweapons', 'engineer', 'medic', 'sniper', 'spy', 'unknown');
|
|
|
|
CREATE TYPE game_mode AS ENUM ('ultiduo', '4v4', '6v6', '7v7', '9v9', 'other');
|
|
|
|
CREATE TYPE map_type AS ENUM ('stopwatch', 'cp', 'koth', 'ctf', 'ultiduo', 'bball', 'other');
|
|
|
|
CREATE TYPE event_type AS ENUM ('charge', 'pointcap', 'medic_death', 'round_win');
|
|
|
|
CREATE TYPE medigun AS ENUM ('medigun', 'kritzkrieg', 'quickfix', 'vaccinator');
|
|
|
|
CREATE OR REPLACE FUNCTION clean_map_name(map TEXT)
|
|
RETURNS TEXT AS $$
|
|
SELECT regexp_replace(replace(map, 'workshop/', ''), '((_(a|b|beta|u|r|v|rc|final|comptf|ugc|f)?[0-9]*[a-z]?)?(_(a|b|beta|u|r|v|rc|final|comptf|ugc|f)?[0-9]*[a-z]?(_nb[0-9]*)?)|([0-9]+[a-z]?))(\.[a-z0-9]+)?$', '', 'g');
|
|
$$ LANGUAGE SQL IMMUTABLE;
|
|
|
|
CREATE TABLE logs (
|
|
id INTEGER PRIMARY KEY,
|
|
red_score INTEGER NOT NULL,
|
|
blue_score INTEGER NOT NULL,
|
|
length INTEGER NOT NULL,
|
|
game_mode game_mode NOT NULL,
|
|
map TEXT NOT NULL,
|
|
clean_map TEXT GENERATED ALWAYS AS (clean_map_name(map)) STORED,
|
|
type map_type NOT NULL,
|
|
date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
|
winner team GENERATED ALWAYS AS (CASE WHEN red_score > blue_score THEN 'red'::team WHEN blue_score > red_score THEN 'blue'::team ELSE 'other'::team END) STORED,
|
|
version SMALLINT NOT NULL,
|
|
is_valid BOOL GENERATED ALWAYS AS (
|
|
length > 60 AND length < 3600 AND clean_map_name(map) != '' AND game_mode != 'other'
|
|
) STORED
|
|
);
|
|
|
|
CREATE INDEX logs_map_idx
|
|
ON logs USING BTREE (map);
|
|
|
|
CREATE INDEX logs_clean_map_idx
|
|
ON logs USING BTREE (clean_map);
|
|
|
|
CREATE INDEX logs_mode_idx
|
|
ON logs USING BTREE (game_mode);
|
|
|
|
CREATE INDEX logs_winner_idx
|
|
ON logs USING BTREE (winner);
|
|
|
|
CREATE INDEX logs_date_idx
|
|
ON logs USING BTREE (date);
|
|
|
|
CREATE INDEX logs_version_idx
|
|
ON logs USING BTREE (version);
|
|
|
|
CREATE TABLE rounds (
|
|
id SERIAL PRIMARY KEY,
|
|
round INTEGER NOT NULL,
|
|
log_id INTEGER NOT NULL REFERENCES logs(id),
|
|
length INTEGER NOT NULL,
|
|
winner team NOT NULL,
|
|
first_cap team NOT NULL,
|
|
red_score INTEGER NOT NULL,
|
|
blue_score INTEGER NOT NULL,
|
|
red_kills INTEGER NOT NULL,
|
|
blue_kills INTEGER NOT NULL,
|
|
red_dmg INTEGER NOT NULL,
|
|
blue_dmg INTEGER NOT NULL,
|
|
red_ubers INTEGER NOT NULL,
|
|
blue_ubers INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX rounds_log_id_idx
|
|
ON rounds USING BTREE (log_id);
|
|
|
|
CREATE UNIQUE INDEX rounds_round_log_id_idx
|
|
ON rounds USING BTREE (round, log_id);
|
|
|
|
CREATE INDEX rounds_winner_idx
|
|
ON rounds USING BTREE (winner);
|
|
|
|
CREATE INDEX rounds_first_cap_idx
|
|
ON rounds USING BTREE (first_cap);
|
|
|
|
CREATE TABLE events_charge (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
|
medigun medigun NOT NULL,
|
|
time INTEGER NOT NULL,
|
|
team team NOT NULL,
|
|
steam_id BIGINT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX events_charge_round_id_idx
|
|
ON events_charge USING BTREE (round_id);
|
|
|
|
CREATE INDEX events_charge_steam_id_idx
|
|
ON events_charge USING BTREE (steam_id);
|
|
|
|
CREATE TABLE events_point_cap (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
|
time INTEGER NOT NULL,
|
|
team team NOT NULL,
|
|
point INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX events_point_cap_round_id_idx
|
|
ON events_point_cap USING BTREE (round_id);
|
|
|
|
CREATE TABLE events_medic_death (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
|
time INTEGER NOT NULL,
|
|
team team NOT NULL,
|
|
steam_id BIGINT NOT NULL,
|
|
killer BIGINT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX events_medic_death_round_id_idx
|
|
ON events_medic_death USING BTREE (round_id);
|
|
|
|
CREATE INDEX events_medic_death_steam_id_idx
|
|
ON events_medic_death USING BTREE (steam_id);
|
|
|
|
CREATE TABLE events_drop (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
|
time INTEGER NOT NULL,
|
|
team team NOT NULL,
|
|
steam_id BIGINT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX events_drop_round_id_idx
|
|
ON events_drop USING BTREE (round_id);
|
|
|
|
CREATE INDEX events_drop_steam_id_idx
|
|
ON events_drop USING BTREE (steam_id);
|
|
|
|
CREATE TABLE events_round_win (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
|
time INTEGER NOT NULL,
|
|
team team NOT NULL
|
|
);
|
|
|
|
CREATE UNIQUE INDEX events_round_win_round_id_idx
|
|
ON events_round_win USING BTREE (round_id);
|
|
|
|
CREATE OR REPLACE FUNCTION team_is_winner(log_id INTEGER, team team) RETURNS BOOL AS $$
|
|
DECLARE
|
|
is_winner BOOLEAN;
|
|
BEGIN
|
|
SELECT team = winner into is_winner FROM logs WHERE id = log_id;
|
|
RETURN is_winner;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION get_game_mode(log_id INTEGER) RETURNS game_mode AS $$
|
|
DECLARE
|
|
result game_mode;
|
|
BEGIN
|
|
SELECT game_mode into result FROM logs WHERE id = log_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION get_clean_map(log_id INTEGER) RETURNS TEXT AS $$
|
|
DECLARE
|
|
result TEXT;
|
|
BEGIN
|
|
SELECT clean_map into result FROM logs WHERE id = log_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION get_date(log_id INTEGER) RETURNS TIMESTAMP WITHOUT TIME ZONE AS $$
|
|
DECLARE
|
|
result TIMESTAMP WITHOUT TIME ZONE;
|
|
BEGIN
|
|
SELECT date into result FROM logs WHERE id = log_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION get_length(log_id INTEGER) RETURNS INTEGER AS $$
|
|
DECLARE
|
|
result INTEGER;
|
|
BEGIN
|
|
SELECT length into result FROM logs WHERE id = log_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE OR REPLACE FUNCTION is_log_valid(log_id INTEGER) RETURNS BOOL AS $$
|
|
DECLARE
|
|
result BOOL;
|
|
BEGIN
|
|
SELECT is_valid into result FROM logs WHERE id = log_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE TABLE players (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
log_id INTEGER NOT NULL REFERENCES logs(id),
|
|
steam_id BIGINT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
team team NOT NULL,
|
|
kills INTEGER NOT NULL,
|
|
deaths INTEGER NOT NULL,
|
|
assists INTEGER NOT NULL,
|
|
suicides INTEGER NOT NULL,
|
|
dmg INTEGER NOT NULL,
|
|
damage_taken INTEGER NOT NULL,
|
|
ubers INTEGER NOT NULL,
|
|
medigun_ubers INTEGER NOT NULL,
|
|
kritzkrieg_ubers INTEGER NOT NULL,
|
|
quickfix_ubers INTEGER NOT NULL,
|
|
vaccinator_ubers INTEGER NOT NULL,
|
|
drops INTEGER NOT NULL,
|
|
medkits INTEGER NOT NULL,
|
|
medkits_hp INTEGER NOT NULL,
|
|
backstabs INTEGER NOT NULL,
|
|
headshots INTEGER NOT NULL,
|
|
heal INTEGER NOT NULL,
|
|
heals_received INTEGER NOT NULL,
|
|
scout_kills INTEGER NOT NULL,
|
|
soldier_kills INTEGER NOT NULL,
|
|
pyro_kills INTEGER NOT NULL,
|
|
demoman_kills INTEGER NOT NULL,
|
|
heavy_kills INTEGER NOT NULL,
|
|
engineer_kills INTEGER NOT NULL,
|
|
medic_kills INTEGER NOT NULL,
|
|
sniper_kills INTEGER NOT NULL,
|
|
spy_kills INTEGER NOT NULL,
|
|
scout_deaths INTEGER NOT NULL,
|
|
soldier_deaths INTEGER NOT NULL,
|
|
pyro_deaths INTEGER NOT NULL,
|
|
demoman_deaths INTEGER NOT NULL,
|
|
heavy_deaths INTEGER NOT NULL,
|
|
engineer_deaths INTEGER NOT NULL,
|
|
medic_deaths INTEGER NOT NULL,
|
|
sniper_deaths INTEGER NOT NULL,
|
|
spy_deaths INTEGER NOT NULL,
|
|
is_winner BOOL GENERATED ALWAYS AS (team_is_winner(log_id, team)) STORED,
|
|
game_mode game_mode GENERATED ALWAYS AS (get_game_mode(log_id)) STORED,
|
|
clean_map TEXT GENERATED ALWAYS AS (get_clean_map(log_id)) STORED,
|
|
date TIMESTAMP WITHOUT TIME ZONE GENERATED ALWAYS AS (get_date(log_id)) STORED,
|
|
length INTEGER GENERATED ALWAYS AS (get_length(log_id)) STORED,
|
|
is_valid BOOL GENERATED ALWAYS AS (
|
|
is_log_valid(log_id) AND damage_taken < 100000 AND heals_received < 100000 AND
|
|
kills < 100 AND deaths < 100 AND get_game_mode(log_id) != 'other'
|
|
) STORED
|
|
);
|
|
|
|
CREATE INDEX players_log_id_idx
|
|
ON players USING BTREE (log_id);
|
|
|
|
CREATE UNIQUE INDEX players_log_steam_id_idx
|
|
ON players USING BTREE (log_id, steam_id);
|
|
|
|
CREATE INDEX players_steam_id_idx
|
|
ON players USING BTREE (steam_id);
|
|
|
|
CREATE INDEX players_team_idx
|
|
ON players USING BTREE (team);
|
|
|
|
CREATE INDEX players_is_winner_idx
|
|
ON players USING BTREE (is_winner);
|
|
|
|
CREATE INDEX players_game_mode_idx
|
|
ON players USING BTREE (game_mode);
|
|
|
|
CREATE INDEX players_clean_map_idx
|
|
ON players USING BTREE (clean_map);
|
|
|
|
CREATE INDEX players_date_idx
|
|
ON players USING BTREE (date);
|
|
|
|
CREATE INDEX players_year_idx
|
|
ON players USING BTREE (extract(year from date));
|
|
|
|
CREATE INDEX players_is_valid_idx
|
|
ON players USING BTREE (is_valid);
|
|
|
|
CREATE OR REPLACE FUNCTION is_player_valid(player_id BIGINT) RETURNS BOOL AS $$
|
|
DECLARE
|
|
result BOOL;
|
|
BEGIN
|
|
SELECT is_valid into result FROM players WHERE id = player_id;
|
|
RETURN result;
|
|
END; $$
|
|
LANGUAGE PLPGSQL IMMUTABLE;
|
|
|
|
CREATE TABLE class_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
player_id BIGINT NOT NULL REFERENCES players(id),
|
|
type class_type NOT NULL,
|
|
time INTEGER NOT NULL,
|
|
kills INTEGER NOT NULL,
|
|
deaths INTEGER NOT NULL,
|
|
assists INTEGER NOT NULL,
|
|
dmg INTEGER NOT NULL,
|
|
is_valid BOOL GENERATED ALWAYS AS (
|
|
is_player_valid(player_id) AND kills < 100 AND deaths < 100 AND dmg < 50000 AND type != 'unknown' AND time > 0
|
|
) STORED
|
|
);
|
|
|
|
CREATE INDEX class_stats_player_id_idx
|
|
ON class_stats USING BTREE (player_id);
|
|
|
|
CREATE UNIQUE INDEX class_stats_player_id_type_idx
|
|
ON class_stats USING BTREE (player_id, type);
|
|
|
|
CREATE INDEX class_stats_player_id_valid_idx
|
|
ON class_stats USING BTREE (player_id, is_valid);
|
|
|
|
CREATE TABLE player_weapon_stats (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
class_stat_id BIGINT NOT NULL REFERENCES class_stats(id),
|
|
weapon TEXT NOT NULL,
|
|
kills INTEGER NOT NULL,
|
|
shots INTEGER NOT NULL,
|
|
hits INTEGER NOT NULL,
|
|
dmg INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX player_weapon_stats_class_stat_id_idx
|
|
ON player_weapon_stats USING BTREE (class_stat_id);
|
|
|
|
CREATE UNIQUE INDEX player_weapon_stats_class_stat_id_weapon_idx
|
|
ON player_weapon_stats USING BTREE (class_stat_id, weapon);
|
|
|
|
CREATE MATERIALIZED VIEW player_stats AS
|
|
SELECT
|
|
game_mode, clean_map, extract(year from date)::INT as year,
|
|
extract(month from date)::INT as month,
|
|
class_stats.type as class,
|
|
sum(class_stats.dmg) as damage,
|
|
sum(class_stats.kills) as kills,
|
|
sum(class_stats.deaths) as deaths,
|
|
sum(class_stats.assists) as assists,
|
|
sum(class_stats.time) as time,
|
|
sum(players.heals_received * (length / time)) as heals_received,
|
|
sum(players.damage_taken * (length / time)) as damage_taken,
|
|
count(*) as count,
|
|
sum(is_winner::INTEGER) as wins,
|
|
steam_id
|
|
FROM players
|
|
INNER JOIN class_stats ON players.id = class_stats.player_id
|
|
WHERE class_stats.is_valid
|
|
GROUP BY game_mode, clean_map, extract(year from date)::INT, extract(month from date)::INT,
|
|
class_stats.type, steam_id;
|
|
|
|
CREATE INDEX player_stats_steam_id_idx
|
|
ON player_stats USING BTREE (steam_id);
|
|
|
|
CREATE INDEX player_stats_game_mode_idx
|
|
ON player_stats USING BTREE (game_mode);
|
|
|
|
CREATE INDEX player_stats_class_idx
|
|
ON player_stats USING BTREE (class);
|
|
|
|
CREATE INDEX player_stats_date_idx
|
|
ON player_stats USING BTREE (year, month);
|
|
|
|
CREATE UNIQUE INDEX player_stats_unique_idx
|
|
ON player_stats USING BTREE (game_mode, clean_map, year, month, steam_id, class);
|
|
|
|
CREATE MATERIALIZED VIEW player_names AS
|
|
SELECT
|
|
steam_id, name, sum(length) as TIME, count(*) AS count
|
|
FROM players
|
|
GROUP BY steam_id, name;
|
|
|
|
CREATE INDEX player_names_steam_id_idx
|
|
ON player_names USING BTREE (steam_id);
|
|
|
|
CREATE UNIQUE INDEX player_names_steam_id_name_idx
|
|
ON player_names USING BTREE (steam_id, name);
|
|
|
|
CREATE INDEX player_names_search_idx
|
|
ON player_names USING GIN (name gin_trgm_ops);
|
|
|
|
CREATE MATERIALIZED VIEW user_names AS
|
|
WITH names AS
|
|
(
|
|
select name, count, steam_id,
|
|
rank() over (partition by steam_id order by steam_id, count desc) rn
|
|
from player_names
|
|
)
|
|
SELECT steam_id, MAX(name) as name
|
|
FROM names
|
|
WHERE rn = 1
|
|
GROUP BY steam_id;
|
|
|
|
CREATE UNIQUE INDEX user_names_steam_id_idx
|
|
ON user_names USING BTREE (steam_id);
|
|
|
|
CREATE TABLE kill_streaks (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
log_id INTEGER NOT NULL REFERENCES logs(id),
|
|
steam_id BIGINT NOT NULL,
|
|
time INTEGER NOT NULL,
|
|
streak INTEGER NOT NULL,
|
|
game_mode game_mode GENERATED ALWAYS AS (get_game_mode(log_id)) STORED,
|
|
clean_map TEXT GENERATED ALWAYS AS (get_clean_map(log_id)) STORED,
|
|
is_valid BOOL GENERATED ALWAYS AS (
|
|
is_log_valid(log_id) AND streak < 20
|
|
) STORED
|
|
);
|
|
|
|
CREATE INDEX kill_streaks_id_idx
|
|
ON kill_streaks USING BTREE (id);
|
|
|
|
CREATE INDEX kill_streaks_steam_id_idx
|
|
ON kill_streaks USING BTREE (steam_id);
|
|
|
|
CREATE INDEX kill_streaks_log_id_idx
|
|
ON kill_streaks USING BTREE (log_id);
|
|
|
|
CREATE INDEX kill_streaks_steam_id_streak_idx
|
|
ON kill_streaks USING BTREE (steam_id, streak);
|
|
|
|
CREATE INDEX kill_streaks_steam_id_streak_valid_idx
|
|
ON kill_streaks USING BTREE (steam_id, is_valid, streak); |