diff roundup/cgi/templating.py @ 8125:b358da7c89e5 permission-performance

Optimize filtering of search results Now the Permission class constructor takes an optional argument 'filter'. Now if we do not find a permission on the whole class *and* all permission objects on the current class with a check method also have a filter method we can improve search performance by filtering in the database (instead of in python).
author Ralf Schlatterbeck <rsc@runtux.com>
date Mon, 21 Oct 2024 16:11:13 +0200
parents 2a4d0413bd20
children f7bd22bdef9d
line wrap: on
line diff
--- a/roundup/cgi/templating.py	Fri Oct 18 18:04:46 2024 +0200
+++ b/roundup/cgi/templating.py	Mon Oct 21 16:11:13 2024 +0200
@@ -3421,7 +3421,8 @@
     def batch(self, permission='View'):
         """ Return a batch object for results from the "current search"
         """
-        check = self._client.db.security.hasPermission
+        sec = self._client.db.security
+        check = sec.hasPermission
         userid = self._client.userid
         if not check('Web Access', userid):
             return Batch(self.client, [], self.pagesize, self.startwith,
@@ -3454,11 +3455,34 @@
 
         # filter for visibility
         item_ids = klass.filter(matches, filterspec, sort, group)
-        if check(permission, userid, self.classname, only_no_check = True):
+        cn = self.classname
+        if check(permission, userid, cn, only_no_check = True):
             allowed = item_ids
         else:
-            allowed = [id for id in item_ids
-                       if check(permission, userid, self.classname, itemid=id)]
+            # Note that is_filterable returns True if no permissions are
+            # found. This makes it fail early (with an empty allowed list)
+            # instead of running through all ids with an empty
+            # permission list.
+            if sec.is_filterable(permission, userid, cn):
+                new_ids = set(item_ids)
+                confirmed = set()
+                for perm in sec.filter_iter(permission, userid, cn):
+                    fargs = perm.filter(self._client.db, userid, klass)
+                    for farg in fargs:
+                        farg.update(sort=sort, group=group, retired=False)
+                        result = klass.filter(list(new_ids), **farg)
+                        new_ids.difference_update(result)
+                        confirmed.update(result)
+                        # all allowed?
+                        if not new_ids:
+                            break
+                    # all allowed?
+                    if not new_ids:
+                        break
+                allowed = list(confirmed)
+            else:
+                allowed = [id for id in item_ids
+                           if check(permission, userid, cn, itemid=id)]
 
         # return the batch object, using IDs only
         return Batch(self.client, allowed, self.pagesize, self.startwith,

Roundup Issue Tracker: http://roundup-tracker.org/