summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-06-13 18:48:49 +0200
committerCôme Chilliet <come.chilliet@nextcloud.com>2023-04-27 11:57:45 +0200
commit35dc2235001bf61f07c78b50e74ca029bb9fc05d (patch)
treeb68b7d6e311caf9f7c3cc597e42c085fce792851 /lib
parent5e96228eb1f7999a327dacab22055ec2aa8e28a3 (diff)
downloadnextcloud-server-35dc2235001bf61f07c78b50e74ca029bb9fc05d.tar.gz
nextcloud-server-35dc2235001bf61f07c78b50e74ca029bb9fc05d.zip
Optimize retrieving display name when searching for users in a group
This is recurrent scenario that we are searching for users and then for each users we fetch the displayName. This is inefficient, so instead try to do one query to fetch everything (e.g. Database backend) or use the already existing DisplayNameCache helper. Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'lib')
-rw-r--r--lib/private/Group/Database.php52
-rw-r--r--lib/private/Group/Group.php17
-rw-r--r--lib/private/User/LazyUser.php20
-rw-r--r--lib/private/User/Manager.php29
-rw-r--r--lib/public/Group/Backend/ABackend.php15
-rw-r--r--lib/public/GroupInterface.php24
-rw-r--r--lib/public/IGroup.php2
7 files changed, 126 insertions, 33 deletions
diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php
index 5f17477db77..bfc95c574e2 100644
--- a/lib/private/Group/Database.php
+++ b/lib/private/Group/Database.php
@@ -43,6 +43,9 @@ use OCP\Group\Backend\IRemoveFromGroupBackend;
use OCP\Group\Backend\ISetDisplayNameBackend;
use OCP\Group\Backend\INamedBackend;
use OCP\IDBConnection;
+use OCP\IUserManager;
+use OC\User\LazyUser;
+use OC\User\DisplayNameCache;
/**
* Class for group management in a SQL Database (e.g. MySQL, SQLite)
@@ -376,6 +379,55 @@ class Database extends ABackend implements
return $users;
}
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
+ $this->fixDI();
+
+ $query = $this->dbConn->getQueryBuilder();
+ $query->select('g.uid', 'u.displayname');
+
+ $query->from('group_user', 'g')
+ ->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
+ ->orderBy('g.uid', 'ASC');
+
+ $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'))
+
+ if ($search !== '') {
+ $query->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
+ $query->expr()->eq('p.userid', 'u.uid'),
+ $query->expr()->eq('p.appid', $query->expr()->literal('settings')),
+ $query->expr()->eq('p.configkey', $query->expr()->literal('email'))
+ ))
+ // sqlite doesn't like re-using a single named parameter here
+ ->andWhere(
+ $query->expr()->orX(
+ $query->expr()->ilike('g.uid', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
+ $query->expr()->ilike('u.displayname', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
+ $query->expr()->ilike('p.configvalue', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))
+ )
+ )
+ ->orderBy('u.uid_lower', 'ASC');
+ }
+
+ if ($limit !== -1) {
+ $query->setMaxResults($limit);
+ }
+ if ($offset !== 0) {
+ $query->setFirstResult($offset);
+ }
+
+ $result = $query->executeQuery();
+
+ $users = [];
+ $userManager = \OCP\Server::get(IUserManager::class);
+ $displayNameCache = \OCP\Server::get(DisplayNameCache::class);
+ while ($row = $result->fetch()) {
+ $users[$row['uid']] = new LazyUser($row['uid'], $displayNameCache, $userManager, $row['displayname'] ?? null);
+ }
+ $result->closeCursor();
+
+ return $users;
+ }
+
/**
* get the number of all users matching the search string in a group
* @param string $gid
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index cca179bfe19..b2903bcdb26 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -242,18 +242,13 @@ class Group implements IGroup {
}
/**
- * search for users in the group by userid
- *
- * @param string $search
- * @param int $limit
- * @param int $offset
- * @return \OC\User\User[]
+ * Search for users in the group by userid or display name
+ * @return IUser[]
*/
- public function searchUsers($search, $limit = null, $offset = null) {
+ public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array {
$users = [];
foreach ($this->backends as $backend) {
- $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
- $users += $this->getVerifiedUsers($userIds);
+ $users = array_merge($users, $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0));
if (!is_null($limit) and $limit <= 0) {
return $users;
}
@@ -309,12 +304,12 @@ class Group implements IGroup {
* @param int $limit
* @param int $offset
* @return \OC\User\User[]
+ * @deprecated 25.0.0 Use searchUsers instead (same implementation)
*/
public function searchDisplayName($search, $limit = null, $offset = null) {
$users = [];
foreach ($this->backends as $backend) {
- $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
- $users = $this->getVerifiedUsers($userIds);
+ $users = array_merge($users, $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0));
if (!is_null($limit) and $limit <= 0) {
return array_values($users);
}
diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php
index 096578b8f37..c36ff86eff4 100644
--- a/lib/private/User/LazyUser.php
+++ b/lib/private/User/LazyUser.php
@@ -30,16 +30,26 @@ use OCP\UserInterface;
class LazyUser implements IUser {
private ?IUser $user = null;
private string $uid;
+ private ?string $displayName;
private IUserManager $userManager;
+ private ?UserInterface $backend;
- public function __construct(string $uid, IUserManager $userManager) {
+ public function __construct(string $uid, IUserManager $userManager, ?string $displayName = null, ?UserInterface $backend = null) {
$this->uid = $uid;
$this->userManager = $userManager;
+ $this->displayName = $displayName;
+ $this->backend = $backend;
}
private function getUser(): IUser {
if ($this->user === null) {
- $this->user = $this->userManager->get($this->uid);
+ if ($this->backend) {
+ /** @var \OC\User\Manager $manager */
+ $manager = $this->userManager;
+ $this->user = $manager->getUserObject($this->uid, $this->backend);
+ } else {
+ $this->user = $this->userManager->get($this->uid);
+ }
}
/** @var IUser */
$user = $this->user;
@@ -51,7 +61,11 @@ class LazyUser implements IUser {
}
public function getDisplayName() {
- return $this->userManager->getDisplayName($this->uid) ?? $this->uid;
+ if ($this->displayName) {
+ return $this->displayName;
+ }
+
+ return $this->userManager->getDisplayName($this->uid);
}
public function setDisplayName($displayName) {
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index 859ebd2a604..c3275934a5a 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -202,7 +202,7 @@ class Manager extends PublicEmitter implements IUserManager {
* @param bool $cacheUser If false the newly created user object will not be cached
* @return \OC\User\User
*/
- protected function getUserObject($uid, $backend, $cacheUser = true) {
+ public function getUserObject($uid, $backend, $cacheUser = true) {
if ($backend instanceof IGetRealUIDBackend) {
$uid = $backend->getRealUID($uid);
}
@@ -293,58 +293,53 @@ class Manager extends PublicEmitter implements IUserManager {
}
/**
- * search by user id
+ * Search by user id
*
* @param string $pattern
* @param int $limit
* @param int $offset
- * @return \OC\User\User[]
+ * @return IUser[]
+ * @deprecated since 25.0.0, use searchDisplayName instead
*/
public function search($pattern, $limit = null, $offset = null) {
$users = [];
+ $displayNameCache = \OCP\Server::get(DisplayNameCache::class);
foreach ($this->backends as $backend) {
$backendUsers = $backend->getUsers($pattern, $limit, $offset);
if (is_array($backendUsers)) {
foreach ($backendUsers as $uid) {
- $users[$uid] = $this->getUserObject($uid, $backend);
+ $users[$uid] = new LazyUser($uid, $displayNameCache, $this, null, $backend);
}
}
}
- uasort($users, function ($a, $b) {
- /**
- * @var \OC\User\User $a
- * @var \OC\User\User $b
- */
+ uasort($users, function (IUser $a, IUser $b) {
return strcasecmp($a->getUID(), $b->getUID());
});
return $users;
}
/**
- * search by displayName
+ * Search by displayName
*
* @param string $pattern
* @param int $limit
* @param int $offset
- * @return \OC\User\User[]
+ * @return IUser[]
*/
public function searchDisplayName($pattern, $limit = null, $offset = null) {
$users = [];
+ $displayNameCache = \OCP\Server::get(DisplayNameCache::class);
foreach ($this->backends as $backend) {
$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
if (is_array($backendUsers)) {
foreach ($backendUsers as $uid => $displayName) {
- $users[] = $this->getUserObject($uid, $backend);
+ $users[] = new LazyUser($uid, $displayNameCache, $this, $displayName, $backend);
}
}
}
- usort($users, function ($a, $b) {
- /**
- * @var \OC\User\User $a
- * @var \OC\User\User $b
- */
+ usort($users, function (IUser $a, IUser $b) {
return strcasecmp($a->getDisplayName(), $b->getDisplayName());
});
return $users;
diff --git a/lib/public/Group/Backend/ABackend.php b/lib/public/Group/Backend/ABackend.php
index 7f5cf732335..e76285dfda9 100644
--- a/lib/public/Group/Backend/ABackend.php
+++ b/lib/public/Group/Backend/ABackend.php
@@ -26,6 +26,10 @@ declare(strict_types=1);
namespace OCP\Group\Backend;
use OCP\GroupInterface;
+use OCP\IUserManager;
+use OCP\Server;
+use OC\User\LazyUser;
+use OC\User\DisplayNameCache;
/**
* @since 14.0.0
@@ -65,4 +69,15 @@ abstract class ABackend implements GroupInterface {
return (bool)($actions & $implements);
}
+
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
+ // Default implementation for compatibility reasons
+ $displayNameCache = Server::get(DisplayNameCache::class);
+ $userManager = Server::get(IUserManager::class);
+ $users = [];
+ foreach ($this->usersInGroup($gid, $search, $limit, $offset) as $userId) {
+ $users[$userId] = new LazyUser($userId, $displayNameCache, $userManager);
+ }
+ return $users;
+ }
}
diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php
index b7c07136126..07fd5cb50c4 100644
--- a/lib/public/GroupInterface.php
+++ b/lib/public/GroupInterface.php
@@ -106,13 +106,35 @@ interface GroupInterface {
public function groupExists($gid);
/**
- * get a list of all users in a group
+ * @brief Get a list of user ids in a group matching the given search parameters.
+ *
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
* @return array an array of user ids
* @since 4.5.0
+ * @deprecated 25.0.0 Use searchInGroup instead, for performance reasons
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0);
+
+ /**
+ * @brief Get a list of users matching the given search parameters.
+ *
+ * Implementations of this method should return lazy evaluated user objects and
+ * preload if possible the display name.
+ *
+ * <code>
+ * $users = $groupBackend->searchInGroup('admin', 'John', 10, 0);
+ * </code>
+ *
+ * @param string $gid The group id of the user we want to search
+ * @param string $search The part of the display name or user id of the users we
+ * want to search. This can be empty to get all the users.
+ * @param int $limit The limit of results
+ * @param int $offset The offset of the results
+ * @return IUser[]
+ * @since 25.0.0
+ */
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array;
}
diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php
index ba13359c472..ec26cc55b69 100644
--- a/lib/public/IGroup.php
+++ b/lib/public/IGroup.php
@@ -99,7 +99,7 @@ interface IGroup {
* @return \OCP\IUser[]
* @since 8.0.0
*/
- public function searchUsers($search, $limit = null, $offset = null);
+ public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array;
/**
* returns the number of users matching the search string