swith to bang composite key

This commit is contained in:
Fabian Freund
2025-09-29 12:51:35 +02:00
parent d215aad3e4
commit 8c05eff173
29 changed files with 433 additions and 700 deletions
@@ -48,8 +48,12 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
);
}
SingleOrNullSelectable<BangData> getBangData(String trigger) {
return select(db.bangDataView)..where((t) => t.trigger.equals(trigger));
SingleOrNullSelectable<BangData> getBangData(
BangGroup group,
String trigger,
) {
return select(db.bangDataView)
..where((t) => t.group.equalsValue(group) & t.trigger.equals(trigger));
}
Selectable<BangData> getBangDataList({
@@ -102,10 +106,11 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
return selectable;
}
Future<int> increaseBangFrequency(String trigger) {
Future<int> increaseBangFrequency(BangGroup group, String trigger) {
return db.bangFrequency.insertOne(
BangFrequencyCompanion.insert(
trigger: trigger,
group: group,
frequency: 1,
lastUsed: DateTime.now(),
),
@@ -130,17 +135,23 @@ class BangDao extends DatabaseAccessor<BangDatabase> with $BangDaoMixin {
}
}
Future<int> addSearchEntry(String trigger, String searchQuery) {
Future<int> addSearchEntry(
BangGroup group,
String trigger,
String searchQuery,
) {
return db.bangHistory.insertOne(
BangHistoryCompanion.insert(
searchQuery: searchQuery,
trigger: trigger,
group: group,
searchDate: DateTime.now(),
),
onConflict: DoUpdate(
target: [db.bangHistory.searchQuery],
(old) => BangHistoryCompanion(
trigger: Value(trigger),
group: Value(group),
searchDate: Value(DateTime.now()),
),
),
@@ -55,8 +55,9 @@ class SyncDao extends DatabaseAccessor<BangDatabase> with $SyncDaoMixin {
});
}
Future<int> deleteBangs(Iterable<String> triggers) {
final statement = delete(db.bang)..where((t) => t.trigger.isIn(triggers));
Future<int> deleteBangs(BangGroup group, Iterable<String> triggers) {
final statement = delete(db.bang)
..where((t) => t.group.equalsValue(group) & t.trigger.isIn(triggers));
return statement.go();
}
@@ -89,7 +90,7 @@ class SyncDao extends DatabaseAccessor<BangDatabase> with $SyncDaoMixin {
.map((e) => remoteBangMap[e]!);
await db.transaction(() async {
await deleteBangs(removedBangs);
await deleteBangs(group, removedBangs);
await insertBangs(addedBangs);
await replaceBangs(changedBangs);
await upsertLastSyncOfGroup(group, syncTime);
@@ -5,14 +5,15 @@ import 'package:weblibre/features/bangs/data/models/bang.dart';
import 'package:weblibre/features/bangs/data/models/search_history_entry.dart';
CREATE TABLE bang (
"trigger" TEXT PRIMARY KEY NOT NULL,
"trigger" TEXT NOT NULL,
"group" ENUM(BangGroup) NOT NULL,
website_name TEXT NOT NULL,
domain TEXT NOT NULL,
url_template TEXT NOT NULL,
category TEXT,
sub_category TEXT,
format TEXT MAPPED BY `const BangFormatConverter()`
format TEXT MAPPED BY `const BangFormatConverter()`,
PRIMARY KEY ("trigger", "group")
) WITH Bang;
CREATE TABLE bang_sync (
@@ -21,15 +22,20 @@ CREATE TABLE bang_sync (
);
CREATE TABLE bang_frequency (
"trigger" TEXT PRIMARY KEY NOT NULL REFERENCES bang ("trigger") ON DELETE CASCADE,
"trigger" TEXT NOT NULL,
"group" ENUM(BangGroup) NOT NULL,
frequency INTEGER NOT NULL,
last_used DATETIME NOT NULL
last_used DATETIME NOT NULL,
PRIMARY KEY ("trigger", "group"),
FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group") ON DELETE CASCADE
);
CREATE TABLE bang_history (
search_query TEXT UNIQUE NOT NULL,
"trigger" TEXT NOT NULL REFERENCES bang ("trigger"),
search_date DATETIME NOT NULL
"trigger" TEXT NOT NULL,
"group" ENUM(BangGroup) NOT NULL,
search_date DATETIME NOT NULL,
FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group")
);
CREATE VIRTUAL TABLE bang_fts
@@ -48,7 +54,7 @@ CREATE VIEW bang_data_view WITH BangData AS
FROM
bang b
LEFT JOIN
bang_frequency bf ON b."trigger" = bf."trigger";
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group";
-- Triggers to keep the FTS index up to date.
CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN
@@ -90,7 +96,7 @@ queryBangs WITH BangData:
INNER JOIN
bang b ON b.rowid = fts.rowid
LEFT JOIN
bang_frequency bf ON b."trigger" = bf."trigger"
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group"
CROSS JOIN
weights
ORDER BY
@@ -114,7 +120,7 @@ queryBangsBasic WITH BangData:
INNER JOIN
bang b ON b.rowid = fts.rowid
LEFT JOIN
bang_frequency bf ON b."trigger" = bf."trigger"
bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group"
CROSS JOIN
weights
WHERE
@@ -160,4 +166,4 @@ evictHistoryEntries:
FROM bang_history
ORDER BY search_date DESC
LIMIT -1 OFFSET :limit
);
);
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,8 @@
*/
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:weblibre/features/bangs/data/models/bang.dart';
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
import 'package:weblibre/features/bangs/data/models/bang_key.dart';
import 'package:weblibre/features/geckoview/domain/entities/browser_icon.dart';
part 'bang_data.g.dart';
@@ -30,6 +32,9 @@ class BangData extends Bang {
final BrowserIcon? icon;
@override
BangGroup get group => super.group!;
BangData({
required super.websiteName,
required super.domain,
@@ -43,6 +48,8 @@ class BangData extends Bang {
this.icon,
}) : frequency = frequency ?? 0;
BangKey toKey() => BangKey(group: group, trigger: trigger);
@override
List<Object?> get hashParameters => [
...super.hashParameters,
@@ -0,0 +1,52 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
class BangKey {
final String trigger;
final BangGroup group;
const BangKey({required this.group, required this.trigger});
@override
String toString() {
return '${group.name}::$trigger';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BangKey &&
runtimeType == other.runtimeType &&
trigger == other.trigger &&
group == other.group;
@override
int get hashCode => Object.hash(trigger, group);
static BangKey? tryFromString(String key) {
try {
final [group, trigger] = key.split('::');
return BangKey(
group: BangGroup.values.firstWhere((g) => g.name == group),
trigger: trigger,
);
} catch (_) {
return null;
}
}
}
class BangKeyConverter implements JsonConverter<BangKey?, String?> {
const BangKeyConverter();
@override
BangKey? fromJson(String? json) {
return json.mapNotNull((json) => BangKey.tryFromString(json));
}
@override
String? toJson(BangKey? object) {
return object?.toString();
}
}
@@ -35,7 +35,7 @@ class BangDataSourceService extends _$BangDataSourceService {
@override
void build() {}
Future<Result<List<Bang>>> fetchRemoteBangs(Uri url, BangGroup? group) {
Future<Result<List<Bang>>> fetchRemoteBangs(Uri url, BangGroup group) {
return Result.fromAsync(() async {
return await compute((args) async {
final client = http.Client();
@@ -51,13 +51,8 @@ class BangDataSourceService extends _$BangDataSourceService {
}
}, [url.toString()]).then(
(json) => json.map((e) {
var bang = Bang.fromJson(e as Map<String, dynamic>);
if (group != null) {
bang = bang.copyWith.group(group);
}
return bang;
final bang = Bang.fromJson(e as Map<String, dynamic>);
return bang.copyWith.group(group);
}).toList(),
);
}, exceptionHandler: handleHttpError);
@@ -42,7 +42,7 @@ final class BangDataSourceServiceProvider
}
String _$bangDataSourceServiceHash() =>
r'2ff811ece417ae498dacc04a2fa963cffb34c8a9';
r'b1bd96bbd834de0f71af86d7f791d367d0a837be';
abstract class _$BangDataSourceService extends $Notifier<void> {
void build();