optimize sql function for v2

This commit is contained in:
2023-04-08 20:22:06 +02:00
parent 00336c592a
commit a9243b4ba7
3 changed files with 198 additions and 520 deletions
+196 -177
View File
@@ -1,15 +1,8 @@
import db from '../db/db' import db from '../db/db'
import { const { count, max, sum } = db.fn
processWeaponReport, import client from '../cache/redis'
populateWeaponSet, import { createWeaponJson } from './process/processWeapon'
processWeaponList import { createPlayerJson } from './process/processPlayer'
} from './process/processWeapon'
import {
populatePlayerSet,
processPlayerList,
processPlayerReport
} from './process/processPlayer'
import { getWeaponSet, getPlayerSet } from '../cache/cacheUtils'
/** /**
* Starts the process of populating the REDIS database globally * Starts the process of populating the REDIS database globally
@@ -18,33 +11,12 @@ import { getWeaponSet, getPlayerSet } from '../cache/cacheUtils'
async function processAll() { async function processAll() {
console.log('Starting data calculation...') console.log('Starting data calculation...')
const timeStart = new Date() const timeStart = new Date()
// Iterate through all servers
const servers = await db.selectFrom('server').selectAll().execute()
await populateAllSets()
const promises: Promise<any>[] = []
servers.forEach((server) => {
promises.push(processServer(server.id))
})
//Iterate through all weapons
const weapons = await getWeaponSet()
weapons.forEach((weapon) => {
promises.push(processWeapon(weapon))
})
//Iterate through all players
const players = await getPlayerSet()
players.forEach((player) => {
promises.push(processPlayer(player))
})
await Promise.all(promises)
//Generate lists now that we have all data
await processAllLists()
await Promise.all([processGlobalStats(), processServerStats()])
console.log( console.log(
'Data calculation finished. Took + ' + 'Data calculation finished. Took + ' +
Math.abs(new Date().getTime() - timeStart.getTime()) / 1000 + Math.abs(new Date().getTime() - timeStart.getTime()) / 1000 +
' seconds' ' seconds'
) )
if (process.env.ENVIRONMENT == 'production') { if (process.env.ENVIRONMENT == 'production') {
return return
@@ -52,157 +24,204 @@ async function processAll() {
setTimeout(processAll, 3600000) setTimeout(processAll, 3600000)
} }
/** async function processGlobalStats() {
* Populte redis of server lists and reports let promises: Promise<any>[] = []
* @param server //Global kills
*/ promises.push(db.selectFrom('kill')
async function processServer(server: number) { .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance')])
/*const promises: Promise<any>[] = [] .execute().then(data => {
//TODO TO REMOVE OR RENAME let transaction = client.multi()
await processServerPlayers(server) data.forEach(({ kills, max_distance, total_distance }) => {
transaction = processGlobalData({ kills, max_distance, total_distance }, transaction)
//Iterate through all weapons present on this server
await populateWeaponSet(server)
const weapons = await cache.SMEMBERS(`servers:${server}:weapons`)
weapons.forEach((weapon) => {
promises.push(processWeaponReport(weapon, server))
})
//Iterate through all players present on this server (and do more stuff on those players)
const players = await cache.SMEMBERS(`servers:${server}:players`)
players.forEach((player) => {
promises.push(processPlayer({ server, player }))
})
await Promise.all(promises)
await processWeaponList(server)*/
}
/**
* Populate redis of player lists and reports
* @param param0
*/
async function processPlayer(player: string) {
const promises: Promise<any>[] = []
//iterate through all servers
const servers = await db.selectFrom('server').selectAll().execute()
servers.forEach((server) => {
promises.push(
processPlayerReport(player, server.id),
//Iterate through all players on servers
getWeaponSet(server.id, player).then((weapons) => {
weapons.forEach((weapon) => {
promises.push(processPlayerReport(player, server.id, weapon))
})
}) })
) return transaction.exec()
}) }))
//iterate through all weapons outside of servers
const weapons = await getWeaponSet(undefined, player)
weapons.forEach((weapon) => {
promises.push(processPlayerReport(player, undefined, weapon))
})
//global weapon data
promises.push(processPlayerReport(player))
await Promise.all(promises)
}
async function processWeapon(weapon: string) { //Global weapon kill
const promises: Promise<any>[] = [] promises.push(db.selectFrom('kill')
//iterate through all servers .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death'])
const servers = await db.selectFrom('server').selectAll().execute() .groupBy('cause_of_death').execute().then(async (data) => {
servers.forEach((server) => { let transaction = client.multi()
promises.push( data.forEach(({ kills, max_distance, total_distance, cause_of_death }) => {
processWeaponReport(weapon, server.id), transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death }, transaction)
//Iterate through all players on servers
getPlayerSet(server.id, weapon).then((players) => {
players.forEach((player) => {
promises.push(processWeaponReport(weapon, server.id, player))
})
}) })
) await transaction.exec()
}) }))
//iterate through all players outside of servers
const players = await getPlayerSet(undefined, weapon)
players.forEach((player) => {
promises.push(processWeaponReport(weapon, undefined, player))
})
//global weapon data
promises.push(processWeaponReport(weapon))
await Promise.all(promises)
}
async function populateAllSets() { //Player weapon kills
const servers = await db.selectFrom('server').selectAll().execute() promises.push(db.selectFrom('kill')
await Promise.all([populatePlayerSet(), populateWeaponSet()]) .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'attacker_id'])
await (async () => { .groupBy(['cause_of_death', 'attacker_id']).execute().then(async (data) => {
const promises: Promise<any>[] = [] let transaction = client.multi()
await getWeaponSet().then((weaponSet) => { data.forEach(({ kills, max_distance, total_distance, cause_of_death, attacker_id }) => {
weaponSet.forEach((weapon: string) => { transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id }, transaction)
promises.push(populatePlayerSet(undefined, weapon))
}) })
}) await transaction.exec()
}))
await getPlayerSet().then((playerSet) => { //Player weapon deaths
playerSet.forEach((player) => { promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'cause_of_death'])
promises.push(populateWeaponSet(undefined, player)) .groupBy(['victim_id', 'cause_of_death']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ deaths, victim_id, cause_of_death }) => {
transaction = transaction.HSET(`weapons:${cause_of_death}:players:${victim_id}`, "deaths", deaths.toString())
}) })
}) return transaction.exec()
return Promise.all(promises) }))
})()
await (async () => { //Global player kills
const promises: Promise<any>[] = servers.map((server) => promises.push(db.selectFrom('kill')
(async () => { .select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'attacker_id'])
await Promise.all([ .groupBy('attacker_id').execute().then(data => {
populatePlayerSet(server.id), let transaction = client.multi()
populateWeaponSet(server.id) data.forEach(({ kills, max_distance, total_distance, attacker_id }) => {
]) processPlayerData({ kills, max_distance, total_distance, attacker_id }, transaction)
const promises: Promise<any>[] = [] })
await getWeaponSet(server.id).then((weaponSet) => { return transaction.exec()
weaponSet.forEach((weapon: string) => {
promises.push(populatePlayerSet(server.id, weapon))
})
})
await getPlayerSet(server.id).then((playerSet) => {
playerSet.forEach((player) => {
promises.push(populateWeaponSet(server.id, player))
})
})
return Promise.all(promises)
})()
)
await Promise.all(promises)
})()
}
async function processAllLists() {
const promises: Promise<any>[] = []
promises.push(processPlayerList(), processWeaponList())
promises.push(
...(await getPlayerSet()).map((player) =>
processWeaponList(undefined, player)
),
...(await (
await getWeaponSet()
).map((weapon) => processPlayerList(undefined, weapon)))
)
const servers = await db.selectFrom('server').selectAll().execute()
promises.push(
...servers.map(async ({ id }) => {
const promises: Promise<any>[] = []
promises.push(
processWeaponList(id),
processPlayerList(id),
...(await getPlayerSet()).map((player) =>
processWeaponList(id, player)
),
...(await (
await getWeaponSet()
).map((weapon) => processPlayerList(id, weapon)))
)
await Promise.all(promises)
}) })
) )
//Global player deaths
promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id'])
.groupBy('victim_id').execute().then(data => {
let transaction = client.multi()
data.forEach(({ deaths, victim_id }) => {
transaction = transaction.HSET('players:' + victim_id, "deaths", deaths.toString())
})
return transaction.exec()
}))
await Promise.all(promises)
promises = [];
promises.push(createWeaponJson());
promises.push(createPlayerJson());
(await client.sMembers('players')).forEach((player: string) => {
promises.push(createWeaponJson(undefined, player))
});
(await client.sMembers('weapons')).forEach((weapon: string) => {
promises.push(createPlayerJson(undefined, weapon))
});
await Promise.all(promises) await Promise.all(promises)
} }
async function processServerStats() {
let promises: Promise<any>[] = []
//Server global kills
promises.push(db.selectFrom('kill')
.select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'server'])
.groupBy(['server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ kills, max_distance, total_distance, server }) => {
transaction = processGlobalData({ kills, max_distance, total_distance, server }, transaction)
})
return transaction.exec()
}))
//Server weapon kills
promises.push(db.selectFrom('kill')
.select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'server'])
.groupBy(['cause_of_death', 'server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ kills, max_distance, total_distance, cause_of_death, server }) => {
transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, server }, transaction)
})
return transaction.exec()
}))
//Server Player weapon kills
promises.push(db.selectFrom('kill')
.select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'cause_of_death', 'attacker_id', 'server'])
.groupBy(['cause_of_death', 'attacker_id', 'server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }) => {
transaction = processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }, transaction)
})
return transaction.exec()
}))
//Server Player weapon deaths
promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'cause_of_death', 'server'])
.groupBy(['victim_id', 'cause_of_death', 'server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ deaths, victim_id, cause_of_death, server }) => {
transaction = transaction.HSET(`servers:${server}:weapons:${cause_of_death}:players:${victim_id}`, "deaths", deaths.toString())
})
return transaction.exec()
}))
//server player kills
promises.push(db.selectFrom('kill')
.select([count('id').as('kills'), max('distance').as('max_distance'), sum('distance').as('total_distance'), 'attacker_id', 'server'])
.groupBy(['attacker_id', 'server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ kills, max_distance, total_distance, attacker_id, server }) => {
transaction = processPlayerData({ kills, max_distance, total_distance, attacker_id, server }, transaction)
})
return transaction.exec()
})
)
//server player deaths
promises.push(db.selectFrom('kill').select([count('id').as('deaths'), 'victim_id', 'server'])
.groupBy(['victim_id', 'server']).execute().then(data => {
let transaction = client.multi()
data.forEach(({ deaths, victim_id, server }) => {
transaction = transaction.HSET('servers:' + server + ':players:' + victim_id, "deaths", deaths.toString())
})
return transaction.exec()
}))
await Promise.all(promises)
promises = [];
((await client.sMembers('servers')).forEach((server: string) => {
if (isNaN(Number(server))) return
promises.push(createWeaponJson(Number(server)));
promises.push(createPlayerJson(Number(server)));
promises.push(client.sMembers('servers:' + server + ':players').then(data => {
data.forEach((player: string) => {
promises.push(createWeaponJson(Number(server), player))
})
}))
promises.push(client.sMembers('servers:' + server + 'weapons').then(data => {
data.forEach((weapon: string) => {
promises.push(createPlayerJson(Number(server), weapon))
})
}))
}));
await Promise.all(promises)
}
function processGlobalData({ kills, max_distance, total_distance, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, server?: number }, transaction: any) {
const serverPrefix = server ? `servers:${server}:` : ''
const cacheLocation = serverPrefix + 'global'
transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString())
transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString())
transaction = transaction.HSET(cacheLocation, "kills", kills.toString())
if (server) {
transaction = transaction.SADD('servers', server.toString())
}
return transaction
}
function processWeaponData({ kills, max_distance, total_distance, cause_of_death, attacker_id, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, cause_of_death: string, attacker_id?: string, server?: number }, transaction: any) {
const serverPrefix = server ? `servers:${server}:` : ''
const playerPrefix = attacker_id ? `players:${attacker_id}:` : ''
const cacheLocation = serverPrefix + playerPrefix + `weapons:` + cause_of_death
transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString())
transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString())
transaction = transaction.HSET(cacheLocation, "kills", kills.toString())
transaction = transaction.SADD(serverPrefix + playerPrefix + `weapons`, cause_of_death.toString())
return transaction
}
function processPlayerData({ kills, max_distance, total_distance, attacker_id, cause_of_death, server }: { kills: string | number | bigint, max_distance: number, total_distance: string | number | bigint, attacker_id: string, server?: number, cause_of_death?: string }, transaction: any) {
const serverPrefix = server ? `servers:${server}:` : ''
const weaponPrefix = cause_of_death ? `weapons:${cause_of_death}:` : ''
const cacheLocation = serverPrefix + weaponPrefix + `players:` + attacker_id
transaction = transaction.HSET(cacheLocation, "total_distance", total_distance.toString())
transaction = transaction.HSET(cacheLocation, "max_distance", max_distance.toString())
transaction = transaction.HSET(cacheLocation, "kills", kills.toString())
transaction = transaction.SADD(serverPrefix + weaponPrefix + `players`, attacker_id.toString())
return transaction
}
export default processAll export default processAll
+1 -201
View File
@@ -1,208 +1,8 @@
/** /**
* This file contains all functions related to population of the REDIS database for players. * This file contains all functions related to population of the REDIS database for players.
*/ */
import db from '../../db/db'
import cache from '../../cache/redis' import cache from '../../cache/redis'
import { getPlayerReport, getPlayerSet } from '../../cache/cacheUtils' import { getPlayerReport, getPlayerSet } from '../../cache/cacheUtils'
import { sql } from 'kysely'
const { count, max, avg } = db.fn
/**
* Populates the set of players (just a list with player IDS)
*
* Inputs `POSTGRESQL`
*
* Outputs `SET` `[servers:{serverId}:][weapons:{weaponId}:]players`
* @param server optional server filter
* @param weapon optional weapon filter
* @returns
*/
export async function populatePlayerSet(server?: number, weapon?: string) {
const serverPrefix = !isNaN(Number(server?.toString()))
? `servers:${server}:`
: ''
const weaponPrefix = weapon ? `weapons:${weapon}:` : ''
const cacheLocation = serverPrefix + weaponPrefix + `players`
const last_entry = Number(await cache.GET(cacheLocation + ':last_entry')) || 0
//processKills
let query = db
.selectFrom('kill')
.select([
'attacker_id',
db
.selectFrom('kill')
.select(max('kill.id').as('last_entry'))
.as('last_entry')
])
.groupBy('attacker_id')
.where('kill.id', '>', last_entry)
if (server) {
query = query.where('server', '=', server)
}
if (weapon) {
query = query.where('cause_of_death', '=', weapon)
}
let newData = await query.execute()
if (newData.length == 0) {
return
}
const promises: Promise<any>[] = []
newData.forEach(({ attacker_id }) => {
if (attacker_id == 'last_entry' || attacker_id == 'processedList') {
console.error('attacker_id cannot be ' + attacker_id)
return
}
promises.push(cache.SADD(cacheLocation, attacker_id))
})
//processDeaths
let query2 = db
.selectFrom('kill')
.select([
sql<string>`DISTINCT(victim_id)`.as('victim_id'),
db
.selectFrom('kill')
.select(max('kill.id').as('last_entry'))
.as('last_entry')
])
.where('kill.id', '>', last_entry)
if (server) {
query2 = query2.where('server', '=', server)
}
if (weapon) {
query2 = query2.where('cause_of_death', '=', weapon)
}
const newData2 = await query2.execute()
newData2.forEach(({ victim_id }) => {
if (victim_id == 'last_entry' || victim_id == 'processedList') {
console.error('victim_id cannot be ' + victim_id)
return
}
promises.push(cache.SADD(cacheLocation, victim_id))
})
newData = await query.execute()
promises.push(
cache.SET(cacheLocation + ':last_entry', newData2[0].last_entry)
)
await Promise.all(promises)
}
/**
* Processes player kills with optional server and/or weapon filter.
* Does not processes deaths from weapons.
*
* Inputs `POSTGRESQL`
*
* Outputs `HASH` `[servers:{serverId}:][weapon:{weaponId}:]players:{playerId}`
* @param player weapon to generate the report for
* @param server optional server filter
* @param weapon optional player filter
*/
export async function processPlayerReport(
player: string,
server?: number,
weapon?: string
) {
if (player == 'last_entry' || player == 'processedList') {
console.error('Player cannot be ' + player)
return
}
const serverPrefix = server ? `servers:${server}:` : ''
const weaponPrefix = weapon ? `weapons:${weapon}:` : ''
const cacheLocation = serverPrefix + weaponPrefix + `players:` + player
const last_entry = Number(await cache.HGET(cacheLocation, 'last_entry')) || 0
let query = db
.selectFrom('kill')
.select([
sql<string>`percentile_disc(0) WITHIN GROUP (ORDER BY attacker_name) FILTER (where attacker_id = ${player})`.as(
'username'
),
count<number>('id')
.filterWhere('attacker_id', '=', player)
.filterWhereRef('attacker_id', '!=', 'victim_id')
.as('kills'),
count<number>('id').filterWhere('victim_id', '=', player).as('deaths'),
avg<number>('kill.distance')
.filterWhere('attacker_id', '=', player)
.as('avg_kill_distance'),
max('kill.distance')
.filterWhere('attacker_id', '=', player)
.as('max_kill_distance'),
db
.selectFrom('kill')
.select(max('kill.id').as('last_entry'))
.as('last_entry')
])
.where((qb) =>
qb.where('attacker_id', '=', player).orWhere('victim_id', '=', player)
)
.where('kill.id', '>', last_entry)
if (server) {
query = query.where('server', '=', server)
}
if (weapon) {
query = query.where('cause_of_death', '=', weapon)
}
const newData = await query.execute()
if (newData.length == 0) {
return
}
const promises: Promise<any>[] = []
newData.forEach(
({ kills, avg_kill_distance, max_kill_distance, deaths, username }) => {
promises.push(
(async () => {
if (!username) {
await cache.HSET(
cacheLocation,
'username',
(await cache.HGET(`players:` + player, 'username')) || ''
)
} else {
await cache.HSET(cacheLocation, 'username', username)
}
})()
)
promises.push(
processAvg(cacheLocation, 'avg_kill_distance', {
newkills: kills || 0,
newavg: avg_kill_distance || 0
}).then((e) => cache.HINCRBY(cacheLocation, 'kills', kills || 0)),
cache.HINCRBY(cacheLocation, 'deaths', deaths || 0),
processMax(cacheLocation, 'max_kill_distance', max_kill_distance)
)
}
)
promises.push(cache.HSET(cacheLocation, 'last_entry', newData[0].last_entry))
await Promise.all(promises)
}
async function processAvg(
cacheLocation: string,
key: 'avg_kill_distance',
{ newkills, newavg }: { newkills: number; newavg: number }
) {
const oldKills = Number(await cache.HGET(cacheLocation, 'kills')) || 0
const oldaverage = Number(await cache.HGET(cacheLocation, key)) || 0
const totalkills = Number(oldKills) + Number(newkills)
const newAverage =
(oldKills / totalkills) * oldaverage + (newkills / totalkills) * newavg || 0
await cache.HSET(cacheLocation, key, newAverage)
}
async function processMax(
cacheLocation: string,
key: 'max_kill_distance',
newmax: number
) {
const oldMax = Number(await cache.HGET(cacheLocation, key)) || 0
if (oldMax < newmax || oldMax == 0)
await cache.HSET(cacheLocation, key, newmax || 0)
}
/** /**
* Combines player reports in a readable json string. * Combines player reports in a readable json string.
@@ -216,7 +16,7 @@ async function processMax(
* @param weapon optional player filter * @param weapon optional player filter
* @returns * @returns
*/ */
export async function processPlayerList(server?: number, weapon?: string) { export async function createPlayerJson(server?: number, weapon?: string) {
const serverPrefix = server ? `servers:${server}:` : '' const serverPrefix = server ? `servers:${server}:` : ''
const weaponPrefix = weapon ? `weapons:${weapon}:` : '' const weaponPrefix = weapon ? `weapons:${weapon}:` : ''
const cacheLocation = serverPrefix + weaponPrefix + `players` const cacheLocation = serverPrefix + weaponPrefix + `players`
+1 -142
View File
@@ -1,149 +1,8 @@
/** /**
* This file contains all functions related to population of the REDIS database for weapons. * This file contains all functions related to population of the REDIS database for weapons.
*/ */
import db from '../../db/db'
import cache from '../../cache/redis' import cache from '../../cache/redis'
import { getWeaponReport } from '../../cache/cacheUtils' import { getWeaponReport } from '../../cache/cacheUtils'
const { count, max, avg } = db.fn
/**
* Populates the set of weapons (just a list with weapon names)
*
* Inputs `POSTGRESQL`
*
* Outputs `SET` `[servers:{serverId}:][players:{playerId}:]weapons`
* @param server optional server filter
* @param player optional player filter
* @returns
*/
export async function populateWeaponSet(server?: number, player?: string) {
const serverPrefix = server ? `servers:${server}:` : ''
const playerPrefix = player ? `players:${player}:` : ''
const cacheLocation = serverPrefix + playerPrefix + `weapons`
const last_entry = Number(await cache.GET(cacheLocation + ':last_entry')) || 0
let query = db
.selectFrom('kill')
.select([
'cause_of_death',
db
.selectFrom('kill')
.select(max('kill.id').as('last_entry'))
.as('last_entry')
])
.groupBy('cause_of_death')
.where('kill.id', '>', last_entry)
if (server) {
query = query.where('server', '=', server)
}
if (player) {
query = query
.where('attacker_id', '=', player)
.orWhere('victim_id', '=', player)
}
const newData = await query.execute()
if (newData.length == 0) {
return
}
const promises: Promise<any>[] = []
newData.forEach(({ cause_of_death }) => {
if (cause_of_death == 'last_entry' || cause_of_death == 'processedList') {
console.error('cause_of_death cannot be ' + cause_of_death)
return
}
promises.push(cache.SADD(cacheLocation, cause_of_death))
})
promises.push(cache.SET(cacheLocation + ':last_entry', newData[0].last_entry))
await Promise.all(promises)
}
/**
* Processes weapon kills with optional server and/or player filter.
* Does not processes deaths from weapons.
*
* Inputs `POSTGRESQL`
*
* Outputs `HASH` `[servers:{serverId}:][players:{playerId}:]weapons:{weaponId}`
* @param weapon weapon to generate the report for
* @param server optional server filter
* @param player optional player filter
*/
export async function processWeaponReport(
weapon: string,
server?: number,
player?: string
) {
if (weapon == 'last_entry' || weapon == 'processedList') {
console.error('Weapon cannot be ' + weapon)
return
}
const serverPrefix = server ? `servers:${server}:` : ''
const playerPrefix = player ? `players:${player}:` : ''
const cacheLocation = serverPrefix + playerPrefix + `weapons:` + weapon
const last_entry = Number(await cache.HGET(cacheLocation, 'last_entry')) || 0
let query = db
.selectFrom('kill')
.select([
count<number>('id').as('kills'),
avg<number>('kill.distance').as('avg_kill_distance'),
max('kill.distance').as('max_kill_distance'),
db
.selectFrom('kill')
.select(max('kill.id').as('last_entry'))
.as('last_entry')
])
.where('cause_of_death', '=', weapon)
.whereRef('attacker_id', '!=', 'victim_id')
.where('kill.id', '>', last_entry)
.groupBy('cause_of_death')
if (server) {
query = query.where('server', '=', server)
}
if (player) {
query = query.where('attacker_id', '=', player)
}
const newData = await query.execute()
if (newData.length == 0) {
return
}
const promises: Promise<any>[] = []
newData.forEach(({ kills, avg_kill_distance, max_kill_distance }) => {
promises.push(
processAvg(cacheLocation, 'avg_kill_distance', {
newkills: kills,
newavg: avg_kill_distance
}).then((e) => cache.HINCRBY(cacheLocation, 'kills', kills)),
processMax(cacheLocation, 'max_kill_distance', max_kill_distance)
)
})
promises.push(cache.HSET(cacheLocation, 'last_entry', newData[0].last_entry))
await Promise.all(promises)
}
async function processAvg(
cacheLocation: string,
key: 'avg_kill_distance',
{ newkills, newavg }: { newkills: number; newavg: number }
) {
const oldKills = Number(await cache.HGET(cacheLocation, 'kills')) || 0
const oldaverage = Number(await cache.HGET(cacheLocation, key)) | 0
const totalkills = oldKills + newkills
const newAverage =
(oldKills / totalkills) * oldaverage + (newkills / totalkills) * newavg
await cache.HSET(cacheLocation, key, newAverage)
}
async function processMax(
cacheLocation: string,
key: 'max_kill_distance',
newmax: number
) {
const oldMax = Number(await cache.HGET(cacheLocation, key)) || -1
newmax = newmax || 0
if (!oldMax || !newmax) console.log(oldMax, newmax)
if (oldMax < newmax) await cache.HSET(cacheLocation, key, newmax)
}
/** /**
* Combines weapons reports in a readable json string. * Combines weapons reports in a readable json string.
@@ -157,7 +16,7 @@ async function processMax(
* @param player optional player filter * @param player optional player filter
* @returns * @returns
*/ */
export async function processWeaponList(server?: number, player?: string) { export async function createWeaponJson(server?: number, player?: string) {
const serverPrefix = server ? `servers:${server}:` : '' const serverPrefix = server ? `servers:${server}:` : ''
const playerPrefix = player ? `players:${player}:` : '' const playerPrefix = player ? `players:${player}:` : ''
const cacheLocation = serverPrefix + playerPrefix + `weapons` const cacheLocation = serverPrefix + playerPrefix + `weapons`