map: add migration

pull/396/head
Varun Patil 2023-02-09 00:35:35 -08:00
parent 72a08b917f
commit 1751ad7d80
6 changed files with 96 additions and 50 deletions

View File

@ -58,6 +58,7 @@ class MapController extends ApiBase
try { try {
$clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds, $root); $clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds, $root);
return new JSONResponse($clusters); return new JSONResponse($clusters);
} catch (\Exception $e) { } catch (\Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);

View File

@ -44,7 +44,7 @@ trait TimelineQueryMap
$count = $query->createFunction('COUNT(m.fileid) AS count'); $count = $query->createFunction('COUNT(m.fileid) AS count');
$query->select($lat, $lon, $count) $query->select($lat, $lon, $count)
->from('memories_map_clusters', 'c') ->from('memories_mapclusters', 'c')
; ;
if ($gridLen > 0.02) { if ($gridLen > 0.02) {
@ -58,7 +58,7 @@ trait TimelineQueryMap
} }
// JOIN with memories for files from the current user // JOIN with memories for files from the current user
$query->innerJoin('c', 'memories', 'm', $query->expr()->eq('c.id', 'm.map_cluster_id')); $query->innerJoin('c', 'memories', 'm', $query->expr()->eq('c.id', 'm.mapcluster'));
// JOIN with filecache for existing files // JOIN with filecache for existing files
$query = $this->joinFilecache($query, $root, true, false); $query = $this->joinFilecache($query, $root, true, false);
@ -96,7 +96,7 @@ trait TimelineQueryMap
// SELECT all photos with this tag // SELECT all photos with this tag
$query->select('f.fileid', 'f.etag')->from('memories', 'm')->where( $query->select('f.fileid', 'f.etag')->from('memories', 'm')->where(
$query->expr()->eq('m.map_cluster_id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)) $query->expr()->eq('m.mapcluster', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT))
); );
// WHERE these photos are in the user's requested folder recursively // WHERE these photos are in the user's requested folder recursively

View File

@ -15,6 +15,7 @@ use Psr\Log\LoggerInterface;
require_once __DIR__.'/../ExifFields.php'; require_once __DIR__.'/../ExifFields.php';
const DELETE_TABLES = ['memories', 'memories_livephoto', 'memories_places']; const DELETE_TABLES = ['memories', 'memories_livephoto', 'memories_places'];
const TRUNCATE_TABLES = ['memories_mapclusters'];
class TimelineWrite class TimelineWrite
{ {
@ -82,7 +83,7 @@ class TimelineWrite
// Check if need to update // Check if need to update
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->select('fileid', 'mtime', 'map_cluster_id') $query->select('fileid', 'mtime', 'mapcluster')
->from('memories') ->from('memories')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
; ;
@ -163,16 +164,24 @@ class TimelineWrite
// Store location data // Store location data
$lat = null; $lat = null;
$lon = null; $lon = null;
$mapCluster = $prevRow ? (int) $prevRow['map_cluster_id'] : -1; $mapCluster = $prevRow ? (int) $prevRow['mapcluster'] : -1;
if (\array_key_exists('GPSLatitude', $exif) && \array_key_exists('GPSLongitude', $exif)) { if (\array_key_exists('GPSLatitude', $exif) && \array_key_exists('GPSLongitude', $exif)) {
try {
$lat = (float) $exif['GPSLatitude']; $lat = (float) $exif['GPSLatitude'];
$lon = (float) $exif['GPSLongitude']; $lon = (float) $exif['GPSLongitude'];
$mapCluster = $this->getMapCluster($fileId, $mapCluster, $lat, $lon);
try {
$mapCluster = $this->getMapCluster($mapCluster, $lat, $lon);
$mapCluster = $mapCluster <= 0 ? null : $mapCluster;
} catch (\Error $e) {
$logger = \OC::$server->get(LoggerInterface::class);
$logger->log(3, 'Error updating map cluster data: '.$e->getMessage(), ['app' => 'memories']);
}
try {
$this->updatePlacesData($fileId, $lat, $lon); $this->updatePlacesData($fileId, $lat, $lon);
} catch (\Error $e) { } catch (\Error $e) {
$logger = \OC::$server->get(LoggerInterface::class); $logger = \OC::$server->get(LoggerInterface::class);
$logger->log(3, 'Error updating geo data: '.$e->getMessage(), ['app' => 'memories']); $logger->log(3, 'Error updating places data: '.$e->getMessage(), ['app' => 'memories']);
} }
} }
@ -191,7 +200,7 @@ class TimelineWrite
'liveid' => $query->createNamedParameter($liveid, IQueryBuilder::PARAM_STR), 'liveid' => $query->createNamedParameter($liveid, IQueryBuilder::PARAM_STR),
'lat' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR), 'lat' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR),
'lon' => $query->createNamedParameter($lon, IQueryBuilder::PARAM_STR), 'lon' => $query->createNamedParameter($lon, IQueryBuilder::PARAM_STR),
'map_cluster_id' => $query->createNamedParameter($mapCluster, IQueryBuilder::PARAM_INT), 'mapcluster' => $query->createNamedParameter($mapCluster, IQueryBuilder::PARAM_INT),
]; ];
if ($prevRow) { if ($prevRow) {
@ -241,7 +250,7 @@ class TimelineWrite
public function clear() public function clear()
{ {
$p = $this->connection->getDatabasePlatform(); $p = $this->connection->getDatabasePlatform();
foreach (DELETE_TABLES as $table) { foreach (array_merge(DELETE_TABLES, TRUNCATE_TABLES) as $table) {
$this->connection->executeStatement($p->getTruncateTableSQL('*PREFIX*'.$table, false)); $this->connection->executeStatement($p->getTruncateTableSQL('*PREFIX*'.$table, false));
} }
} }

View File

@ -7,27 +7,27 @@ namespace OCA\Memories\Db;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection; use OCP\IDBConnection;
const CLUSTER_DEG = 0.0003;
trait TimelineWriteMap trait TimelineWriteMap
{ {
protected IDBConnection $connection; protected IDBConnection $connection;
protected function getMapCluster(int $fileId, int $prevCluster, float $lat, float $lon): int protected function getMapCluster(int $prevCluster, float $lat, float $lon): int
{ {
// get all clusters within 30 metres // Get all possible clusters within CLUSTER_DEG radius
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->select('id') $query->select('id', 'lat', 'lon')
->from('memories_map_clusters') ->from('memories_mapclusters')
->andWhere($query->expr()->gte('lat', $query->createNamedParameter($lat - 0.0003, IQueryBuilder::PARAM_STR))) ->andWhere($query->expr()->gte('lat', $query->createNamedParameter($lat - CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
->andWhere($query->expr()->lte('lat', $query->createNamedParameter($lat + 0.0003, IQueryBuilder::PARAM_STR))) ->andWhere($query->expr()->lte('lat', $query->createNamedParameter($lat + CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
->andWhere($query->expr()->gte('lon', $query->createNamedParameter($lon - 0.0003, IQueryBuilder::PARAM_STR))) ->andWhere($query->expr()->gte('lon', $query->createNamedParameter($lon - CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
->andWhere($query->expr()->lte('lon', $query->createNamedParameter($lon + 0.0003, IQueryBuilder::PARAM_STR))) ->andWhere($query->expr()->lte('lon', $query->createNamedParameter($lon + CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
; ;
$rows = $query->executeQuery()->fetchAll();
$result = $query->executeQuery();
$rows = $result->fetchAll();
// Find cluster closest to the point // Find cluster closest to the point
$minDist = 999999999; $minDist = PHP_INT_MAX;
$minId = -1; $minId = -1;
foreach ($rows as $r) { foreach ($rows as $r) {
$clusterLat = (float) $r['lat']; $clusterLat = (float) $r['lat'];
@ -59,10 +59,28 @@ trait TimelineWriteMap
return $minId; return $minId;
} }
protected function createMapCluster(float $lat, float $lon): int protected function addToCluster(int $clusterId, float $lat, float $lon): void
{
if ($clusterId <= 0) {
return;
}
$query = $this->connection->getQueryBuilder();
$query->update('memories_mapclusters')
->set('point_count', $query->createFunction('point_count + 1'))
->set('lat_sum', $query->createFunction("lat_sum + {$lat}"))
->set('lon_sum', $query->createFunction("lon_sum + {$lon}"))
->set('lat', $query->createFunction('lat_sum / point_count'))
->set('lon', $query->createFunction('lon_sum / point_count'))
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))
;
$query->executeStatement();
}
private function createMapCluster(float $lat, float $lon): int
{ {
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->insert('memories_map_clusters') $query->insert('memories_mapclusters')
->values([ ->values([
'point_count' => $query->createNamedParameter(1, IQueryBuilder::PARAM_INT), 'point_count' => $query->createNamedParameter(1, IQueryBuilder::PARAM_INT),
'lat_sum' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR), 'lat_sum' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR),
@ -76,35 +94,17 @@ trait TimelineWriteMap
return (int) $query->getLastInsertId(); return (int) $query->getLastInsertId();
} }
protected function removeFromCluster(int $clusterId, float $lat, float $lon): void private function removeFromCluster(int $clusterId, float $lat, float $lon): void
{ {
if ($clusterId <= 0) { if ($clusterId <= 0) {
return; return;
} }
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->update('memories_map_clusters') $query->update('memories_mapclusters')
->set('point_count', $query->createFunction('point_count - 1')) ->set('point_count', $query->createFunction('point_count - 1'))
->set('lat_sum', $query->createFunction('lat_sum - '.$lat)) ->set('lat_sum', $query->createFunction("lat_sum - {$lat}"))
->set('lon_sum', $query->createFunction('lon_sum - '.$lon)) ->set('lon_sum', $query->createFunction("lon_sum - {$lon}"))
->set('lat', $query->createFunction('lat_sum / point_count'))
->set('lon', $query->createFunction('lon_sum / point_count'))
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))
;
$query->executeStatement();
}
protected function addToCluster(int $clusterId, float $lat, float $lon): void
{
if ($clusterId <= 0) {
return;
}
$query = $this->connection->getQueryBuilder();
$query->update('memories_map_clusters')
->set('point_count', $query->createFunction('point_count + 1'))
->set('lat_sum', $query->createFunction('lat_sum + '.$lat))
->set('lon_sum', $query->createFunction('lon_sum + '.$lon))
->set('lat', $query->createFunction('lat_sum / point_count')) ->set('lat', $query->createFunction('lat_sum / point_count'))
->set('lon', $query->createFunction('lon_sum / point_count')) ->set('lon', $query->createFunction('lon_sum / point_count'))
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT))) ->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))

View File

@ -48,8 +48,8 @@ class Version401100Date20230208181533 extends SimpleMigrationStep
/** @var ISchemaWrapper $schema */ /** @var ISchemaWrapper $schema */
$schema = $schemaClosure(); $schema = $schemaClosure();
// Add lat lon to memories
$table = $schema->getTable('memories'); $table = $schema->getTable('memories');
if (!$table->hasColumn('lat')) { if (!$table->hasColumn('lat')) {
$table->addColumn('lat', Types::DECIMAL, [ $table->addColumn('lat', Types::DECIMAL, [
'notnull' => false, 'notnull' => false,
@ -63,8 +63,44 @@ class Version401100Date20230208181533 extends SimpleMigrationStep
'precision' => 9, 'precision' => 9,
'scale' => 6, 'scale' => 6,
]); ]);
$table->addIndex(['lat', 'lon'], 'memories_lat_lon_index'); $table->addIndex(['lat', 'lon'], 'memories_lat_lon_index');
$table->addColumn('mapcluster', Types::INTEGER, [
'notnull' => false,
'default' => null,
]);
$table->addIndex(['mapcluster'], 'memories_mapcluster_index');
}
// Add clusters table
if (!$schema->hasTable('memories_mapclusters')) {
$table = $schema->createTable('memories_mapclusters');
$table->addColumn('id', Types::INTEGER, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('point_count', Types::INTEGER, [
'notnull' => true,
]);
$table->addColumn('lat_sum', Types::FLOAT, [
'notnull' => false,
'default' => null,
]);
$table->addColumn('lon_sum', Types::FLOAT, [
'notnull' => false,
'default' => null,
]);
$table->addColumn('lat', Types::FLOAT, [
'notnull' => false,
'default' => null,
]);
$table->addColumn('lon', Types::FLOAT, [
'notnull' => false,
'default' => null,
]);
$table->setPrimaryKey(['id']);
$table->addIndex(['lat', 'lon'], 'memories_clst_ll_idx');
} }
return $schema; return $schema;