1
0
mirror of synced 2024-12-18 09:15:54 +01:00

Speed up a few profile operations by writing better SQL with fewer DB hits.

This commit is contained in:
Jennifer Taylor 2021-09-07 17:57:00 +00:00
parent 7560e30498
commit 469df34bea

View File

@ -488,10 +488,10 @@ class UserData(BaseData):
A dictionary previously stored by a game class if found, or None otherwise. A dictionary previously stored by a game class if found, or None otherwise.
""" """
sql = ( sql = (
"SELECT refid.refid AS refid, extid.extid AS extid " + "SELECT refid.refid AS refid, extid.extid AS extid, profile.data AS data " +
"FROM refid, extid " + "FROM refid, extid, profile " +
"WHERE refid.userid = :userid AND refid.game = :game AND refid.version = :version AND " "WHERE refid.userid = :userid AND refid.game = :game AND refid.version = :version AND "
"extid.userid = refid.userid AND extid.game = refid.game" "extid.userid = refid.userid AND extid.game = refid.game AND profile.refid = refid.refid"
) )
cursor = self.execute(sql, {'userid': userid, 'game': game.value, 'version': version}) cursor = self.execute(sql, {'userid': userid, 'game': game.value, 'version': version})
if cursor.rowcount != 1: if cursor.rowcount != 1:
@ -499,23 +499,14 @@ class UserData(BaseData):
return None return None
result = cursor.fetchone() result = cursor.fetchone()
profile = Profile( return Profile(
game, game,
version, version,
result['refid'], result['refid'],
result['extid'], result['extid'],
self.deserialize(result['data']),
) )
sql = "SELECT data FROM profile WHERE refid = :refid"
cursor = self.execute(sql, {'refid': profile.refid})
if cursor.rowcount != 1:
# Profile doesn't exist
return None
result = cursor.fetchone()
profile.update(self.deserialize(result['data']))
return profile
def get_any_profile(self, game: GameConstants, version: int, userid: UserID) -> Optional[Profile]: def get_any_profile(self, game: GameConstants, version: int, userid: UserID) -> Optional[Profile]:
""" """
Given a game/version/userid, look up the associated profile. If the profile for that version Given a game/version/userid, look up the associated profile. If the profile for that version
@ -530,14 +521,13 @@ class UserData(BaseData):
Returns: Returns:
A dictionary previously stored by a game class if found, or None otherwise. A dictionary previously stored by a game class if found, or None otherwise.
""" """
played = self.get_games_played(userid) played = self.get_games_played(userid, game=game)
versions = [p[1] for p in played if p[0] == game] versions = {p[1] for p in played}
versions.sort(reverse=True)
if version in versions: if version in versions:
return self.get_profile(game, version, userid) return self.get_profile(game, version, userid)
elif len(versions) > 0: elif len(versions) > 0:
return self.get_profile(game, versions[0], userid) return self.get_profile(game, max(versions), userid)
else: else:
return None return None
@ -555,23 +545,56 @@ class UserData(BaseData):
A List of tuples containing a userid and a dictionary previously stored by a game class if found, A List of tuples containing a userid and a dictionary previously stored by a game class if found,
or None otherwise. or None otherwise.
""" """
return [ if not userids:
(userid, self.get_any_profile(game, version, userid)) return []
for userid in userids sql = "SELECT version, userid FROM refid WHERE game = :game AND userid IN :userids"
] cursor = self.execute(sql, {'game': game.value, 'userids': userids})
profilever: Dict[UserID, int] = {}
def get_games_played(self, userid: UserID) -> List[Tuple[GameConstants, int]]: for result in cursor.fetchall():
tuid = UserID(result['userid'])
tver = result['version']
if tuid not in profilever:
# Just assign it the first profile we find
profilever[tuid] = tver
else:
# If the profile for this version exists, prioritize it
if tver == version:
profilever[tuid] = tver
# Only update the profile version with the newest game profile if the game
# profile for this version doesn't exist.
elif profilever[tuid] != version:
profilever[tuid] = max(profilever[tuid], tver)
result = []
for uid in userids:
if uid not in profilever:
result.append((uid, None))
else:
result.append((uid, self.get_profile(game, profilever[uid], uid)))
return result
def get_games_played(self, userid: UserID, game: Optional[GameConstants] = None) -> List[Tuple[GameConstants, int]]:
""" """
Given a user ID, look up all game/version combos this user has played. Given a user ID, look up all game/version combos this user has played.
Parameters: Parameters:
userid - Integer user ID, as looked up by one of the above functions. userid - Integer user ID, as looked up by one of the above functions.
game - An optional game series to constrain search to.
Returns: Returns:
A List of Tuples of game, version for each game/version the user has played. A List of Tuples of game, version for each game/version the user has played.
""" """
sql = "SELECT game, version FROM refid WHERE userid = :userid" sql = "SELECT game, version FROM refid WHERE userid = :userid"
cursor = self.execute(sql, {'userid': userid}) vals: Dict[str, Any] = {'userid': userid}
if game is not None:
sql += " AND game = :game"
vals['game'] = game.value
cursor = self.execute(sql, vals)
profiles = [] profiles = []
for result in cursor.fetchall(): for result in cursor.fetchall():
profiles.append((GameConstants(result['game']), result['version'])) profiles.append((GameConstants(result['game']), result['version']))
@ -598,17 +621,16 @@ class UserData(BaseData):
profiles = [] profiles = []
for result in cursor.fetchall(): for result in cursor.fetchall():
profile = Profile( profiles.append(
(
UserID(result['userid']),
Profile(
game, game,
version, version,
result['refid'], result['refid'],
result['extid'], result['extid'],
self.deserialize(result['data']),
) )
profile.update(self.deserialize(result['data']))
profiles.append(
(
UserID(result['userid']),
profile,
) )
) )