fix(mobile): add partial index based on library ID to remote assets (#20214)

* feat: add libraryId to SyncAssetV1

* add partial index

# Conflicts:
#	mobile/drift_schemas/main/drift_schema_v5.json
#	mobile/lib/infrastructure/repositories/db.repository.dart
#	mobile/lib/infrastructure/repositories/db.repository.steps.dart
#	mobile/test/drift/main/generated/schema_v5.dart

* chore: make build

* rebase

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong 2025-07-30 23:59:00 +05:30 committed by GitHub
parent f85d8add01
commit 641a3baadd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 43 additions and 2 deletions

Binary file not shown.

View file

@ -5,7 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true) @TableIndex(name: 'idx_remote_asset_owner_checksum', columns: {#ownerId, #checksum})
@TableIndex.sql('''
CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum
ON remote_asset_entity (owner_id, checksum)
WHERE (library_id IS NULL);
''')
@TableIndex.sql('''
CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum
ON remote_asset_entity (owner_id, library_id, checksum)
WHERE (library_id IS NOT NULL);
''')
@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum})
class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const RemoteAssetEntity(); const RemoteAssetEntity();
@ -30,6 +40,8 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin
TextColumn get stackId => text().nullable()(); TextColumn get stackId => text().nullable()();
TextColumn get libraryId => text().nullable()();
@override @override
Set<Column> get primaryKey => {id}; Set<Column> get primaryKey => {id};
} }

View file

@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository {
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
@override @override
int get schemaVersion => 5; int get schemaVersion => 6;
@override @override
MigrationStrategy get migration => MigrationStrategy( MigrationStrategy get migration => MigrationStrategy(
@ -103,6 +103,15 @@ class Drift extends $Drift implements IDatabaseRepository {
), ),
); );
}, },
from5To6: (m, v6) async {
// Drops the (checksum, ownerId) and adds it back as (ownerId, checksum)
await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum');
await m.create(v6.idxRemoteAssetOwnerChecksum);
// Adds libraryId to remote_asset_entity
await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId);
await m.create(v6.uQRemoteAssetsOwnerChecksum);
await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum);
},
), ),
); );

View file

