added popular sites module
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:weblibre/features/popular_sites/data/database/daos/site.drift.dart';
|
||||
import 'package:weblibre/features/popular_sites/data/database/database.dart';
|
||||
import 'package:weblibre/features/popular_sites/data/database/definitions.drift.dart';
|
||||
|
||||
@DriftAccessor()
|
||||
class SiteDao extends DatabaseAccessor<SitesDatabase> with $SiteDaoMixin {
|
||||
SiteDao(super.db);
|
||||
|
||||
/// Popular-domain prefix completions for the omnibar, ordered by Tranco
|
||||
/// popularity (densely re-ranked at build time, so `rank` is a contiguous
|
||||
/// 1..N order). [prefix] is matched case-insensitively against the start of
|
||||
/// the registrable domain.
|
||||
///
|
||||
/// LIKE metacharacters (`%`, `_`, and the `\` escape char itself) in the
|
||||
/// user input are escaped so they match literally rather than acting as
|
||||
/// wildcards — see the `ESCAPE '\'` clause on `searchSitesByPrefix`.
|
||||
Selectable<Site> searchByPrefix(String prefix, {int limit = 8}) {
|
||||
final escaped = prefix
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll(r'\', r'\\')
|
||||
.replaceAll('%', r'\%')
|
||||
.replaceAll('_', r'\_');
|
||||
|
||||
return db.definitionsDrift.searchSitesByPrefix(
|
||||
pattern: '$escaped%',
|
||||
limit: limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/popular_sites/data/database/database.dart'
|
||||
as i1;
|
||||
|
||||
mixin $SiteDaoMixin on i0.DatabaseAccessor<i1.SitesDatabase> {
|
||||
SiteDaoManager get managers => SiteDaoManager(this);
|
||||
}
|
||||
|
||||
class SiteDaoManager {
|
||||
final $SiteDaoMixin _db;
|
||||
SiteDaoManager(this._db);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_dev/api/migrations_native.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:weblibre/features/popular_sites/data/database/daos/site.dart';
|
||||
import 'package:weblibre/features/popular_sites/data/database/database.drift.dart';
|
||||
|
||||
@DriftDatabase(include: {'definitions.drift'}, daos: [SiteDao])
|
||||
class SitesDatabase extends $SitesDatabase {
|
||||
@override
|
||||
final int schemaVersion = 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
beforeOpen: (details) async {
|
||||
if (kDebugMode) {
|
||||
await validateDatabaseSchema();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
SitesDatabase(super.e);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/popular_sites/data/database/definitions.drift.dart'
|
||||
as i1;
|
||||
import 'package:weblibre/features/popular_sites/data/database/daos/site.dart'
|
||||
as i2;
|
||||
import 'package:weblibre/features/popular_sites/data/database/database.dart'
|
||||
as i3;
|
||||
import 'package:drift/internal/modular.dart' as i4;
|
||||
import 'package:sqlite3/common.dart' as i5;
|
||||
|
||||
abstract class $SitesDatabase extends i0.GeneratedDatabase {
|
||||
$SitesDatabase(i0.QueryExecutor e) : super(e);
|
||||
$SitesDatabaseManager get managers => $SitesDatabaseManager(this);
|
||||
late final i1.Sites sites = i1.Sites(this);
|
||||
late final i2.SiteDao siteDao = i2.SiteDao(this as i3.SitesDatabase);
|
||||
i1.DefinitionsDrift get definitionsDrift => i4.ReadDatabaseContainer(
|
||||
this,
|
||||
).accessor<i1.DefinitionsDrift>(i1.DefinitionsDrift.new);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [sites];
|
||||
}
|
||||
|
||||
class $SitesDatabaseManager {
|
||||
final $SitesDatabase _db;
|
||||
$SitesDatabaseManager(this._db);
|
||||
i1.$SitesTableManager get sites => i1.$SitesTableManager(_db, _db.sites);
|
||||
}
|
||||
|
||||
extension DefineFunctions on i5.CommonDatabase {
|
||||
void defineFunctions({
|
||||
required String Function(int, String?) lexoRankNext,
|
||||
required String Function(int, String?) lexoRankPrevious,
|
||||
required String Function(String?, String?) lexoRankReorderAfter,
|
||||
required String Function(String?, String?) lexoRankReorderBefore,
|
||||
required int Function() generateContentHash,
|
||||
required bool Function(String?) urlIndexable,
|
||||
required String Function(String?) urlCanonical,
|
||||
required String Function(String?) urlHost,
|
||||
required String Function(String?) urlPath,
|
||||
}) {
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_next',
|
||||
argumentCount: const i5.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as int;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankNext(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_previous',
|
||||
argumentCount: const i5.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as int;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankPrevious(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_reorder_after',
|
||||
argumentCount: const i5.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankReorderAfter(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'lexo_rank_reorder_before',
|
||||
argumentCount: const i5.AllowedArgumentCount(2),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
final arg1 = args[1] as String?;
|
||||
return lexoRankReorderBefore(arg0, arg1);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'generate_content_hash',
|
||||
argumentCount: const i5.AllowedArgumentCount(0),
|
||||
function: (args) {
|
||||
return generateContentHash();
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'url_indexable',
|
||||
argumentCount: const i5.AllowedArgumentCount(1),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
return urlIndexable(arg0);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'url_canonical',
|
||||
argumentCount: const i5.AllowedArgumentCount(1),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
return urlCanonical(arg0);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'url_host',
|
||||
argumentCount: const i5.AllowedArgumentCount(1),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
return urlHost(arg0);
|
||||
},
|
||||
);
|
||||
createFunction(
|
||||
functionName: 'url_path',
|
||||
argumentCount: const i5.AllowedArgumentCount(1),
|
||||
function: (args) {
|
||||
final arg0 = args[0] as String?;
|
||||
return urlPath(arg0);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE sites (
|
||||
domain TEXT NOT NULL PRIMARY KEY,
|
||||
rank INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Prefix completion ordered by popularity. The caller escapes LIKE
|
||||
-- metacharacters in the user input and appends '%', so `ESCAPE '\'` makes any
|
||||
-- literal % / _ the user typed match literally instead of as wildcards.
|
||||
searchSitesByPrefix:
|
||||
SELECT * FROM sites
|
||||
WHERE domain LIKE :pattern ESCAPE '\'
|
||||
ORDER BY rank ASC
|
||||
LIMIT :limit;
|
||||
@@ -0,0 +1,334 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:weblibre/features/popular_sites/data/database/definitions.drift.dart'
|
||||
as i1;
|
||||
import 'package:drift/internal/modular.dart' as i2;
|
||||
|
||||
typedef $SitesCreateCompanionBuilder =
|
||||
i1.SitesCompanion Function({
|
||||
required String domain,
|
||||
required int rank,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
typedef $SitesUpdateCompanionBuilder =
|
||||
i1.SitesCompanion Function({
|
||||
i0.Value<String> domain,
|
||||
i0.Value<int> rank,
|
||||
i0.Value<int> rowid,
|
||||
});
|
||||
|
||||
class $SitesFilterComposer extends i0.Composer<i0.GeneratedDatabase, i1.Sites> {
|
||||
$SitesFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get domain => $composableBuilder(
|
||||
column: $table.domain,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
|
||||
i0.ColumnFilters<int> get rank => $composableBuilder(
|
||||
column: $table.rank,
|
||||
builder: (column) => i0.ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $SitesOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.Sites> {
|
||||
$SitesOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get domain => $composableBuilder(
|
||||
column: $table.domain,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i0.ColumnOrderings<int> get rank => $composableBuilder(
|
||||
column: $table.rank,
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $SitesAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.Sites> {
|
||||
$SitesAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get domain =>
|
||||
$composableBuilder(column: $table.domain, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<int> get rank =>
|
||||
$composableBuilder(column: $table.rank, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $SitesTableManager
|
||||
extends
|
||||
i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.Sites,
|
||||
i1.Site,
|
||||
i1.$SitesFilterComposer,
|
||||
i1.$SitesOrderingComposer,
|
||||
i1.$SitesAnnotationComposer,
|
||||
$SitesCreateCompanionBuilder,
|
||||
$SitesUpdateCompanionBuilder,
|
||||
(i1.Site, i0.BaseReferences<i0.GeneratedDatabase, i1.Sites, i1.Site>),
|
||||
i1.Site,
|
||||
i0.PrefetchHooks Function()
|
||||
> {
|
||||
$SitesTableManager(i0.GeneratedDatabase db, i1.Sites table)
|
||||
: super(
|
||||
i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$SitesFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$SitesOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$SitesAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
i0.Value<String> domain = const i0.Value.absent(),
|
||||
i0.Value<int> rank = const i0.Value.absent(),
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.SitesCompanion(domain: domain, rank: rank, rowid: rowid),
|
||||
createCompanionCallback:
|
||||
({
|
||||
required String domain,
|
||||
required int rank,
|
||||
i0.Value<int> rowid = const i0.Value.absent(),
|
||||
}) => i1.SitesCompanion.insert(
|
||||
domain: domain,
|
||||
rank: rank,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $SitesProcessedTableManager =
|
||||
i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.Sites,
|
||||
i1.Site,
|
||||
i1.$SitesFilterComposer,
|
||||
i1.$SitesOrderingComposer,
|
||||
i1.$SitesAnnotationComposer,
|
||||
$SitesCreateCompanionBuilder,
|
||||
$SitesUpdateCompanionBuilder,
|
||||
(i1.Site, i0.BaseReferences<i0.GeneratedDatabase, i1.Sites, i1.Site>),
|
||||
i1.Site,
|
||||
i0.PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class Sites extends i0.Table with i0.TableInfo<Sites, i1.Site> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
Sites(this.attachedDatabase, [this._alias]);
|
||||
late final i0.GeneratedColumn<String> domain = i0.GeneratedColumn<String>(
|
||||
'domain',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL PRIMARY KEY',
|
||||
);
|
||||
late final i0.GeneratedColumn<int> rank = i0.GeneratedColumn<int>(
|
||||
'rank',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [domain, rank];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'sites';
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {domain};
|
||||
@override
|
||||
i1.Site map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.Site(
|
||||
domain: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}domain'],
|
||||
)!,
|
||||
rank: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int,
|
||||
data['${effectivePrefix}rank'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Sites createAlias(String alias) {
|
||||
return Sites(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class Site extends i0.DataClass implements i0.Insertable<i1.Site> {
|
||||
final String domain;
|
||||
final int rank;
|
||||
const Site({required this.domain, required this.rank});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['domain'] = i0.Variable<String>(domain);
|
||||
map['rank'] = i0.Variable<int>(rank);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory Site.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
i0.ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return Site(
|
||||
domain: serializer.fromJson<String>(json['domain']),
|
||||
rank: serializer.fromJson<int>(json['rank']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'domain': serializer.toJson<String>(domain),
|
||||
'rank': serializer.toJson<int>(rank),
|
||||
};
|
||||
}
|
||||
|
||||
i1.Site copyWith({String? domain, int? rank}) =>
|
||||
i1.Site(domain: domain ?? this.domain, rank: rank ?? this.rank);
|
||||
Site copyWithCompanion(i1.SitesCompanion data) {
|
||||
return Site(
|
||||
domain: data.domain.present ? data.domain.value : this.domain,
|
||||
rank: data.rank.present ? data.rank.value : this.rank,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('Site(')
|
||||
..write('domain: $domain, ')
|
||||
..write('rank: $rank')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(domain, rank);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.Site &&
|
||||
other.domain == this.domain &&
|
||||
other.rank == this.rank);
|
||||
}
|
||||
|
||||
class SitesCompanion extends i0.UpdateCompanion<i1.Site> {
|
||||
final i0.Value<String> domain;
|
||||
final i0.Value<int> rank;
|
||||
final i0.Value<int> rowid;
|
||||
const SitesCompanion({
|
||||
this.domain = const i0.Value.absent(),
|
||||
this.rank = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
});
|
||||
SitesCompanion.insert({
|
||||
required String domain,
|
||||
required int rank,
|
||||
this.rowid = const i0.Value.absent(),
|
||||
}) : domain = i0.Value(domain),
|
||||
rank = i0.Value(rank);
|
||||
static i0.Insertable<i1.Site> custom({
|
||||
i0.Expression<String>? domain,
|
||||
i0.Expression<int>? rank,
|
||||
i0.Expression<int>? rowid,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (domain != null) 'domain': domain,
|
||||
if (rank != null) 'rank': rank,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
i1.SitesCompanion copyWith({
|
||||
i0.Value<String>? domain,
|
||||
i0.Value<int>? rank,
|
||||
i0.Value<int>? rowid,
|
||||
}) {
|
||||
return i1.SitesCompanion(
|
||||
domain: domain ?? this.domain,
|
||||
rank: rank ?? this.rank,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (domain.present) {
|
||||
map['domain'] = i0.Variable<String>(domain.value);
|
||||
}
|
||||
if (rank.present) {
|
||||
map['rank'] = i0.Variable<int>(rank.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('SitesCompanion(')
|
||||
..write('domain: $domain, ')
|
||||
..write('rank: $rank, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class DefinitionsDrift extends i2.ModularAccessor {
|
||||
DefinitionsDrift(i0.GeneratedDatabase db) : super(db);
|
||||
i0.Selectable<i1.Site> searchSitesByPrefix({
|
||||
required String pattern,
|
||||
required int limit,
|
||||
}) {
|
||||
return customSelect(
|
||||
'SELECT * FROM sites WHERE domain LIKE ?1 ESCAPE \'\\\' ORDER BY rank ASC LIMIT ?2',
|
||||
variables: [i0.Variable<String>(pattern), i0.Variable<int>(limit)],
|
||||
readsFrom: {sites},
|
||||
).asyncMap(sites.mapFromRow);
|
||||
}
|
||||
|
||||
i1.Sites get sites =>
|
||||
i2.ReadDatabaseContainer(attachedDatabase).resultSet<i1.Sites>('sites');
|
||||
}
|
||||
Reference in New Issue
Block a user