changeset 6031:1b2e1497f5ed

replace slow get_by_filter() calls by faster _getUserIdByKey calls get_by_filter is O(N) with N being the amount of users. it reads all user profiles from disk, so it is very slow if you have many. _getUserIdByKey uses a cached dictionary with direct lookup search value -> userid implement optional case-insensitive lookup for _getUserIdByKey use lower-cased search value and lower-cased dict keys to support the case-insensitive the lowercase support dicts are only built / kept in memory and not cached to disk
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Fri, 14 Feb 2014 17:01:16 +0100
parents 5ab38cec99f7
children 9248e31d7a95
files MoinMoin/user.py
diffstat 1 files changed, 37 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/user.py	Fri Feb 14 15:29:47 2014 +0100
+++ b/MoinMoin/user.py	Fri Feb 14 17:01:16 2014 +0100
@@ -21,6 +21,7 @@
 """
 
 import os, time, codecs, base64
+from copy import deepcopy
 import md5crypt
 
 try:
@@ -57,7 +58,10 @@
     return userlist
 
 def get_by_filter(request, filter_func):
-    """ Searches for an user with a given filter function """
+    """ Searches for a user with a given filter function
+
+    Be careful: SLOW for big wikis, rather use _getUserIdByKey & related.
+    """
     for uid in getUserList(request):
         theuser = User(request, uid)
         if filter_func(theuser):
@@ -65,16 +69,13 @@
 
 def get_by_email_address(request, email_address):
     """ Searches for an user with a particular e-mail address and returns it. """
-    filter_func = lambda user: user.valid and user.email.lower() == email_address.lower()
-    return get_by_filter(request, filter_func)
+    return _getUserIdByKey(request, 'email', email_address, case=False)
 
 def get_by_jabber_id(request, jabber_id):
     """ Searches for an user with a perticular jabber id and returns it. """
-    filter_func = lambda user: user.valid and user.jid.lower() == jabber_id.lower()
-    return get_by_filter(request, filter_func)
+    return _getUserIdByKey(request, 'jid', jabber_id, case=False)
 
-
-def _getUserIdByKey(request, key, search):
+def _getUserIdByKey(request, key, search, case=True):
     """ Get the user ID for a specified key/value pair.
 
     This method must only be called for keys that are
@@ -82,14 +83,18 @@
 
     @param key: the key to look in
     @param search: the value to look for
+    @param case: do a case-sensitive lookup?
     @return the corresponding user ID or None
     """
     if key not in CACHED_USER_ATTRS:
         raise ValueError("unsupported key, must be in CACHED_USER_ATTRS")
     if not search:
         return None
+    cfg_cache_attr = key + "2id"
+    if not case:
+        cfg_cache_attr += "_lower"
+        search = search.lower()
     cfg = request.cfg
-    cfg_cache_attr = key + "2id"
     try:
         attr2id = getattr(cfg.cache, cfg_cache_attr)
         from_disk = False
@@ -128,14 +133,18 @@
                   or None to delete the in-memory cache.
     """
     for attrname in CACHED_USER_ATTRS:
-        cfg_cache_attr = attrname + "2id"
         if cache is None:
             try:
-                delattr(request.cfg.cache, cfg_cache_attr)
+                delattr(request.cfg.cache, attrname + "2id")
+            except:
+                pass
+            try:
+                delattr(request.cfg.cache, attrname + "2id_lower")
             except:
                 pass
         else:
-            setattr(request.cfg.cache, cfg_cache_attr, cache[attrname])
+            setattr(request.cfg.cache, attrname + "2id", cache[attrname])
+            setattr(request.cfg.cache, attrname + "2id_lower", cache[attrname + "_lower"])
 
 
 def loadLookupCaches(request):
@@ -148,7 +157,8 @@
         cache = {}
         for attrname in CACHED_USER_ATTRS:
             cache[attrname] = {}
-    setMemoryLookupCaches(request, cache)
+    cache_with_lowercase = addLowerCaseKeys(cache)
+    setMemoryLookupCaches(request, cache_with_lowercase)
 
 
 def rebuildLookupCaches(request):
@@ -176,7 +186,8 @@
                     else:
                         attr2id[value] = userid
 
-    setMemoryLookupCaches(request, cache)
+    cache_with_lowercase = addLowerCaseKeys(cache)
+    setMemoryLookupCaches(request, cache_with_lowercase)
     diskcache.update(cache)
     diskcache.unlock()
     return cache
@@ -190,6 +201,17 @@
     caching.CacheEntry(request, arena, key, scope=scope).remove()
 
 
+def addLowerCaseKeys(cache):
+    """add lowercased lookup keys, so we can support case-insensitive lookup"""
+    c = deepcopy(cache)  # we do not want to modify cache itself
+    for attrname in CACHED_USER_ATTRS:
+        attr2id = c[attrname]
+        attr2id_lower = c[attrname + "_lower"] = {}
+        for key, value in attr2id.iteritems():
+            attr2id_lower[key.lower()] = value
+    return c
+
+
 def getUserId(request, searchName):
     """ Get the user ID for a specific user NAME.
 
@@ -1030,7 +1052,8 @@
                         else:
                             attr2id[value] = userid
 
-        setMemoryLookupCaches(self._request, cache)
+        cache_with_lowercase = addLowerCaseKeys(cache)
+        setMemoryLookupCaches(self._request, cache_with_lowercase)
         diskcache.update(cache)
         diskcache.unlock()