@@ -66,11 +66,9 @@ def __init__(self, bot):
6666 # Ensure indices exist
6767 self .db .create_index ([("deku_id" , pymongo .ASCENDING )], unique = True )
6868
69- # Generate the pipeline
70- self .pipeline = [
71- {'$project' : {'deku_id' : 1 , 'name' : 1 , 'release_date' : 1 }}, # Filter to only stuff we want
72- ]
73- self .aggregatePipeline = list (self .db .aggregate (self .pipeline ))
69+ self .gameNamesCache = None
70+ self .topGames = None
71+ self .recalculate_cache ()
7472
7573 if AUTO_SYNC :
7674 self .sync_db .start ()
@@ -131,27 +129,27 @@ async def sync_db(self) -> Tuple[int, str]:
131129 logging .info (f'[Games] Finished syncing { count } games' )
132130
133131 self .last_sync = {'at' : sync_time , 'running' : False }
134- self .aggregatePipeline = list (self .db .aggregate (self .pipeline ))
135-
132+ self .recalculate_cache ()
136133 return count
137134
138- def search (self , query : str ) -> Optional [dict ]:
139- match = {'deku_id' : None , 'score' : 0 , 'name' : None }
140- for game in self .aggregatePipeline :
141- # If the game we are comparing is really short (<=3 chars), do not allow a match if our search is longer.
142- # This prevents things like 'a' being the best match for 'realMyst' and not 'realMyst: Masterpiece Edition'
143- if len (game ['name' ]) <= 5 and len (query ) > 5 :
144- continue
135+ def recalculate_cache (self ):
136+ self .gameNamesCache = list (self .db .aggregate ([{'$project' : {'deku_id' : 1 , 'name' : 1 }}]))
145137
146- score = rapidfuzz .fuzz .WRatio (query .lower (), game ['name' ], processor = rapidfuzz .utils .default_process )
138+ # Cache the 10 most popular games
139+ users = mclient .bowser .users .find ({"favgames" : {'$exists' : True , '$not' : {'$size' : 0 }}})
140+ games = {}
141+ for user in users :
142+ for game_id in user ['favgames' ]:
143+ if game_id not in games :
144+ games [game_id ] = 1
147145
148- if not match [ 'score' ] or ( score > match [ 'score' ]) :
149- match = { 'deku_id' : game [ 'deku_id' ], 'score' : score , 'name' : game [ 'name' ]}
146+ else :
147+ games [ game_id ] += 1
150148
151- if match [ 'score' ] < SEARCH_RATIO_THRESHOLD :
152- return None
149+ top_10 = dict ( sorted ( games . items (), key = lambda kv : kv [ 1 ], reverse = True )[ 0 : 10 ])
150+ games = self . db . find ({ "deku_id" : { "$in" : list ( top_10 . keys ())}}, projection = { 'deku_id' : 1 , 'name' : 1 })
153151
154- return match
152+ self . topGames = list ( games )
155153
156154 async def get_image (self , deku_id : str , as_url : bool = False ):
157155 game = self .db .find_one ({'deku_id' : deku_id }, projection = {'image' : 1 })
@@ -174,16 +172,39 @@ def get_name(self, deku_id: str):
174172 document = self .db .find_one ({'deku_id' : deku_id }, projection = {'name' : 1 })
175173 return document ['name' ] if document else None
176174
175+ def search (self , query : str , multiResult = False ) -> Optional [dict ]:
176+ gameList = self .gameNamesCache
177+
178+ # If the game we are comparing is really short (<=3 chars), do not allow a match if our search is longer.
179+ # This prevents things like 'a' being the best match for 'realMyst' and not 'realMyst: Masterpiece Edition'
180+ if len (query ) > 5 :
181+ gameList = [g for g in gameList if len (g ['name' ]) > 5 ]
182+
183+ results = rapidfuzz .process .extract (
184+ query ,
185+ [g ['name' ] for g in gameList ],
186+ scorer = rapidfuzz .fuzz .WRatio ,
187+ limit = 10 if multiResult else 1 ,
188+ processor = rapidfuzz .utils .default_process ,
189+ score_cutoff = SEARCH_RATIO_THRESHOLD ,
190+ )
191+
192+ if not results :
193+ return None
194+
195+ ret = [{'deku_id' : gameList [i ]['deku_id' ], 'score' : score , 'name' : name } for name , score , i in results ]
196+ return ret if multiResult else ret [0 ]
197+
177198 async def _games_search_autocomplete (self , interaction : discord .Interaction , current : str ):
178199 if current :
179- game = self .search (current )
200+ games = self .search (current , True )
180201
181202 else :
182203 # Current textbox is empty
183- return []
204+ return [app_commands . Choice ( name = game [ 'name' ], value = game [ 'deku_id' ]) for game in self . topGames ]
184205
185- if game :
186- return [app_commands .Choice (name = game ['name' ], value = game ['deku_id' ])]
206+ if games :
207+ return [app_commands .Choice (name = game ['name' ], value = game ['deku_id' ]) for game in games ]
187208 else :
188209 return []
189210
0 commit comments