Passed
Push — find-similar ( 3204f0...e726cf )
by Matias
04:36
created

RelationMapper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
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
			$face1 = $relation->getFace1();
125
			$face2 = $relation->getFace2();
126
			$state = $relation->getState();
127
128
			$row = array();
129
			if (isset($matrix[$face1])) {
130
				$row = $matrix[$face1];
131
			}
132
			$row[$face2] = $state;
133
			$matrix[$face1] = $row;
134
		}
135
		return $matrix;
136
	}
137
138
	public function existsOnMatrix(Relation $relation, array $matrix): bool {
139
		$face1 = $relation->getFace1();
140
		$face2 = $relation->getFace2();
141
142
		if (isset($matrix[$face1])) {
143
			$row = $matrix[$face1];
144
			if (isset($row[$face2])) {
145
				return true;
146
			}
147
		}
148
		if (isset($matrix[$face2])) {
149
			$row = $matrix[$face2];
150
			if (isset($row[$face1])) {
151
				return true;
152
			}
153
		}
154
		return false;
155
	}
156
157
	public function merge(string $userId, int $modelId, array $relations): int {
158
		$added = 0;
159
		$this->db->beginTransaction();
160
		try {
161
			$oldMatrix = $this->findByUserAsMatrix($userId, $modelId);
162
			foreach ($relations as $relation) {
163
				if ($this->existsOnMatrix($relation, $oldMatrix))
164
					continue;
165
				$this->insert($relation);
166
				$added++;
167
			}
168
			$this->db->commit();
169
		} catch (\Exception $e) {
170
			$this->db->rollBack();
171
			throw $e;
172
		}
173
		return $added;
174
	}
175
176
}