2022-10-07 19:28:39 +00:00
|
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace OCA\Memories\Db;
|
|
|
|
|
|
|
|
use OCP\IDBConnection;
|
|
|
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
|
|
|
use OCP\Files\Folder;
|
|
|
|
|
|
|
|
trait TimelineQueryFaces {
|
|
|
|
protected IDBConnection $connection;
|
|
|
|
|
2022-10-08 06:46:08 +00:00
|
|
|
public function transformFaceFilter(IQueryBuilder &$query, string $userId, string $faceStr) {
|
2022-10-08 06:26:09 +00:00
|
|
|
// Get title and uid of face user
|
2022-10-08 06:46:08 +00:00
|
|
|
$faceNames = explode('/', $faceStr);
|
|
|
|
if (count($faceNames) !== 2) throw new \Exception("Invalid face query");
|
2022-10-08 06:26:09 +00:00
|
|
|
$faceUid = $faceNames[0];
|
|
|
|
$faceName = $faceNames[1];
|
|
|
|
|
|
|
|
// Get cluster ID
|
|
|
|
$sq = $query->getConnection()->getQueryBuilder();
|
|
|
|
$id = $sq->select('id')->from('recognize_face_clusters')
|
|
|
|
->where($query->expr()->eq('user_id', $sq->createNamedParameter($faceUid)))
|
|
|
|
->andWhere($query->expr()->eq('title', $sq->createNamedParameter($faceName)))
|
|
|
|
->executeQuery()->fetchOne();
|
2022-10-08 06:46:08 +00:00
|
|
|
if (!$id) throw new \Exception("Unknown person: $faceStr");
|
2022-10-08 06:26:09 +00:00
|
|
|
|
|
|
|
// Join with cluster
|
2022-10-07 19:28:39 +00:00
|
|
|
$query->innerJoin('m', 'recognize_face_detections', 'rfd', $query->expr()->andX(
|
|
|
|
$query->expr()->eq('rfd.file_id', 'm.fileid'),
|
2022-10-08 06:26:09 +00:00
|
|
|
$query->expr()->eq('rfd.cluster_id', $query->createNamedParameter($id)),
|
2022-10-07 19:28:39 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getFaces(Folder $folder) {
|
|
|
|
$query = $this->connection->getQueryBuilder();
|
|
|
|
|
|
|
|
// SELECT all face clusters
|
|
|
|
$count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
|
2022-10-08 06:26:09 +00:00
|
|
|
$query->select('rfc.id', 'rfc.user_id', 'rfc.title', $count)->from('recognize_face_clusters', 'rfc');
|
2022-10-07 19:28:39 +00:00
|
|
|
|
|
|
|
// WHERE there are faces with this cluster
|
|
|
|
$query->innerJoin('rfc', 'recognize_face_detections', 'rfd', $query->expr()->eq('rfc.id', 'rfd.cluster_id'));
|
|
|
|
|
|
|
|
// WHERE these items are memories indexed photos
|
|
|
|
$query->innerJoin('rfd', 'memories', 'm', $query->expr()->eq('m.fileid', 'rfd.file_id'));
|
|
|
|
|
|
|
|
// WHERE these photos are in the user's requested folder recursively
|
|
|
|
$query->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, true, false));
|
|
|
|
|
|
|
|
// GROUP by ID of face cluster
|
|
|
|
$query->groupBy('rfc.id');
|
|
|
|
|
|
|
|
// ORDER by number of faces in cluster
|
|
|
|
$query->orderBy('count', 'DESC');
|
|
|
|
|
|
|
|
// FETCH all faces
|
|
|
|
$faces = $query->executeQuery()->fetchAll();
|
|
|
|
|
|
|
|
// Post process
|
|
|
|
foreach($faces as &$row) {
|
2022-10-08 00:57:48 +00:00
|
|
|
$row['id'] = intval($row['id']);
|
2022-10-07 19:28:39 +00:00
|
|
|
$row["name"] = $row["title"];
|
|
|
|
unset($row["title"]);
|
|
|
|
$row["count"] = intval($row["count"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $faces;
|
|
|
|
}
|
|
|
|
|
2022-10-08 02:00:55 +00:00
|
|
|
public function getFacePreviews(Folder $folder) {
|
2022-10-07 19:28:39 +00:00
|
|
|
$query = $this->connection->getQueryBuilder();
|
|
|
|
|
2022-10-08 02:09:05 +00:00
|
|
|
// Windowing
|
2022-10-08 02:00:55 +00:00
|
|
|
$rowNumber = $query->createFunction('ROW_NUMBER() OVER (PARTITION BY rfd.cluster_id) as n');
|
|
|
|
|
2022-10-07 19:28:39 +00:00
|
|
|
// SELECT face detections for ID
|
2022-10-08 02:00:55 +00:00
|
|
|
$query->select(
|
|
|
|
'rfd.cluster_id',
|
|
|
|
'rfd.file_id',
|
|
|
|
'rfd.x', 'rfd.y', 'rfd.width', 'rfd.height',
|
|
|
|
'f.etag',
|
|
|
|
$rowNumber,
|
|
|
|
)->from('recognize_face_detections', 'rfd');
|
|
|
|
$query->where($query->expr()->isNotNull('rfd.cluster_id'));
|
2022-10-07 19:28:39 +00:00
|
|
|
|
|
|
|
// WHERE these photos are memories indexed
|
|
|
|
$query->innerJoin('rfd', 'memories', 'm', $query->expr()->eq('m.fileid', 'rfd.file_id'));
|
|
|
|
|
|
|
|
// WHERE these photos are in the user's requested folder recursively
|
|
|
|
$query->innerJoin('m', 'filecache', 'f', $this->getFilecacheJoinQuery($query, $folder, true, false));
|
|
|
|
|
2022-10-08 02:00:55 +00:00
|
|
|
// Make this a sub query
|
|
|
|
$fun = $query->createFunction('(' . $query->getSQL() . ')');
|
|
|
|
|
|
|
|
// Create outer query
|
|
|
|
$outerQuery = $this->connection->getQueryBuilder();
|
|
|
|
$outerQuery->setParameters($query->getParameters());
|
|
|
|
$outerQuery->select('*')->from($fun, 't');
|
|
|
|
$outerQuery->where($query->expr()->lte('t.n', $outerQuery->createParameter('nc')));
|
|
|
|
$outerQuery->setParameter('nc', 4, IQueryBuilder::PARAM_INT);
|
2022-10-07 19:28:39 +00:00
|
|
|
|
|
|
|
// FETCH all face detections
|
2022-10-08 02:00:55 +00:00
|
|
|
$previews = $outerQuery->executeQuery()->fetchAll();
|
2022-10-07 19:28:39 +00:00
|
|
|
|
|
|
|
// Post-process, everthing is a number
|
|
|
|
foreach($previews as &$row) {
|
2022-10-08 02:00:55 +00:00
|
|
|
$row["cluster_id"] = intval($row["cluster_id"]);
|
2022-10-07 19:28:39 +00:00
|
|
|
$row["fileid"] = intval($row["file_id"]);
|
|
|
|
$row["x"] = floatval($row["x"]);
|
|
|
|
$row["y"] = floatval($row["y"]);
|
|
|
|
$row["width"] = floatval($row["width"]);
|
|
|
|
$row["height"] = floatval($row["height"]);
|
2022-10-08 02:00:55 +00:00
|
|
|
|
|
|
|
// remove stale
|
|
|
|
unset($row["file_id"]);
|
|
|
|
unset($row["n"]);
|
2022-10-07 19:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $previews;
|
|
|
|
}
|
|
|
|
}
|