Add db api to query summary of all the files

This commit is contained in:
Ming Ming 2024-03-18 00:33:26 +08:00
parent 99c8448bde
commit 97b3e99aa7
5 changed files with 173 additions and 0 deletions

View file

@ -126,6 +126,31 @@ class DbLocationGroupResult {
final List<DbLocationGroup> countryCode;
}
@toString
class DbFilesSummaryItem {
const DbFilesSummaryItem({
required this.count,
});
@override
String toString() => _$toString();
final int count;
}
@toString
class DbFilesSummary {
const DbFilesSummary({
required this.items,
});
@override
String toString() => _$toString();
@Format(r"{length: ${$?.length}}")
final Map<DateTime, DbFilesSummaryItem> items;
}
@npLog
abstract class NpDb {
factory NpDb() => NpDbSqlite();
@ -321,9 +346,23 @@ abstract class NpDb {
String? location,
bool? isFavorite,
List<String>? mimes,
int? offset,
int? limit,
});
/// Summarize files matching some specific requirements
///
/// See [getFileDescriptors]
///
/// Returned data are sorted by [DbFileDescriptor.bestDateTime] in descending
/// order
Future<DbFilesSummary> getFilesSummary({
required DbAccount account,
List<String>? includeRelativeRoots,
List<String>? excludeRelativeRoots,
List<String>? mimes,
});
Future<DbLocationGroupResult> groupLocations({
required DbAccount account,
List<String>? includeRelativeRoots,

View file

@ -37,3 +37,17 @@ extension _$DbLocationGroupResultToString on DbLocationGroupResult {
return "DbLocationGroupResult {name: [length: ${name.length}], admin1: [length: ${admin1.length}], admin2: [length: ${admin2.length}], countryCode: [length: ${countryCode.length}]}";
}
}
extension _$DbFilesSummaryItemToString on DbFilesSummaryItem {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "DbFilesSummaryItem {count: $count}";
}
}
extension _$DbFilesSummaryToString on DbFilesSummary {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "DbFilesSummary {items: {length: ${items.length}}}";
}
}

View file

@ -40,6 +40,14 @@ class FileDescriptor {
final DateTime bestDateTime;
}
class CountFileGroupsByDateResult {
const CountFileGroupsByDateResult({
required this.dateCount,
});
final Map<DateTime, int> dateCount;
}
extension SqliteDbFileExtension on SqliteDb {
/// Return files located inside [dir]
Future<List<CompleteFile>> queryFilesByDirKey({
@ -574,6 +582,62 @@ extension SqliteDbFileExtension on SqliteDb {
return query.map((r) => r.read(files.fileId)!).getSingleOrNull();
}
/// Count number of files per date
Future<CountFileGroupsByDateResult> countFileGroupsByDate({
required ByAccount account,
List<String>? includeRelativeRoots,
List<String>? excludeRelativeRoots,
List<String>? mimes,
}) async {
_log.info(
"[countFileGroupsByDate] "
"includeRelativeRoots: $includeRelativeRoots, "
"excludeRelativeRoots: $excludeRelativeRoots, "
"mimes: $mimes",
);
final count = countAll();
final localDate = accountFiles.bestDateTime
.modify(const DateTimeModifier.localTime())
.date;
final query = _queryFiles().let((q) {
q
..setQueryMode(
FilesQueryMode.expression,
expressions: [localDate, count],
)
..setAccount(account);
if (includeRelativeRoots != null) {
if (includeRelativeRoots.none((p) => p.isEmpty)) {
for (final r in includeRelativeRoots) {
q.byOrRelativePathPattern("$r/%");
}
}
}
return q.build();
});
if (excludeRelativeRoots != null) {
for (final r in excludeRelativeRoots) {
query.where(accountFiles.relativePath.like("$r/%").not());
}
}
if (mimes != null) {
query.where(files.contentType.isIn(mimes));
} else {
query.where(files.isCollection.isNotValue(true));
}
query
..orderBy([OrderingTerm.desc(accountFiles.bestDateTime)])
..groupBy([localDate]);
final results = await query
.map((r) => MapEntry<DateTime, int>(
DateTime.parse(r.read(localDate)!),
r.read(count)!,
))
.get();
return CountFileGroupsByDateResult(dateCount: results.toMap());
}
/// Update Db files
///
/// Return a list of files that are not yet inserted to the DB (thus not

View file

@ -461,6 +461,27 @@ class NpDbSqlite implements NpDb {
return sqlObjs.toDbFileDescriptors();
}
@override
Future<DbFilesSummary> getFilesSummary({
required DbAccount account,
List<String>? includeRelativeRoots,
List<String>? excludeRelativeRoots,
List<String>? mimes,
}) async {
final result = await _db.use((db) async {
return await db.countFileGroupsByDate(
account: ByAccount.db(account),
includeRelativeRoots: includeRelativeRoots,
excludeRelativeRoots: excludeRelativeRoots,
mimes: mimes,
);
});
return DbFilesSummary(
items: result.dateCount
.map((key, value) => MapEntry(key, DbFilesSummaryItem(count: value))),
);
}
@override
Future<DbLocationGroupResult> groupLocations({
required DbAccount account,

View file

@ -8,6 +8,7 @@ import '../test_util.dart' as util;
void main() {
group("database.SqliteDbFileExtension", () {
test("cleanUpDanglingFiles", _cleanUpDanglingFiles);
test("countFileGroupsByDate", _countFileGroupsByDate);
});
}
@ -46,3 +47,37 @@ Future<void> _cleanUpDanglingFiles() async {
[0, 1],
);
}
Future<void> _countFileGroupsByDate() async {
final account = util.buildAccount();
final files = (util.FilesBuilder()
..addDir("admin")
..addJpeg(
"admin/test1.jpg",
lastModified: DateTime(2024, 1, 2, 3, 4, 5),
)
..addJpeg(
"admin/test2.jpg",
lastModified: DateTime(2024, 1, 2, 4, 5, 6),
)
..addJpeg(
"admin/test3.jpg",
lastModified: DateTime(2024, 1, 3, 4, 5, 6),
))
.build();
final db = util.buildTestDb();
addTearDown(() => db.close());
await db.transaction(() async {
await db.insertAccounts([account]);
await util.insertFiles(db, account, files);
});
final result = await db.countFileGroupsByDate(account: ByAccount.db(account));
expect(
result.dateCount,
{
DateTime(2024, 1, 2): 2,
DateTime(2024, 1, 3): 1,
},
);
}