diff --git a/lib/BackgroundJob/Tasks/CreateClustersTask.php b/lib/BackgroundJob/Tasks/CreateClustersTask.php index 5e9215a3..1b98036c 100644 --- a/lib/BackgroundJob/Tasks/CreateClustersTask.php +++ b/lib/BackgroundJob/Tasks/CreateClustersTask.php @@ -388,7 +388,7 @@ private function fillFaceRelationsFromPersons(string $userId, int $modelId): int } // Merge new suggested relations - return $this->relationMapper->merge($relations); + return $this->relationMapper->merge($userId, $modelId, $relations); } } diff --git a/lib/Db/RelationMapper.php b/lib/Db/RelationMapper.php index e78d7d89..b0b5fcd1 100644 --- a/lib/Db/RelationMapper.php +++ b/lib/Db/RelationMapper.php @@ -36,21 +36,25 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'facerecog_relations', '\OCA\FaceRecognition\Db\Relation'); } - public function exists(Relation $relation): bool { + /** + * Find all relation from that user. + * + * @param string $userId User user to search + * @param int $modelId + * @return array + */ + public function findByUser(string $userId, int $modelId): array { $qb = $this->db->getQueryBuilder(); - $query = $qb - ->select(['id']) - ->from($this->getTableName()) - ->where($qb->expr()->andX($qb->expr()->eq('face1', $qb->createParameter('face1')), $qb->expr()->eq('face2', $qb->createParameter('face2')))) - ->orWhere($qb->expr()->andX($qb->expr()->eq('face2', $qb->createParameter('face1')), $qb->expr()->eq('face1', $qb->createParameter('face2')))) - ->setParameter('face1', $relation->getFace1()) - ->setParameter('face2', $relation->getFace2()); - - $resultStatement = $query->execute(); - $row = $resultStatement->fetch(); - $resultStatement->closeCursor(); - - return ($row !== false); + $qb->select('r.id', 'r.face1', 'r.face2', 'r.state') + ->from($this->getTableName(), 'r') + ->innerJoin('r', 'facerecog_faces', 'f', $qb->expr()->eq('r.face1', 'f.id')) + ->innerJoin('f', 'facerecog_images', 'i', $qb->expr()->eq('f.image', 'i.id')) + ->where($qb->expr()->eq('i.user', $qb->createParameter('user_id'))) + ->andWhere($qb->expr()->eq('i.model', $qb->createParameter('model_id'))) + ->setParameter('user_id', $userId) + ->setParameter('model_id', $modelId); + + return $this->findEntities($qb); } public function findFromPerson(string $userId, int $personId, int $state): array { @@ -88,20 +92,85 @@ public function findFromPersons(int $personId1, int $personId2) { return $this->findEntities($qb); } - public function merge(array $relations): int { - $addedCount = 0; + /** + * Deletes all relations from that user. + * + * @param string $userId User to drop persons from a table. + */ + public function deleteUser(string $userId) { + $sub = $this->db->getQueryBuilder(); + $sub->select(new Literal('1')) + ->from('facerecog_faces', 'f') + ->innerJoin('f', 'facerecog_images', 'i', $sub->expr()->eq('f.image', 'i.id')) + ->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id'))); - $this->db->beginTransaction(); + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->where('EXISTS (' . $sub->getSQL() . ')') + ->setParameter('user_id', $userId) + ->execute(); + } + + /** + * Find all the relations of a user as an matrix array, which is faster to access. + * @param string $userId + * @param int $modelId + * return array + */ + public function findByUserAsMatrix(string $userId, int $modelId): array { + $matrix = array(); + $relations = $this->findByUser($userId, $modelId); foreach ($relations as $relation) { - if ($this->exists($relation)) - continue; + $face1 = $relation->getFace1(); + $face2 = $relation->getFace2(); + $state = $relation->getState(); + + $row = array(); + if (isset($matrix[$face1])) { + $row = $matrix[$face1]; + } + $row[$face2] = $state; + $matrix[$face1] = $row; + } + return $matrix; + } - $this->insert($relation); - $addedCount++; + public function existsOnMatrix(Relation $relation, array $matrix): bool { + $face1 = $relation->getFace1(); + $face2 = $relation->getFace2(); + + if (isset($matrix[$face1])) { + $row = $matrix[$face1]; + if (isset($row[$face2])) { + return true; + } + } + if (isset($matrix[$face2])) { + $row = $matrix[$face2]; + if (isset($row[$face1])) { + return true; + } } - $this->db->commit(); + return false; + } - return $addedCount; + public function merge(string $userId, int $modelId, array $relations): int { + $added = 0; + $this->db->beginTransaction(); + try { + $oldMatrix = $this->findByUserAsMatrix($userId, $modelId); + foreach ($relations as $relation) { + if ($this->existsOnMatrix($relation, $oldMatrix)) + continue; + $this->insert($relation); + $added++; + } + $this->db->commit(); + } catch (\Exception $e) { + $this->db->rollBack(); + throw $e; + } + return $added; } } \ No newline at end of file diff --git a/lib/Service/FaceManagementService.php b/lib/Service/FaceManagementService.php index 1caee95f..8c25f29a 100644 --- a/lib/Service/FaceManagementService.php +++ b/lib/Service/FaceManagementService.php @@ -30,6 +30,7 @@ use OCA\FaceRecognition\Db\FaceMapper; use OCA\FaceRecognition\Db\ImageMapper; use OCA\FaceRecognition\Db\PersonMapper; +use OCA\FaceRecognition\Db\RelationMapper; use OCA\FaceRecognition\Service\SettingsService; @@ -56,6 +57,9 @@ class FaceManagementService { /** @var PersonMapper */ private $personMapper; + /** @var RelationMapper */ + private $relationMapper; + /** @var SettingsService */ private $settingsService; @@ -63,12 +67,14 @@ public function __construct(IUserManager $userManager, FaceMapper $faceMapper, ImageMapper $imageMapper, PersonMapper $personMapper, + RelationMapper $relationMapper, SettingsService $settingsService) { $this->userManager = $userManager; $this->faceMapper = $faceMapper; $this->imageMapper = $imageMapper; $this->personMapper = $personMapper; + $this->relationMapper = $relationMapper; $this->settingsService = $settingsService; } @@ -134,6 +140,7 @@ public function resetClustersForUser(string $userId) { $this->faceMapper->unsetPersonsRelationForUser($userId, $model); $this->personMapper->deleteUserPersons($userId); + $this->relationMapper->deleteUser($userId); } /**