Passed
Push — find-similar ( e726cf...a578af )
by Matias
04:51
created

RelationMapper   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Test Coverage

Coverage 3.09%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 88
c 7
b 0
f 0
dl 0
loc 150
ccs 3
cts 97
cp 0.0309
rs 10
wmc 22

9 Methods

Rating   Name   Duplication   Size   Complexity  
A findFromPerson() 0 11 1
A findFromPersons() 0 20 1
A __construct() 0 2 1
A deleteUser() 0 12 1
A findByUser() 0 12 1
A findByUserAsMatrix() 0 12 3
A merge() 0 17 4
A getStateOnMatrix() 0 14 5
A existsOnMatrix() 0 14 5
1
<?php
2
/**
3
 * @copyright Copyright (c) 2020, Matias De lellis <[email protected]>
4
 *
5
 * @author Matias De lellis <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\FaceRecognition\Db;
25
26
use OC\DB\QueryBuilder\Literal;
0 ignored issues
show
Bug introduced by
The type OC\DB\QueryBuilder\Literal was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
28
use OCP\IDBConnection;
29
use OCP\AppFramework\Db\QBMapper;
30
use OCP\AppFramework\Db\DoesNotExistException;
31
use OCP\DB\QueryBuilder\IQueryBuilder;
32
33
class RelationMapper extends QBMapper {
34
35 1
	public function __construct(IDBConnection $db) {
36 1
		parent::__construct($db, 'facerecog_relations', '\OCA\FaceRecognition\Db\Relation');
37 1
	}
38
39
	/**
40
	 * Find all relation from that user.
41
	 *
42
	 * @param string $userId User user to search
43
	 * @param int $modelId
44
	 * @return array
45
	 */
46
	public function findByUser(string $userId, int $modelId): array {
47
		$qb = $this->db->getQueryBuilder();
48
		$qb->select('r.id', 'r.face1', 'r.face2', 'r.state')
49
		    ->from($this->getTableName(), 'r')
50
		    ->innerJoin('r', 'facerecog_faces', 'f', $qb->expr()->eq('r.face1', 'f.id'))
51
		    ->innerJoin('f', 'facerecog_images', 'i', $qb->expr()->eq('f.image', 'i.id'))
52
		    ->where($qb->expr()->eq('i.user', $qb->createParameter('user_id')))
53
		    ->andWhere($qb->expr()->eq('i.model', $qb->createParameter('model_id')))
54
		    ->setParameter('user_id', $userId)
55
		    ->setParameter('model_id', $modelId);
56
57
		return $this->findEntities($qb);
58
	}
