From 8e18b3df634f66c1050145257f31d51a1a2b8d33 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:26:59 +0900 Subject: [PATCH 01/11] lazy load MGUMatch --- lua/wikis/commons/MatchGroup/Util.lua | 167 ++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 22 deletions(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 21a3e75b87d..3cfe052a05b 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -234,6 +234,7 @@ MatchGroupUtil.types.Status = TypeUtil.optional(TypeUtil.literalUnion('notplayed ---@field winner integer? ---@field status string? ---@field extradata table? +---@field package record match2game MatchGroupUtil.types.Game = TypeUtil.struct({ comment = 'string?', @@ -290,6 +291,7 @@ MatchGroupUtil.types.Game = TypeUtil.struct({ ---@field timestamp number ---@field timezoneId string? ---@field bestof number? +---@field package record match2 MatchGroupUtil.types.Match = TypeUtil.struct({ bracketData = MatchGroupUtil.types.BracketData, @@ -543,6 +545,124 @@ function MatchGroupUtil.fetchMatchForBracketDisplay(bracketId, matchId) return match, bracketResetMatch end +local MatchRecordMT = { + PROPERTIES = { + 'bestof', 'extradata', 'date', 'dateexact', 'finished', 'game', 'icon', 'icondark', 'links', 'liquipediatier', + 'liquipediatiertype', 'mode', 'pagename', 'parent', 'patch', 'publishertier', 'resulttype', 'section', + 'series', 'shortname', 'status', 'stream', 'tickername', 'tournament', 'type', 'vod', 'walkover', 'winner', + 'match2bracketdata' + } +} + +---@param record match2 +---@param property string +function MatchRecordMT.__index(record, property) + local matchId = rawget(record, 'match2id') + if property == 'match2games' then + record.match2games = MatchRecordMT._fetchGames(matchId) + elseif property == 'match2opponents' then + record.match2opponents = MatchRecordMT._fetchOpponents(matchId) + else + Table.mergeInto( + record, + mw.ext.LiquipediaDB.lpdb('match2', { + conditions = '[[match2id::' .. matchId .. ']]', + query = table.concat( + Array.filter(MatchRecordMT.PROPERTIES, function (recordProperty) + return rawget(record, recordProperty) == nil + end), + ',' + ), + limit = 1, + })[1] + ) + end + return rawget(record, property) +end + +---@param matchId string +---@return match2game[] +function MatchRecordMT._fetchGames(matchId) + local gameRecords = mw.ext.LiquipediaDB.lpdb('match2game', { + conditions = '[[match2id::' .. matchId .. ']]', + query = 'date, game, length, map, mode, opponents, patch, resulttype, status, scores,' .. + 'subgroup, type, vod, walkover, winner', + order = 'match2gameid asc', + limit = 1000, + }) + + Array.forEach(gameRecords, function (gameRecord, index) + setmetatable(gameRecord, { + __index = function (t, k) + if k == 'extradata' then + local queriedExtradata = mw.ext.LiquipediaDB.lpdb('match2game', { + conditions = '[[match2id::' .. matchId .. ']] AND [[match2gameid::' .. index .. ']]', + query = 'extradata', + limit = 1, + })[1].extradata + t.extradata = queriedExtradata + return queriedExtradata + end + end + }) + end) + return gameRecords +end + +---@param matchId string +---@return match2opponent[] +function MatchRecordMT._fetchOpponents(matchId) + local opponentRecords = mw.ext.LiquipediaDB.lpdb('match2opponent', { + conditions = '[[match2id::' .. matchId .. ']]', + query = 'match2opponentid, name, placement, score, status, template, type, extradata', + order = 'match2opponentid asc', + limit = 1000, + }) + local _, players = Array.groupBy( + mw.ext.LiquipediaDB.lpdb('match2player', { + conditions = '[[match2id::' .. matchId .. ']]', + query = 'match2opponentid, name, displayname, flag, extradata', + order = 'match2playerid asc', + groupby = 'match2opponentid asc', + limit = 1000, + }), + function (player) + return tonumber(player.match2opponentid) + end + ) + Array.forEach(opponentRecords, function (opponentRecord) + opponentRecord.match2players = players[tonumber(opponentRecord.match2opponentid)] + end) + return opponentRecords +end + +local MatchMT = {} + +---@param match MatchGroupUtilMatch +---@param property any +function MatchMT.__index(match, property) + if property == 'bracketData' then + local bracketData = MatchGroupUtil.bracketDataFromRecord(Json.parseIfString(match.record.match2bracketdata)) + if bracketData.type == 'bracket' then + bracketData.lowerEdges = bracketData.lowerEdges + or MatchGroupUtil.autoAssignLowerEdges(#bracketData.lowerMatchIds, #match.opponents) + end + match.bracketData = bracketData + elseif property == 'games' then + local gamesRecord = match.record.match2games or MatchMT._fetchGames(match.matchId) + match.games = Array.map(gamesRecord, function(game) + return MatchGroupUtil.gameFromRecord(game, #match.opponents) + end) + elseif property == 'links' then + match.links = Json.parseIfString(match.record.links) or {} + elseif property == 'phase' then + match.phase = MatchGroupUtil.computeMatchPhase(match) + elseif property == 'stream' then + match.stream = Json.parseIfString(match.record.stream) or {} + end + return rawget(match, property) +end + ---Converts a match record to a structurally typed table with the appropriate data types for field values. ---The match record is either a match created in the store bracket codepath (WikiSpecific.processMatch), ---or a record fetched from LPDB (MatchGroupUtil.fetchMatchRecords). @@ -553,30 +673,22 @@ end ---@param record match2 ---@return MatchGroupUtilMatch function MatchGroupUtil.matchFromRecord(record) + setmetatable(record, MatchRecordMT) local extradata = MatchGroupUtil.parseOrCopyExtradata(record.extradata) local opponents = Array.map(record.match2opponents, FnUtil.curry(MatchGroupUtil.opponentFromRecord, record)) - local games = Array.map(record.match2games, function(game) return MatchGroupUtil.gameFromRecord(game, #opponents) end) - local bracketData = MatchGroupUtil.bracketDataFromRecord(Json.parseIfString(record.match2bracketdata)) - if bracketData.type == 'bracket' then - bracketData.lowerEdges = bracketData.lowerEdges - or MatchGroupUtil.autoAssignLowerEdges(#bracketData.lowerMatchIds, #opponents) - end local walkover = nilIfEmpty(record.walkover) local match = { bestof = tonumber(record.bestof) or 0, - bracketData = bracketData, comment = nilIfEmpty(Table.extract(extradata, 'comment')), extradata = extradata, date = record.date, dateIsExact = Logic.readBool(record.dateexact), finished = Logic.readBool(record.finished), game = record.game, - games = games, icon = nilIfEmpty(record.icon), iconDark = nilIfEmpty(record.icondark), - links = Json.parseIfString(record.links) or {}, matchId = record.match2id, liquipediatier = record.liquipediatier, liquipediatiertype = record.liquipediatiertype, @@ -591,7 +703,6 @@ function MatchGroupUtil.matchFromRecord(record) series = nilIfEmpty(record.series), shortname = nilIfEmpty(record.shortname), status = nilIfEmpty(record.status), - stream = Json.parseIfString(record.stream) or {}, tickername = record.tickername, timestamp = tonumber(Table.extract(extradata, 'timestamp')), timezoneId = Table.extract(extradata, 'timezoneid'), @@ -600,11 +711,10 @@ function MatchGroupUtil.matchFromRecord(record) vod = nilIfEmpty(record.vod), walkover = walkover and walkover:lower() or nil, winner = tonumber(record.winner), + record = record, } - match.phase = MatchGroupUtil.computeMatchPhase(match) - - return match + return setmetatable(match, MatchMT) end ---@param data table? @@ -741,22 +851,34 @@ function MatchGroupUtil.playerFromRecord(record) } end +local GameMT = { + ---@param game MatchGroupUtilGame + ---@param property any + __index = function (game, property) + if property == 'extradata' then + game.extradata = MatchGroupUtil.parseOrCopyExtradata(game.record.extradata) + elseif property == 'comment' then + game.comment = Table.extract(game.extradata, 'comment') + elseif property == 'dateIsExact' then + game.dateIsExact = Logic.readBool(Table.extract(game.extradata, 'dateexact')) + elseif property == 'header' then + game.header = nilIfEmpty(Table.extract(game.extradata, 'header')) + elseif property == 'mapDisplayName' then + game.mapDisplayName = nilIfEmpty(Table.extract(game.extradata, 'displayname')) + end + return rawget(game, property) + end +} + ---@param record match2game ---@param opponentCount integer? ---@return MatchGroupUtilGame function MatchGroupUtil.gameFromRecord(record, opponentCount) - local extradata = MatchGroupUtil.parseOrCopyExtradata(record.extradata) - - return { - comment = nilIfEmpty(Table.extract(extradata, 'comment')), + return setmetatable({ date = record.date, - dateIsExact = nilIfEmpty(Table.extract(extradata, 'dateexact')), - extradata = extradata, game = record.game, - header = nilIfEmpty(Table.extract(extradata, 'header')), length = record.length, map = nilIfEmpty(record.map), - mapDisplayName = nilIfEmpty(Table.extract(extradata, 'displayname')), mode = nilIfEmpty(record.mode), opponents = record.opponents, patch = record.patch, @@ -768,7 +890,8 @@ function MatchGroupUtil.gameFromRecord(record, opponentCount) vod = nilIfEmpty(record.vod), walkover = nilIfEmpty(record.walkover) and record.walkover:lower() or nil, winner = tonumber(record.winner), - } + record = record, + }, GameMT) end ---@param data table From 48b431bcb555cde82a6e20768b7ce1f847b0f99f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:18:11 +0900 Subject: [PATCH 02/11] trigger lazy load --- lua/wikis/commons/Standings/Parse/Lpdb.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lua/wikis/commons/Standings/Parse/Lpdb.lua b/lua/wikis/commons/Standings/Parse/Lpdb.lua index 5706713cf5d..2ab3036aca8 100644 --- a/lua/wikis/commons/Standings/Parse/Lpdb.lua +++ b/lua/wikis/commons/Standings/Parse/Lpdb.lua @@ -58,6 +58,13 @@ function StandingsParseLpdb.importFromMatches(rounds, scoreMapper) 'match2', { conditions = tostring(conditions), + query = table.concat({ + 'match2opponents', 'match2id', 'bestof', 'extradata', 'date', 'dateexact', 'finished', 'game', + 'icon', 'icondark', 'liquipediatier', 'liquipediatiertype', 'mode', 'pagename', 'parent', + 'patch', 'publishertier', 'resulttype', 'section', 'series', 'shortname', 'status', + 'tickername', 'tournament', 'type', 'vod', 'walkover', 'winner' + }, ','), + order = 'date asc', }, function(match2) local roundNumbers = matchIdToRound[match2.match2id] From 95be42dad5b78c4135d1f5166619f095fba3086a Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:34:15 +0900 Subject: [PATCH 03/11] lazy load opponents too --- lua/wikis/commons/MatchGroup/Util.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 3cfe052a05b..6fcf51b2404 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -655,6 +655,10 @@ function MatchMT.__index(match, property) end) elseif property == 'links' then match.links = Json.parseIfString(match.record.links) or {} + elseif property == 'opponents' then + match.opponents = Array.map( + match.record.match2opponents, FnUtil.curry(MatchGroupUtil.opponentFromRecord, match.record) + ) elseif property == 'phase' then match.phase = MatchGroupUtil.computeMatchPhase(match) elseif property == 'stream' then @@ -675,7 +679,6 @@ end function MatchGroupUtil.matchFromRecord(record) setmetatable(record, MatchRecordMT) local extradata = MatchGroupUtil.parseOrCopyExtradata(record.extradata) - local opponents = Array.map(record.match2opponents, FnUtil.curry(MatchGroupUtil.opponentFromRecord, record)) local walkover = nilIfEmpty(record.walkover) @@ -693,7 +696,6 @@ function MatchGroupUtil.matchFromRecord(record) liquipediatier = record.liquipediatier, liquipediatiertype = record.liquipediatiertype, mode = record.mode, - opponents = opponents, pageName = record.pagename, parent = record.parent, patch = record.patch, From 9060da9bdeed9f0182107125d6f344f3fd43d48d Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:51:57 +0900 Subject: [PATCH 04/11] lint --- lua/wikis/commons/MatchGroup/Util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 6fcf51b2404..6b1a36700dc 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -234,7 +234,7 @@ MatchGroupUtil.types.Status = TypeUtil.optional(TypeUtil.literalUnion('notplayed ---@field winner integer? ---@field status string? ---@field extradata table? ----@field package record match2game +---@field package record match2game MatchGroupUtil.types.Game = TypeUtil.struct({ comment = 'string?', From 4508f5e47aec656f09941a79849715271a4fc024 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:58:16 +0900 Subject: [PATCH 05/11] lazy loading in match table --- lua/wikis/commons/MatchTable.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/MatchTable.lua b/lua/wikis/commons/MatchTable.lua index 8b6411e8bf9..30afcc42433 100644 --- a/lua/wikis/commons/MatchTable.lua +++ b/lua/wikis/commons/MatchTable.lua @@ -319,8 +319,8 @@ function MatchTable:query() Lpdb.executeMassQuery('match2', { conditions = tostring(self:buildConditions()), order = 'date desc', - query = 'match2id, match2opponents, match2games, date, dateexact, icon, icondark, liquipediatier, game, type,' - .. 'liquipediatiertype, tournament, pagename, parent, section, tickername, vod, winner, match2bracketdata,' + query = 'match2id, match2opponents, date, dateexact, icon, icondark, liquipediatier, game, type,' + .. 'liquipediatiertype, tournament, pagename, parent, section, tickername, vod, winner,' .. 'extradata, bestof, publishertier', limit = 50, }, function(match) From 4823145aec44fa5f6db14aaf4533a1b12e9610cc Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:58:29 +0900 Subject: [PATCH 06/11] fix incorrect call --- lua/wikis/commons/MatchGroup/Util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 6b1a36700dc..675277ccfc2 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -649,7 +649,7 @@ function MatchMT.__index(match, property) end match.bracketData = bracketData elseif property == 'games' then - local gamesRecord = match.record.match2games or MatchMT._fetchGames(match.matchId) + local gamesRecord = match.record.match2games or MatchRecordMT._fetchGames(match.matchId) match.games = Array.map(gamesRecord, function(game) return MatchGroupUtil.gameFromRecord(game, #match.opponents) end) From c7c6cb77fd58d6a32d1e678cb0d2742244fa9744 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:16:35 +0900 Subject: [PATCH 07/11] do not try lazy loading if there is no matchid --- lua/wikis/commons/MatchGroup/Util.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 675277ccfc2..95eb4f4eeee 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -558,6 +558,9 @@ local MatchRecordMT = { ---@param property string function MatchRecordMT.__index(record, property) local matchId = rawget(record, 'match2id') + if type(matchId) ~= 'string' then + return + end if property == 'match2games' then record.match2games = MatchRecordMT._fetchGames(matchId) elseif property == 'match2opponents' then From 5decb63ee2d88aeb8c8bcf00fd2583e24d6fbd9f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 8 Jun 2026 09:11:16 +0900 Subject: [PATCH 08/11] update LPDB mock --- lua/spec/match2_spec.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/spec/match2_spec.lua b/lua/spec/match2_spec.lua index 3dd7e0c0e14..aa8520241ab 100644 --- a/lua/spec/match2_spec.lua +++ b/lua/spec/match2_spec.lua @@ -34,8 +34,13 @@ describe('match2', function() dataSaved.match2games = dataSavedGame local ret = {dataSaved} - dataSaved, dataSavedGame, dataSavedOpponent = {}, {}, {} return ret + elseif tbl == 'match2opponent' then + return dataSavedOpponent + elseif tbl == 'match2player' then + return dataSavedPlayer + elseif tbl == 'match2game' then + return dataSavedGame end return {} end) From e2fd2e679e19833268240756533c4158982ccc41 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:07:13 +0900 Subject: [PATCH 09/11] avoid querying match2 table more than once --- lua/wikis/commons/MatchGroup/Util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 95eb4f4eeee..7108f947d33 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -565,7 +565,7 @@ function MatchRecordMT.__index(record, property) record.match2games = MatchRecordMT._fetchGames(matchId) elseif property == 'match2opponents' then record.match2opponents = MatchRecordMT._fetchOpponents(matchId) - else + elseif not record._propertiesLoaded then Table.mergeInto( record, mw.ext.LiquipediaDB.lpdb('match2', { @@ -579,6 +579,7 @@ function MatchRecordMT.__index(record, property) limit = 1, })[1] ) + record._propertiesLoaded = true end return rawget(record, property) end From d46fe40afeda072e80549aa2e11f7b8df125dfa5 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:09:07 +0900 Subject: [PATCH 10/11] rawget --- lua/wikis/commons/MatchGroup/Util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index 7108f947d33..bf8bbfa9d94 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -565,7 +565,7 @@ function MatchRecordMT.__index(record, property) record.match2games = MatchRecordMT._fetchGames(matchId) elseif property == 'match2opponents' then record.match2opponents = MatchRecordMT._fetchOpponents(matchId) - elseif not record._propertiesLoaded then + elseif not rawget(record, '_propertiesLoaded') then Table.mergeInto( record, mw.ext.LiquipediaDB.lpdb('match2', { From 1af09d7320618d7f6f67cce15a4a3bd2da92a59e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:47:41 +0900 Subject: [PATCH 11/11] optimize --- lua/wikis/commons/MatchGroup/Util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index bf8bbfa9d94..df30dc68983 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -649,13 +649,13 @@ function MatchMT.__index(match, property) local bracketData = MatchGroupUtil.bracketDataFromRecord(Json.parseIfString(match.record.match2bracketdata)) if bracketData.type == 'bracket' then bracketData.lowerEdges = bracketData.lowerEdges - or MatchGroupUtil.autoAssignLowerEdges(#bracketData.lowerMatchIds, #match.opponents) + or MatchGroupUtil.autoAssignLowerEdges(#bracketData.lowerMatchIds, #match.record.match2opponents) end match.bracketData = bracketData elseif property == 'games' then local gamesRecord = match.record.match2games or MatchRecordMT._fetchGames(match.matchId) match.games = Array.map(gamesRecord, function(game) - return MatchGroupUtil.gameFromRecord(game, #match.opponents) + return MatchGroupUtil.gameFromRecord(game, #match.record.match2opponents) end) elseif property == 'links' then match.links = Json.parseIfString(match.record.links) or {}