@ -121,6 +121,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
visibility: Value(asset.visibility.toAssetVisibility()), visibility: Value(asset.visibility.toAssetVisibility()),
livePhotoVideoId: Value(asset.livePhotoVideoId), livePhotoVideoId: Value(asset.livePhotoVideoId),
stackId: Value(asset.stackId), stackId: Value(asset.stackId),
libraryId: Value(asset.libraryId),
); );
batch.insert( batch.insert(

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -14786,6 +14786,10 @@
"isFavorite": { "isFavorite": {
"type": "boolean" "type": "boolean"
}, },
"libraryId": {
"nullable": true,
"type": "string"
},
"livePhotoVideoId": { "livePhotoVideoId": {
"nullable": true, "nullable": true,
"type": "string" "type": "string"
@ -14832,6 +14836,7 @@
"fileModifiedAt", "fileModifiedAt",
"id", "id",
"isFavorite", "isFavorite",
"libraryId",
"livePhotoVideoId", "livePhotoVideoId",
"localDateTime", "localDateTime",
"originalFileName", "originalFileName",

View file

@ -354,6 +354,7 @@ export const columns = {
'asset.duration', 'asset.duration',
'asset.livePhotoVideoId', 'asset.livePhotoVideoId',
'asset.stackId', 'asset.stackId',
'asset.libraryId',
], ],
syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'],
syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'],

View file

@ -115,6 +115,7 @@ export class SyncAssetV1 {
visibility!: AssetVisibility; visibility!: AssetVisibility;
livePhotoVideoId!: string | null; livePhotoVideoId!: string | null;
stackId!: string | null; stackId!: string | null;
libraryId!: string | null;
} }
@ExtraModel() @ExtraModel()

View file

@ -66,6 +66,7 @@ select
"asset"."duration", "asset"."duration",
"asset"."livePhotoVideoId", "asset"."livePhotoVideoId",
"asset"."stackId", "asset"."stackId",
"asset"."libraryId",
"asset"."updateId" "asset"."updateId"
from from
"asset" "asset"
@ -95,6 +96,7 @@ select
"asset"."duration", "asset"."duration",
"asset"."livePhotoVideoId", "asset"."livePhotoVideoId",
"asset"."stackId", "asset"."stackId",
"asset"."libraryId",
"asset"."updateId" "asset"."updateId"
from from
"asset" "asset"
@ -357,6 +359,7 @@ select
"asset"."duration", "asset"."duration",
"asset"."livePhotoVideoId", "asset"."livePhotoVideoId",
"asset"."stackId", "asset"."stackId",
"asset"."libraryId",
"asset"."updateId" "asset"."updateId"
from from
"asset" "asset"
@ -605,6 +608,7 @@ select
"asset"."duration", "asset"."duration",
"asset"."livePhotoVideoId", "asset"."livePhotoVideoId",
"asset"."stackId", "asset"."stackId",
"asset"."libraryId",
"asset"."updateId" "asset"."updateId"
from from
"asset" "asset"
@ -652,6 +656,7 @@ select
"asset"."duration", "asset"."duration",
"asset"."livePhotoVideoId", "asset"."livePhotoVideoId",
"asset"."stackId", "asset"."stackId",
"asset"."libraryId",
"asset"."updateId" "asset"."updateId"
from from
"asset" "asset"

View file

@ -382,6 +382,7 @@ export class JobService extends BaseService {
visibility: asset.visibility, visibility: asset.visibility,
livePhotoVideoId: asset.livePhotoVideoId, livePhotoVideoId: asset.livePhotoVideoId,
stackId: asset.stackId, stackId: asset.stackId,
libraryId: asset.libraryId,
}, },
exif: { exif: {
assetId: exif.assetId, assetId: exif.assetId,

View file

@ -38,6 +38,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
duration: '0:10:00.00000', duration: '0:10:00.00000',
livePhotoVideoId: null, livePhotoVideoId: null,
stackId: null, stackId: null,
libraryId: null,
}); });
const { album } = await ctx.newAlbum({ ownerId: user2.id }); const { album } = await ctx.newAlbum({ ownerId: user2.id });
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
@ -64,6 +65,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
duration: asset.duration, duration: asset.duration,
livePhotoVideoId: asset.livePhotoVideoId, livePhotoVideoId: asset.livePhotoVideoId,
stackId: asset.stackId, stackId: asset.stackId,
libraryId: asset.libraryId,
}, },
type: SyncEntityType.AlbumAssetV1, type: SyncEntityType.AlbumAssetV1,
}, },

View file

@ -36,6 +36,7 @@ describe(SyncEntityType.AssetV1, () => {
localDateTime: date, localDateTime: date,
deletedAt: null, deletedAt: null,
duration: '0:10:00.00000', duration: '0:10:00.00000',
libraryId: null,
}); });
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
@ -59,6 +60,7 @@ describe(SyncEntityType.AssetV1, () => {
duration: asset.duration, duration: asset.duration,
stackId: null, stackId: null,
livePhotoVideoId: null, livePhotoVideoId: null,
libraryId: asset.libraryId,
}, },
type: 'AssetV1', type: 'AssetV1',
}, },

View file

@ -40,6 +40,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
localDateTime: date, localDateTime: date,
deletedAt: null, deletedAt: null,
duration: '0:10:00.00000', duration: '0:10:00.00000',
libraryId: null,
}); });
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
@ -65,6 +66,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
duration: asset.duration, duration: asset.duration,
stackId: null, stackId: null,
livePhotoVideoId: null, livePhotoVideoId: null,
libraryId: asset.libraryId,
}, },
type: SyncEntityType.PartnerAssetV1, type: SyncEntityType.PartnerAssetV1,
}, },