59
60
	public function findFromPerson(string $userId, int $personId, int $state): array {
61
		$qb = $this->db->getQueryBuilder();
62
		$qb->select('r.id', 'r.face1', 'r.face2', 'r.state')
63
		   ->from($this->getTableName(), 'r')
64
		   ->innerJoin('r', 'facerecog_faces' ,'f', $qb->expr()->orX($qb->expr()->eq('r.face1', 'f.id'), $qb->expr()->eq('r.face2', 'f.id')))
0 ignored issues
show
Bug introduced by
$qb->expr()->orX($qb->ex...>eq('r.face2', 'f.id')) of type OCP\DB\QueryBuilder\ICompositeExpression is incompatible with the type string expected by parameter $condition of OCP\DB\QueryBuilder\IQueryBuilder::innerJoin(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
		   ->innerJoin('r', 'facerecog_faces' ,'f', /** @scrutinizer ignore-type */ $qb->expr()->orX($qb->expr()->eq('r.face1', 'f.id'), $qb->expr()->eq('r.face2', 'f.id')))
Loading history...
65
		   ->innerJoin('f', 'facerecog_persons' ,'p', $qb->expr()->eq('f.person', 'p.id'))
66
		   ->where($qb->expr()->eq('p.user', $qb->createNamedParameter($userId)))
67
		   ->andWhere($qb->expr()->eq('p.id', $qb->createNamedParameter($personId)))
68
		   ->andWhere($qb->expr()->eq('r.state', $qb->createNamedParameter($state)));
69
70
		return $this->findEntities($qb);
71
	}
72
73
	public function findFromPersons(int $personId1, int $personId2) {
74
		$sub1 = $this->db->getQueryBuilder();
75
		$sub1->select('f.id')
76
		      ->from('facerecog_faces', 'f')
77
		      ->where($sub1->expr()->eq('f.person', $sub1->createParameter('person1')));
78
79
		$sub2 = $this->db->getQueryBuilder();
80
		$sub2->select('f.id')
81
		      ->from('facerecog_faces', 'f')
82
		      ->where($sub2->expr()->eq('f.person', $sub2->createParameter('person2')));
83
84
		$qb = $this->db->getQueryBuilder();
85
		$qb->select('r.id', 'r.face1', 'r.face2', 'r.state')
86
		   ->from($this->getTableName(), 'r')
87
		   ->where('((r.face1 IN (' . $sub1->getSQL() . ')) AND (r.face2 IN (' . $sub2->getSQL() . ')))')
88
		   ->orWhere('((r.face2 IN (' . $sub1->getSQL() . ')) AND (r.face1 IN (' . $sub2->getSQL() . ')))')
89
		   ->setParameter('person1', $personId1)
90
		   ->setParameter('person2', $personId2);
91
92
		return $this->findEntities($qb);
93
	}
94
95
	/**
96
	 * Deletes all relations from that user.
97
	 *
98
	 * @param string $userId User to drop persons from a table.
99
	 */
100
	public function deleteUser(string $userId) {
101
		$sub = $this->db->getQueryBuilder();
102
		$sub->select(new Literal('1'))
103
		     ->from('facerecog_faces', 'f')
104
		     ->innerJoin('f', 'facerecog_images', 'i', $sub->expr()->eq('f.image', 'i.id'))
105
		     ->andWhere($sub->expr()->eq('i.user', $sub->createParameter('user_id')));
106
107
		$qb = $this->db->getQueryBuilder();
108
		$qb->delete($this->getTableName())
109
		    ->where('EXISTS (' . $sub->getSQL() . ')')
110
		    ->setParameter('user_id', $userId)
111
		    ->execute();
112
	}
113
114
	/**
115
	 * Find all the relations of a user as an matrix array, which is faster to access.
116
	 * @param string $userId
117
	 * @param int $modelId
118
	 * return array
119
	 */
120
	public function findByUserAsMatrix(string $userId, int $modelId): array {
121
		$matrix = array();
122
		$relations = $this->findByUser($userId, $modelId);
123
		foreach ($relations as $relation) {
124
			$row = array();
125
			if (isset($matrix[$relation->face1])) {
126
				$row = $matrix[$relation->face1];
127
			}
128
			$row[$relation->face2] = $relation->state;
129
			$matrix[$relation->face1] = $row;
130
		}
131
		return $matrix;
132
	}
133
134
	public function getStateOnMatrix(int $face1, int $face2, array $matrix): int {
135
		if (isset($matrix[$face1])) {
136
			$row = $matrix[$face1];
137
			if (isset($row[$face2])) {
138
				return $matrix[$face1][$face2];
139
			}
140
		}
141
		if (isset($matrix[$face2])) {
142
			$row = $matrix[$face2];
143
			if (isset($row[$face1])) {
144
				return $matrix[$face2][$face1];
145
			}
146
		}
147
		return Relation::PROPOSED;
148
	}
149
150
	public function existsOnMatrix(int $face1, int $face2, array $matrix): bool {
151
		if (isset($matrix[$face1])) {
152
			$row = $matrix[$face1];
153
			if (isset($row[$face2])) {
154
				return true;
155
			}
156
		}
157
		if (isset($matrix[$face2])) {
158
			$row = $matrix[$face2];
159
			if (isset($row[$face1])) {
160
				return true;
161
			}
162
		}
163
		return false;
164
	}
165
166
	public function merge(string $userId, int $modelId, array $relations): int {
167
		$added = 0;
168
		$this->db->beginTransaction();
169
		try {
170
			$oldMatrix = $this->findByUserAsMatrix($userId, $modelId);
171
			foreach ($relations as $relation) {
172
				if ($this->existsOnMatrix($relation->face1, $relation->face2, $oldMatrix))
173
					continue;
174
				$this->insert($relation);
175
				$added++;
176
			}
177
			$this->db->commit();
178
		} catch (\Exception $e) {
179
			$this->db->rollBack();
180
			throw $e;
181
		}
182
		return $added;
183
	}
184
185
}