feat(server): dictionaries, loaded from files and searched
The server now holds dictionaries for everyone: `dictionary_entry` and `dictionary_source`, reference data owned by no one, never synced. It loads the dictionary files in its `dictionaries` folder at every start, those whose SHA-256 changed: in the background, each source replaced in one transaction, so searches meanwhile find the old entries; a file it can't read is logged and left out. All of JMdict loads in under half a minute. On an instance the folder is deploy/dictionaries, mounted read-only (compose.yaml); the server's README says how. DictionaryEndpoint (signed in): the dictionaries with their credits; a search (exact key, exact loose key, key starting with the query, loose key starting with it, each in the source's order; katakana as hiragana, macrons written out), some 150-450 ms over all of JMdict; and the entries with a label, counted and a page at a time (all JLPT N5 words). The curator now keeps spellings that aren't quite right apart, as `looseKeys` (romaji with long vowels typed once): "sushi" finds 寿司 before 数詞 (すうし). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
a875a91f40
commit
3013bc04b3
48 changed files with 16048 additions and 73 deletions
4
halkyon_learn_server/.gitignore
vendored
4
halkyon_learn_server/.gitignore
vendored
|
|
@ -20,3 +20,7 @@ config/firebase_service_account_key.json
|
|||
|
||||
# SQLite databases created by the ephemeral test database
|
||||
**/sp_*.db*
|
||||
|
||||
# Dictionary files: made by halkyon_learn_curator, not kept in git.
|
||||
/dictionaries/*.jsonl
|
||||
/dictionaries/*.jsonl.gz
|
||||
|
|
|
|||
|
|
@ -9,6 +9,26 @@ Start your server by running:
|
|||
|
||||
When you are finished, you can shut down the running server with `Q`.
|
||||
|
||||
## Dictionaries
|
||||
|
||||
The dictionaries people look words up in (JMdict, …) are reference data the
|
||||
server holds for everyone. They come as dictionary files, which
|
||||
`halkyon_learn_curator` makes from the sources' downloads (its README says how).
|
||||
At every start, the server loads the files in its `dictionaries` folder that
|
||||
changed since they were last loaded (by their SHA-256): in the background, a
|
||||
source at a time, and all at once, so searches meanwhile still find the old
|
||||
entries. A file it can't read is logged and left out.
|
||||
|
||||
- **Development:** put the file into `halkyon_learn_server/dictionaries/` and
|
||||
restart `serverpod start`.
|
||||
- **An instance:** put it into `deploy/dictionaries/` beside `compose.yaml`
|
||||
(mounted read-only as `/app/dictionaries`) and restart the server container.
|
||||
`DICTIONARIES_DIR` names another folder.
|
||||
|
||||
JMdict in English, with the JLPT levels, is about 17 MB gzipped and takes the
|
||||
server half a minute to load (219,000 entries). Its licence (CC BY-SA 4.0) asks
|
||||
for attribution: the app lists the credits each file carries.
|
||||
|
||||
## Licence
|
||||
|
||||
Copyright (C) 2026 The Halkyon Learn contributors
|
||||
|
|
|
|||
5
halkyon_learn_server/dictionaries/README.md
Normal file
5
halkyon_learn_server/dictionaries/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Dictionary files
|
||||
|
||||
Dictionary files (`*.jsonl`, `*.jsonl.gz`) for the server to load at start; see
|
||||
the server's README. They are downloads turned into files by `halkyon_learn_curator`,
|
||||
not kept in git.
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:halkyon_learn_server/src/auth/auth_config.dart';
|
||||
import 'package:halkyon_learn_server/src/dictionary/dictionary_loader.dart';
|
||||
import 'package:halkyon_learn_server/src/generated/serverpod.dart';
|
||||
import 'package:serverpod_auth_idp_server/core.dart';
|
||||
|
||||
|
|
@ -37,6 +41,17 @@ Future<void> run(List<String> args) async {
|
|||
.cron('47 3 * * *')
|
||||
.filePruning
|
||||
.pruneFiles();
|
||||
|
||||
// The dictionaries the operator put into the server's folder, those that
|
||||
// changed since; in the background, as loading takes a minute or two.
|
||||
unawaited(
|
||||
pod.withSession(
|
||||
(session) => loadDictionaries(
|
||||
session,
|
||||
Directory(Platform.environment['DICTIONARIES_DIR'] ?? 'dictionaries'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The identifier of the daily pruning.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
### Who made what a dictionary is drawn from, under which licence.
|
||||
class: DictionaryCredit
|
||||
fields:
|
||||
### What it is (`JMdict`).
|
||||
name: String
|
||||
### Who made it.
|
||||
by: String
|
||||
### Its licence, an SPDX identifier.
|
||||
license: String
|
||||
### Where it is described.
|
||||
url: String
|
||||
139
halkyon_learn_server/lib/src/dictionary/dictionary_endpoint.dart
Normal file
139
halkyon_learn_server/lib/src/dictionary/dictionary_endpoint.dart
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import 'package:halkyon_learn_server/src/dictionary/dictionary_search.dart';
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// The dictionaries the instance holds, to look words up in and to take
|
||||
/// words from by a label (all JLPT N5 words). Reference data: any signed-in
|
||||
/// person may read all of it.
|
||||
class DictionaryEndpoint extends Endpoint {
|
||||
@override
|
||||
bool get requireLogin => true;
|
||||
|
||||
/// The most a search returns.
|
||||
static const maxSearchResults = 50;
|
||||
|
||||
/// The most one page of labelled entries holds.
|
||||
static const maxPage = 500;
|
||||
|
||||
/// The dictionaries there are, with whose work they are.
|
||||
Future<List<DictionarySource>> dictionaries(Session session) =>
|
||||
DictionarySource.db.find(
|
||||
session,
|
||||
orderBy: (t) => t.language,
|
||||
);
|
||||
|
||||
/// The entries of the dictionary of [language] with meanings in
|
||||
/// [glossLanguage] that [query] finds, best first: those with a key that
|
||||
/// is the query, then a loose key that is (`sushi` for すうし), then a key
|
||||
/// starting with it, then a loose key; each in the dictionary's order. At
|
||||
/// most [limit] (30 unless given).
|
||||
Future<List<DictionaryHit>> search(
|
||||
Session session,
|
||||
String language,
|
||||
String glossLanguage,
|
||||
String query, {
|
||||
int? limit,
|
||||
}) async {
|
||||
final variants = queryVariants(language, query);
|
||||
if (variants.isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
final count = (limit ?? 30).clamp(1, maxSearchResults);
|
||||
Future<List<DictionaryEntry>> matching(String Function(String) pattern) =>
|
||||
DictionaryEntry.db.find(
|
||||
session,
|
||||
where: (t) {
|
||||
Expression<Object?> keys = Constant.bool(false);
|
||||
for (final variant in variants) {
|
||||
final like = pattern(likeEscaped(variant));
|
||||
keys = keys | t.searchKeys.like(like) | t.looseKeys.like(like);
|
||||
}
|
||||
return t.language.equals(language) &
|
||||
t.glossLanguage.equals(glossLanguage) &
|
||||
keys;
|
||||
},
|
||||
orderBy: (t) => t.rank,
|
||||
limit: count,
|
||||
);
|
||||
// SELECT … WHERE "searchKeys" LIKE '%|eat|%' OR "looseKeys" LIKE … ORDER
|
||||
// BY "rank" LIMIT …, then LIKE '%|eat%' for keys starting with it.
|
||||
final candidates = {
|
||||
for (final entry in [
|
||||
...await matching((key) => '%|$key|%'),
|
||||
...await matching((key) => '%|$key%'),
|
||||
])
|
||||
entry.id: entry,
|
||||
}.values.toList();
|
||||
int tier(DictionaryEntry entry) {
|
||||
bool any(String keys, String Function(String) pattern) => variants.any(
|
||||
(variant) => keys.contains(pattern(variant.replaceAll('|', ' '))),
|
||||
);
|
||||
return any(entry.searchKeys, (key) => '|$key|')
|
||||
? 0
|
||||
: any(entry.looseKeys, (key) => '|$key|')
|
||||
? 1
|
||||
: any(entry.searchKeys, (key) => '|$key')
|
||||
? 2
|
||||
: 3;
|
||||
}
|
||||
|
||||
final tiers = {for (final entry in candidates) entry.id: tier(entry)};
|
||||
candidates.sort(
|
||||
(a, b) => switch (tiers[a.id]!.compareTo(tiers[b.id]!)) {
|
||||
0 => a.rank.compareTo(b.rank),
|
||||
final order => order,
|
||||
},
|
||||
);
|
||||
return [for (final entry in candidates.take(count)) _hit(entry)];
|
||||
}
|
||||
|
||||
/// How many entries of the dictionary of [language] with meanings in
|
||||
/// [glossLanguage] carry [label].
|
||||
Future<int> countLabelled(
|
||||
Session session,
|
||||
String language,
|
||||
String glossLanguage,
|
||||
String label,
|
||||
) => DictionaryEntry.db.count(
|
||||
session,
|
||||
where: (t) =>
|
||||
t.language.equals(language) &
|
||||
t.glossLanguage.equals(glossLanguage) &
|
||||
t.labels.like('%|${likeEscaped(label)}|%'),
|
||||
);
|
||||
|
||||
/// The entries that carry [label], in the dictionary's order: [limit] of
|
||||
/// them (100 unless given, at most [maxPage]) after the first [offset].
|
||||
Future<List<DictionaryHit>> labelled(
|
||||
Session session,
|
||||
String language,
|
||||
String glossLanguage,
|
||||
String label, {
|
||||
int? offset,
|
||||
int? limit,
|
||||
}) async => [
|
||||
for (final entry in await DictionaryEntry.db.find(
|
||||
session,
|
||||
where: (t) =>
|
||||
t.language.equals(language) &
|
||||
t.glossLanguage.equals(glossLanguage) &
|
||||
t.labels.like('%|${likeEscaped(label)}|%'),
|
||||
orderBy: (t) => t.rank,
|
||||
offset: (offset ?? 0).clamp(0, 1 << 30),
|
||||
limit: (limit ?? 100).clamp(1, maxPage),
|
||||
))
|
||||
_hit(entry),
|
||||
];
|
||||
|
||||
DictionaryHit _hit(DictionaryEntry entry) => DictionaryHit(
|
||||
source: entry.source,
|
||||
sourceId: entry.sourceId,
|
||||
headword: entry.headword,
|
||||
reading: entry.reading,
|
||||
labels: [
|
||||
for (final label in entry.labels.split('|'))
|
||||
if (label.isNotEmpty) label,
|
||||
],
|
||||
entry: entry.entry,
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
### An entry of a dictionary the instance holds (JMdict, …): reference data for
|
||||
### everyone, owned by no one and never synced. The server loads it from a
|
||||
### dictionary file (dictionary_loader.dart; the format is the curator's README).
|
||||
### A card made from it copies what it needs and keeps the source and id only as
|
||||
### a soft reference, so loading a newer version never touches cards.
|
||||
class: DictionaryEntry
|
||||
table: dictionary_entry
|
||||
fields:
|
||||
### The source its file names (`jmdict`).
|
||||
source: String
|
||||
### Its id in the source.
|
||||
sourceId: String
|
||||
### The words' language, a BCP 47 tag (`ja`).
|
||||
language: String
|
||||
### The meanings' language (`en`).
|
||||
glossLanguage: String
|
||||
### How the word is shown.
|
||||
headword: String
|
||||
### How it is read; null when the headword is its own reading.
|
||||
reading: String?
|
||||
### Its order among the source's entries, lower first.
|
||||
rank: int
|
||||
### Its labels between bars (`|common|jlpt:n5|`), for LIKE to find one.
|
||||
labels: String
|
||||
### What a search matches, lower case, between bars
|
||||
### (`|食べる|たべる|taberu|eat|`).
|
||||
searchKeys: String
|
||||
### What a search matches after every searchKeys match: spellings people
|
||||
### type that aren't quite right (`sushi` for すうし), between bars too.
|
||||
looseKeys: String
|
||||
### The entry as cards take it: its JSON, as the file holds it.
|
||||
entry: String
|
||||
indexes:
|
||||
# An entry by its source and id; loading replaces one source at a time.
|
||||
dictionary_entry_source_idx:
|
||||
fields: source, glossLanguage, sourceId
|
||||
unique: true
|
||||
# A language's entries in their order, for what is picked by a label.
|
||||
dictionary_entry_rank_idx:
|
||||
fields: language, glossLanguage, rank
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
### A dictionary entry as the app receives it: what it shows and turns into a
|
||||
### card, without what only the search needs.
|
||||
class: DictionaryHit
|
||||
fields:
|
||||
### The source and the entry's id in it: a card keeps them, to find the
|
||||
### word again and not to add it twice.
|
||||
source: String
|
||||
sourceId: String
|
||||
headword: String
|
||||
### Null when the headword is its own reading.
|
||||
reading: String?
|
||||
### `common`, `jlpt:n5`, …
|
||||
labels: List<String>
|
||||
### The entry as cards take it, as JSON (the curator's README).
|
||||
entry: String
|
||||
174
halkyon_learn_server/lib/src/dictionary/dictionary_loader.dart
Normal file
174
halkyon_learn_server/lib/src/dictionary/dictionary_loader.dart
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:halkyon_learn_server/src/dictionary/dictionary_search.dart';
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// The dictionary file format the loader reads, and the newest version of
|
||||
/// it (the curator's README describes it).
|
||||
const dictionaryFormat = 'halkyon-dictionary';
|
||||
const dictionaryFormatVersion = 1;
|
||||
|
||||
/// How many entries go into the database at once.
|
||||
const _batch = 1000;
|
||||
|
||||
/// Loads every dictionary file in [directory] (`*.jsonl`, or gzipped
|
||||
/// `*.jsonl.gz`) that changed since it was loaded: the operator drops a
|
||||
/// file the curator wrote into the server's `dictionaries` folder, and the
|
||||
/// server loads it on its next start. A file that can't be read is logged
|
||||
/// and left out, the others still load. No folder, nothing to load.
|
||||
Future<void> loadDictionaries(Session session, Directory directory) async {
|
||||
if (!directory.existsSync()) {
|
||||
return;
|
||||
}
|
||||
final files = [
|
||||
for (final entity in directory.listSync())
|
||||
if (entity is File &&
|
||||
(entity.path.endsWith('.jsonl') || entity.path.endsWith('.jsonl.gz')))
|
||||
entity,
|
||||
]..sort((a, b) => a.path.compareTo(b.path));
|
||||
for (final file in files) {
|
||||
try {
|
||||
await loadDictionaryFile(session, file);
|
||||
} on Object catch (error, st) {
|
||||
session.log(
|
||||
'Loading the dictionary ${file.path} failed.',
|
||||
level: LogLevel.error,
|
||||
exception: error,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the dictionary [file] unless it is loaded as it is: its source's
|
||||
/// entries in its meaning language are replaced by the file's, all at once,
|
||||
/// so a search meanwhile finds the old ones. Returns whether it loaded.
|
||||
Future<bool> loadDictionaryFile(Session session, File file) async {
|
||||
final hash = (await sha256.bind(file.openRead()).first).toString();
|
||||
final lines = _lines(file);
|
||||
final header = await _header(await lines.first);
|
||||
final source = header['source']! as String;
|
||||
final glossLanguage = header['glossLanguage']! as String;
|
||||
final loaded = await DictionarySource.db.findFirstRow(
|
||||
session,
|
||||
where: (t) =>
|
||||
t.source.equals(source) & t.glossLanguage.equals(glossLanguage),
|
||||
);
|
||||
if (loaded?.fileHash == hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final watch = Stopwatch()..start();
|
||||
final language = header['language']! as String;
|
||||
final name = file.uri.pathSegments.last;
|
||||
var count = 0;
|
||||
await session.db.transaction((transaction) async {
|
||||
await DictionaryEntry.db.deleteWhere(
|
||||
session,
|
||||
where: (t) =>
|
||||
t.source.equals(source) & t.glossLanguage.equals(glossLanguage),
|
||||
transaction: transaction,
|
||||
);
|
||||
final batch = <DictionaryEntry>[];
|
||||
Future<void> flush() async {
|
||||
await DictionaryEntry.db.insert(
|
||||
session,
|
||||
batch,
|
||||
transaction: transaction,
|
||||
);
|
||||
count += batch.length;
|
||||
batch.clear();
|
||||
}
|
||||
|
||||
await for (final line in _lines(file).skip(1)) {
|
||||
if (line.trim().isEmpty) {
|
||||
continue;
|
||||
}
|
||||
final json = jsonDecode(line) as Map<String, Object?>;
|
||||
batch.add(
|
||||
DictionaryEntry(
|
||||
source: source,
|
||||
sourceId: json['id']! as String,
|
||||
language: language,
|
||||
glossLanguage: glossLanguage,
|
||||
headword: json['headword']! as String,
|
||||
reading: json['reading'] as String?,
|
||||
rank: json['rank']! as int,
|
||||
labels: barred((json['labels']! as List).cast<String>()),
|
||||
searchKeys: barred((json['keys']! as List).cast<String>()),
|
||||
looseKeys: barred(
|
||||
(json['looseKeys'] as List? ?? const []).cast<String>(),
|
||||
),
|
||||
entry: jsonEncode(json['entry']),
|
||||
),
|
||||
);
|
||||
if (batch.length == _batch) {
|
||||
await flush();
|
||||
}
|
||||
}
|
||||
await flush();
|
||||
|
||||
final row = DictionarySource(
|
||||
id: loaded?.id,
|
||||
source: source,
|
||||
sourceVersion: header['sourceVersion']! as String,
|
||||
language: language,
|
||||
glossLanguage: glossLanguage,
|
||||
license: header['license']! as String,
|
||||
credits: [
|
||||
for (final credit
|
||||
in (header['credits']! as List).cast<Map<String, Object?>>())
|
||||
DictionaryCredit.fromJson(credit),
|
||||
],
|
||||
entryCount: count,
|
||||
fileName: name,
|
||||
fileHash: hash,
|
||||
loadedAt: DateTime.now().toUtc(),
|
||||
);
|
||||
if (loaded == null) {
|
||||
await DictionarySource.db.insertRow(
|
||||
session,
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
} else {
|
||||
await DictionarySource.db.updateRow(
|
||||
session,
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
});
|
||||
session.log(
|
||||
'Loaded the dictionary $name: $count entries of $source '
|
||||
'(${header['sourceVersion']}) in ${watch.elapsed.inSeconds} s.',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// The lines of [file], unzipped when its name ends in `.gz`.
|
||||
Stream<String> _lines(File file) {
|
||||
final bytes = file.openRead();
|
||||
return (file.path.endsWith('.gz') ? bytes.transform(gzip.decoder) : bytes)
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter());
|
||||
}
|
||||
|
||||
/// The file's first line, if it is a header the loader understands.
|
||||
Future<Map<String, Object?>> _header(String line) async {
|
||||
final header = jsonDecode(line);
|
||||
if (header is! Map<String, Object?> || header['format'] != dictionaryFormat) {
|
||||
throw const FormatException('Not a dictionary file.');
|
||||
}
|
||||
final version = header['formatVersion'];
|
||||
if (version is! int || version > dictionaryFormatVersion) {
|
||||
throw FormatException(
|
||||
'Dictionary format $version; this server reads up to '
|
||||
'$dictionaryFormatVersion.',
|
||||
);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/// What a search for [query] in a dictionary of [language] looks for: the
|
||||
/// query as typed, in lower case, trimmed. In Japanese also with katakana as
|
||||
/// hiragana, and long vowels with macrons written out (`tōkyō` as
|
||||
/// `toukyou`), as the keys hold them. Empty for nothing to look for.
|
||||
///
|
||||
/// Everything else specific to a language is in the keys, which the curator
|
||||
/// made. Romaji in the Kunrei style (`si`, `tu`) isn't turned into Hepburn:
|
||||
/// meanings would turn with it (`time` into `chime`).
|
||||
List<String> queryVariants(String language, String query) {
|
||||
final typed = query.trim().toLowerCase().replaceAll(RegExp(r'\s+'), ' ');
|
||||
if (typed.isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
return {
|
||||
typed,
|
||||
if (language == 'ja') ...[_toHiragana(typed), _withoutMacrons(typed)],
|
||||
}.toList();
|
||||
}
|
||||
|
||||
/// [text] for a LIKE pattern: its wildcards and the escape character
|
||||
/// itself escaped, and the bars between keys taken out.
|
||||
String likeEscaped(String text) => text
|
||||
.replaceAll(r'\', r'\\')
|
||||
.replaceAll('%', r'\%')
|
||||
.replaceAll('_', r'\_')
|
||||
.replaceAll('|', ' ');
|
||||
|
||||
/// [values] between bars, as a dictionary entry's labels and search keys
|
||||
/// are kept, for LIKE to find one whole (`%|eat|%`) or by its start.
|
||||
String barred(Iterable<String> values) =>
|
||||
'|${[for (final value in values) value.replaceAll('|', ' ')].join('|')}|';
|
||||
|
||||
String _toHiragana(String text) => String.fromCharCodes([
|
||||
for (final code in text.runes)
|
||||
if (code >= 0x30A1 && code <= 0x30F6) code - 0x60 else code,
|
||||
]);
|
||||
|
||||
String _withoutMacrons(String text) => text
|
||||
.replaceAll('ā', 'aa')
|
||||
.replaceAll('ī', 'ii')
|
||||
.replaceAll('ū', 'uu')
|
||||
.replaceAll('ē', 'ee')
|
||||
.replaceAll('ō', 'ou');
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
### A dictionary the instance holds: one source in one meaning language, as
|
||||
### loaded from its file. The app lists the credits where it names its data
|
||||
### sources; the file's hash tells the loader whether it changed since.
|
||||
class: DictionarySource
|
||||
table: dictionary_source
|
||||
fields:
|
||||
### The source (`jmdict`).
|
||||
source: String
|
||||
### Its version (`3.6.2 (2026-10-05)`).
|
||||
sourceVersion: String
|
||||
### The words' language (`ja`).
|
||||
language: String
|
||||
### The meanings' language (`en`).
|
||||
glossLanguage: String
|
||||
### The data's licence, an SPDX identifier.
|
||||
license: String
|
||||
### Everything it is drawn from.
|
||||
credits: List<DictionaryCredit>
|
||||
### How many entries it has.
|
||||
entryCount: int
|
||||
### The file it was loaded from, and the file's SHA-256.
|
||||
fileName: String
|
||||
fileHash: String
|
||||
### When it was loaded, in UTC.
|
||||
loadedAt: DateTime
|
||||
indexes:
|
||||
dictionary_source_idx:
|
||||
fields: source, glossLanguage
|
||||
unique: true
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A file in a person's content, an image or a sound (ADR 0004). This row is synced;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
import '../auth/github_sign_in_refusal.dart' as _irgbwy1y;
|
||||
|
||||
/// Signing in with GitHub, or linking a GitHub account, was refused; nothing
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
import '../studyset/studyset.dart' as _ie5k1jc2;
|
||||
|
||||
/// A flashcard in a studyset. Its content lives in its revisions
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
import '../card/card.dart' as _i4926i81;
|
||||
|
||||
/// One version of a card's content, never changed once written (ADR 0001,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// Who made what a dictionary is drawn from, under which licence.
|
||||
abstract class DictionaryCredit
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
DictionaryCredit._({
|
||||
required this.name,
|
||||
required this.by,
|
||||
required this.license,
|
||||
required this.url,
|
||||
});
|
||||
|
||||
factory DictionaryCredit({
|
||||
required String name,
|
||||
required String by,
|
||||
required String license,
|
||||
required String url,
|
||||
}) = _DictionaryCreditImpl;
|
||||
|
||||
factory DictionaryCredit.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DictionaryCredit(
|
||||
name: jsonSerialization['name'] as String,
|
||||
by: jsonSerialization['by'] as String,
|
||||
license: jsonSerialization['license'] as String,
|
||||
url: jsonSerialization['url'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// What it is (`JMdict`).
|
||||
String name;
|
||||
|
||||
/// Who made it.
|
||||
String by;
|
||||
|
||||
/// Its licence, an SPDX identifier.
|
||||
String license;
|
||||
|
||||
/// Where it is described.
|
||||
String url;
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryCredit]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
DictionaryCredit copyWith({
|
||||
String? name,
|
||||
String? by,
|
||||
String? license,
|
||||
String? url,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DictionaryCredit',
|
||||
'name': name,
|
||||
'by': by,
|
||||
'license': license,
|
||||
'url': url,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DictionaryCredit',
|
||||
'name': name,
|
||||
'by': by,
|
||||
'license': license,
|
||||
'url': url,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _DictionaryCreditImpl extends DictionaryCredit {
|
||||
_DictionaryCreditImpl({
|
||||
required String name,
|
||||
required String by,
|
||||
required String license,
|
||||
required String url,
|
||||
}) : super._(
|
||||
name: name,
|
||||
by: by,
|
||||
license: license,
|
||||
url: url,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryCredit]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
DictionaryCredit copyWith({
|
||||
String? name,
|
||||
String? by,
|
||||
String? license,
|
||||
String? url,
|
||||
}) {
|
||||
return DictionaryCredit(
|
||||
name: name ?? this.name,
|
||||
by: by ?? this.by,
|
||||
license: license ?? this.license,
|
||||
url: url ?? this.url,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,851 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// An entry of a dictionary the instance holds (JMdict, …): reference data for
|
||||
/// everyone, owned by no one and never synced. The server loads it from a
|
||||
/// dictionary file (dictionary_loader.dart; the format is the curator's README).
|
||||
/// A card made from it copies what it needs and keeps the source and id only as
|
||||
/// a soft reference, so loading a newer version never touches cards.
|
||||
abstract class DictionaryEntry
|
||||
implements _is.TableRow<int?>, _is.ProtocolSerialization {
|
||||
DictionaryEntry._({
|
||||
this.id,
|
||||
required this.source,
|
||||
required this.sourceId,
|
||||
required this.language,
|
||||
required this.glossLanguage,
|
||||
required this.headword,
|
||||
this.reading,
|
||||
required this.rank,
|
||||
required this.labels,
|
||||
required this.searchKeys,
|
||||
required this.looseKeys,
|
||||
required this.entry,
|
||||
});
|
||||
|
||||
factory DictionaryEntry({
|
||||
int? id,
|
||||
required String source,
|
||||
required String sourceId,
|
||||
required String language,
|
||||
required String glossLanguage,
|
||||
required String headword,
|
||||
String? reading,
|
||||
required int rank,
|
||||
required String labels,
|
||||
required String searchKeys,
|
||||
required String looseKeys,
|
||||
required String entry,
|
||||
}) = _DictionaryEntryImpl;
|
||||
|
||||
factory DictionaryEntry.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DictionaryEntry(
|
||||
id: jsonSerialization['id'] as int?,
|
||||
source: jsonSerialization['source'] as String,
|
||||
sourceId: jsonSerialization['sourceId'] as String,
|
||||
language: jsonSerialization['language'] as String,
|
||||
glossLanguage: jsonSerialization['glossLanguage'] as String,
|
||||
headword: jsonSerialization['headword'] as String,
|
||||
reading: jsonSerialization['reading'] as String?,
|
||||
rank: jsonSerialization['rank'] as int,
|
||||
labels: jsonSerialization['labels'] as String,
|
||||
searchKeys: jsonSerialization['searchKeys'] as String,
|
||||
looseKeys: jsonSerialization['looseKeys'] as String,
|
||||
entry: jsonSerialization['entry'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
static final t = DictionaryEntryTable();
|
||||
|
||||
static const db = DictionaryEntryRepository._();
|
||||
|
||||
@override
|
||||
int? id;
|
||||
|
||||
/// The source its file names (`jmdict`).
|
||||
String source;
|
||||
|
||||
/// Its id in the source.
|
||||
String sourceId;
|
||||
|
||||
/// The words' language, a BCP 47 tag (`ja`).
|
||||
String language;
|
||||
|
||||
/// The meanings' language (`en`).
|
||||
String glossLanguage;
|
||||
|
||||
/// How the word is shown.
|
||||
String headword;
|
||||
|
||||
/// How it is read; null when the headword is its own reading.
|
||||
String? reading;
|
||||
|
||||
/// Its order among the source's entries, lower first.
|
||||
int rank;
|
||||
|
||||
/// Its labels between bars (`|common|jlpt:n5|`), for LIKE to find one.
|
||||
String labels;
|
||||
|
||||
/// What a search matches, lower case, between bars
|
||||
/// (`|食べる|たべる|taberu|eat|`).
|
||||
String searchKeys;
|
||||
|
||||
/// What a search matches after every searchKeys match: spellings people
|
||||
/// type that aren't quite right (`sushi` for すうし), between bars too.
|
||||
String looseKeys;
|
||||
|
||||
/// The entry as cards take it: its JSON, as the file holds it.
|
||||
String entry;
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryEntry]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
DictionaryEntry copyWith({
|
||||
int? id,
|
||||
String? source,
|
||||
String? sourceId,
|
||||
String? language,
|
||||
String? glossLanguage,
|
||||
String? headword,
|
||||
String? reading,
|
||||
int? rank,
|
||||
String? labels,
|
||||
String? searchKeys,
|
||||
String? looseKeys,
|
||||
String? entry,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DictionaryEntry',
|
||||
if (id != null) 'id': id,
|
||||
'source': source,
|
||||
'sourceId': sourceId,
|
||||
'language': language,
|
||||
'glossLanguage': glossLanguage,
|
||||
'headword': headword,
|
||||
if (reading != null) 'reading': reading,
|
||||
'rank': rank,
|
||||
'labels': labels,
|
||||
'searchKeys': searchKeys,
|
||||
'looseKeys': looseKeys,
|
||||
'entry': entry,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DictionaryEntry',
|
||||
if (id != null) 'id': id,
|
||||
'source': source,
|
||||
'sourceId': sourceId,
|
||||
'language': language,
|
||||
'glossLanguage': glossLanguage,
|
||||
'headword': headword,
|
||||
if (reading != null) 'reading': reading,
|
||||
'rank': rank,
|
||||
'labels': labels,
|
||||
'searchKeys': searchKeys,
|
||||
'looseKeys': looseKeys,
|
||||
'entry': entry,
|
||||
};
|
||||
}
|
||||
|
||||
static DictionaryEntryInclude include() {
|
||||
return DictionaryEntryInclude._();
|
||||
}
|
||||
|
||||
static DictionaryEntryIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
DictionaryEntryInclude? include,
|
||||
}) {
|
||||
return DictionaryEntryIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DictionaryEntryImpl extends DictionaryEntry {
|
||||
_DictionaryEntryImpl({
|
||||
int? id,
|
||||
required String source,
|
||||
required String sourceId,
|
||||
required String language,
|
||||
required String glossLanguage,
|
||||
required String headword,
|
||||
String? reading,
|
||||
required int rank,
|
||||
required String labels,
|
||||
required String searchKeys,
|
||||
required String looseKeys,
|
||||
required String entry,
|
||||
}) : super._(
|
||||
id: id,
|
||||
source: source,
|
||||
sourceId: sourceId,
|
||||
language: language,
|
||||
glossLanguage: glossLanguage,
|
||||
headword: headword,
|
||||
reading: reading,
|
||||
rank: rank,
|
||||
labels: labels,
|
||||
searchKeys: searchKeys,
|
||||
looseKeys: looseKeys,
|
||||
entry: entry,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryEntry]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
DictionaryEntry copyWith({
|
||||
Object? id = _Undefined,
|
||||
String? source,
|
||||
String? sourceId,
|
||||
String? language,
|
||||
String? glossLanguage,
|
||||
String? headword,
|
||||
Object? reading = _Undefined,
|
||||
int? rank,
|
||||
String? labels,
|
||||
String? searchKeys,
|
||||
String? looseKeys,
|
||||
String? entry,
|
||||
}) {
|
||||
return DictionaryEntry(
|
||||
id: id is int? ? id : this.id,
|
||||
source: source ?? this.source,
|
||||
sourceId: sourceId ?? this.sourceId,
|
||||
language: language ?? this.language,
|
||||
glossLanguage: glossLanguage ?? this.glossLanguage,
|
||||
headword: headword ?? this.headword,
|
||||
reading: reading is String? ? reading : this.reading,
|
||||
rank: rank ?? this.rank,
|
||||
labels: labels ?? this.labels,
|
||||
searchKeys: searchKeys ?? this.searchKeys,
|
||||
looseKeys: looseKeys ?? this.looseKeys,
|
||||
entry: entry ?? this.entry,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DictionaryEntryUpdateTable extends _is.UpdateTable<DictionaryEntryTable> {
|
||||
DictionaryEntryUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<String, String> source(String value) => _is.ColumnValue(
|
||||
table.source,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> sourceId(String value) => _is.ColumnValue(
|
||||
table.sourceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> language(String value) => _is.ColumnValue(
|
||||
table.language,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> glossLanguage(String value) =>
|
||||
_is.ColumnValue(
|
||||
table.glossLanguage,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> headword(String value) => _is.ColumnValue(
|
||||
table.headword,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> reading(String? value) => _is.ColumnValue(
|
||||
table.reading,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<int, int> rank(int value) => _is.ColumnValue(
|
||||
table.rank,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> labels(String value) => _is.ColumnValue(
|
||||
table.labels,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> searchKeys(String value) => _is.ColumnValue(
|
||||
table.searchKeys,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> looseKeys(String value) => _is.ColumnValue(
|
||||
table.looseKeys,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> entry(String value) => _is.ColumnValue(
|
||||
table.entry,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class DictionaryEntryTable extends _is.Table<int?> {
|
||||
DictionaryEntryTable({super.tableRelation})
|
||||
: super(tableName: 'dictionary_entry') {
|
||||
updateTable = DictionaryEntryUpdateTable(this);
|
||||
source = _is.ColumnString(
|
||||
'source',
|
||||
this,
|
||||
);
|
||||
sourceId = _is.ColumnString(
|
||||
'sourceId',
|
||||
this,
|
||||
);
|
||||
language = _is.ColumnString(
|
||||
'language',
|
||||
this,
|
||||
);
|
||||
glossLanguage = _is.ColumnString(
|
||||
'glossLanguage',
|
||||
this,
|
||||
);
|
||||
headword = _is.ColumnString(
|
||||
'headword',
|
||||
this,
|
||||
);
|
||||
reading = _is.ColumnString(
|
||||
'reading',
|
||||
this,
|
||||
);
|
||||
rank = _is.ColumnInt(
|
||||
'rank',
|
||||
this,
|
||||
);
|
||||
labels = _is.ColumnString(
|
||||
'labels',
|
||||
this,
|
||||
);
|
||||
searchKeys = _is.ColumnString(
|
||||
'searchKeys',
|
||||
this,
|
||||
);
|
||||
looseKeys = _is.ColumnString(
|
||||
'looseKeys',
|
||||
this,
|
||||
);
|
||||
entry = _is.ColumnString(
|
||||
'entry',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final DictionaryEntryUpdateTable updateTable;
|
||||
|
||||
/// The source its file names (`jmdict`).
|
||||
late final _is.ColumnString source;
|
||||
|
||||
/// Its id in the source.
|
||||
late final _is.ColumnString sourceId;
|
||||
|
||||
/// The words' language, a BCP 47 tag (`ja`).
|
||||
late final _is.ColumnString language;
|
||||
|
||||
/// The meanings' language (`en`).
|
||||
late final _is.ColumnString glossLanguage;
|
||||
|
||||
/// How the word is shown.
|
||||
late final _is.ColumnString headword;
|
||||
|
||||
/// How it is read; null when the headword is its own reading.
|
||||
late final _is.ColumnString reading;
|
||||
|
||||
/// Its order among the source's entries, lower first.
|
||||
late final _is.ColumnInt rank;
|
||||
|
||||
/// Its labels between bars (`|common|jlpt:n5|`), for LIKE to find one.
|
||||
late final _is.ColumnString labels;
|
||||
|
||||
/// What a search matches, lower case, between bars
|
||||
/// (`|食べる|たべる|taberu|eat|`).
|
||||
late final _is.ColumnString searchKeys;
|
||||
|
||||
/// What a search matches after every searchKeys match: spellings people
|
||||
/// type that aren't quite right (`sushi` for すうし), between bars too.
|
||||
late final _is.ColumnString looseKeys;
|
||||
|
||||
/// The entry as cards take it: its JSON, as the file holds it.
|
||||
late final _is.ColumnString entry;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
source,
|
||||
sourceId,
|
||||
language,
|
||||
glossLanguage,
|
||||
headword,
|
||||
reading,
|
||||
rank,
|
||||
labels,
|
||||
searchKeys,
|
||||
looseKeys,
|
||||
entry,
|
||||
];
|
||||
}
|
||||
|
||||
class DictionaryEntryInclude extends _is.IncludeObject {
|
||||
DictionaryEntryInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => DictionaryEntry.t;
|
||||
}
|
||||
|
||||
class DictionaryEntryIncludeList extends _is.IncludeList {
|
||||
DictionaryEntryIncludeList._({
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(DictionaryEntry.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => DictionaryEntry.t;
|
||||
}
|
||||
|
||||
class DictionaryEntryRepository {
|
||||
const DictionaryEntryRepository._();
|
||||
|
||||
/// Returns a list of [DictionaryEntry]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<DictionaryEntry>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<DictionaryEntry>(
|
||||
where: where?.call(DictionaryEntry.t),
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [DictionaryEntry] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<DictionaryEntry?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<DictionaryEntry>(
|
||||
where: where?.call(DictionaryEntry.t),
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [DictionaryEntry] by its [id] or null if no such row exists.
|
||||
Future<DictionaryEntry?> findById(
|
||||
_is.DatabaseSession session,
|
||||
int id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<DictionaryEntry>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [DictionaryEntry]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [DictionaryEntry]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionaryEntry> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<DictionaryEntry>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [DictionaryEntry] and returns the inserted row.
|
||||
///
|
||||
/// The returned [DictionaryEntry] will have its `id` field set.
|
||||
Future<DictionaryEntry> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionaryEntry row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<DictionaryEntry>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [DictionaryEntry]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [DictionaryEntry]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionaryEntry> rows, {
|
||||
required _is.ColumnSelections<DictionaryEntryTable> conflictColumns,
|
||||
_is.ColumnSelections<DictionaryEntryTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<DictionaryEntry>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(DictionaryEntry.t),
|
||||
updateColumns: updateColumns?.call(DictionaryEntry.t),
|
||||
updateWhere: updateWhere?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [DictionaryEntry] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [DictionaryEntry] will have its `id` field set.
|
||||
Future<DictionaryEntry?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionaryEntry row, {
|
||||
required _is.ColumnSelections<DictionaryEntryTable> conflictColumns,
|
||||
_is.ColumnSelections<DictionaryEntryTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<DictionaryEntry>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(DictionaryEntry.t),
|
||||
updateColumns: updateColumns?.call(DictionaryEntry.t),
|
||||
updateWhere: updateWhere?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [DictionaryEntry]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionaryEntry> rows, {
|
||||
_is.ColumnSelections<DictionaryEntryTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<DictionaryEntry>(
|
||||
rows,
|
||||
columns: columns?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [DictionaryEntry]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<DictionaryEntry> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionaryEntry row, {
|
||||
_is.ColumnSelections<DictionaryEntryTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<DictionaryEntry>(
|
||||
row,
|
||||
columns: columns?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [DictionaryEntry] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<DictionaryEntry?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
int id, {
|
||||
required _is.ColumnValueListBuilder<DictionaryEntryUpdateTable>
|
||||
columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<DictionaryEntry>(
|
||||
id,
|
||||
columnValues: columnValues(DictionaryEntry.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [DictionaryEntry]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<DictionaryEntryUpdateTable>
|
||||
columnValues,
|
||||
required _is.WhereExpressionBuilder<DictionaryEntryTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<DictionaryEntry>(
|
||||
columnValues: columnValues(DictionaryEntry.t.updateTable),
|
||||
where: where(DictionaryEntry.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [DictionaryEntry]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionaryEntry> rows, {
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<DictionaryEntry>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [DictionaryEntry].
|
||||
Future<DictionaryEntry> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionaryEntry row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<DictionaryEntry>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionaryEntry>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<DictionaryEntryTable> where,
|
||||
_is.OrderByBuilder<DictionaryEntryTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionaryEntryTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<DictionaryEntry>(
|
||||
where: where(DictionaryEntry.t),
|
||||
orderBy: orderBy?.call(DictionaryEntry.t),
|
||||
orderByList: orderByList?.call(DictionaryEntry.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionaryEntryTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<DictionaryEntry>(
|
||||
where: where?.call(DictionaryEntry.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [DictionaryEntry] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<DictionaryEntryTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<DictionaryEntry>(
|
||||
where: where(DictionaryEntry.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A dictionary entry as the app receives it: what it shows and turns into a
|
||||
/// card, without what only the search needs.
|
||||
abstract class DictionaryHit
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
DictionaryHit._({
|
||||
required this.source,
|
||||
required this.sourceId,
|
||||
required this.headword,
|
||||
this.reading,
|
||||
required this.labels,
|
||||
required this.entry,
|
||||
});
|
||||
|
||||
factory DictionaryHit({
|
||||
required String source,
|
||||
required String sourceId,
|
||||
required String headword,
|
||||
String? reading,
|
||||
required List<String> labels,
|
||||
required String entry,
|
||||
}) = _DictionaryHitImpl;
|
||||
|
||||
factory DictionaryHit.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DictionaryHit(
|
||||
source: jsonSerialization['source'] as String,
|
||||
sourceId: jsonSerialization['sourceId'] as String,
|
||||
headword: jsonSerialization['headword'] as String,
|
||||
reading: jsonSerialization['reading'] as String?,
|
||||
labels: _i5lsenq5.Protocol().deserialize<List<String>>(
|
||||
jsonSerialization['labels'],
|
||||
),
|
||||
entry: jsonSerialization['entry'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// The source and the entry's id in it: a card keeps them, to find the
|
||||
/// word again and not to add it twice.
|
||||
String source;
|
||||
|
||||
String sourceId;
|
||||
|
||||
String headword;
|
||||
|
||||
/// Null when the headword is its own reading.
|
||||
String? reading;
|
||||
|
||||
/// `common`, `jlpt:n5`, …
|
||||
List<String> labels;
|
||||
|
||||
/// The entry as cards take it, as JSON (the curator's README).
|
||||
String entry;
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryHit]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
DictionaryHit copyWith({
|
||||
String? source,
|
||||
String? sourceId,
|
||||
String? headword,
|
||||
String? reading,
|
||||
List<String>? labels,
|
||||
String? entry,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DictionaryHit',
|
||||
'source': source,
|
||||
'sourceId': sourceId,
|
||||
'headword': headword,
|
||||
if (reading != null) 'reading': reading,
|
||||
'labels': labels.toJson(),
|
||||
'entry': entry,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DictionaryHit',
|
||||
'source': source,
|
||||
'sourceId': sourceId,
|
||||
'headword': headword,
|
||||
if (reading != null) 'reading': reading,
|
||||
'labels': labels.toJson(),
|
||||
'entry': entry,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DictionaryHitImpl extends DictionaryHit {
|
||||
_DictionaryHitImpl({
|
||||
required String source,
|
||||
required String sourceId,
|
||||
required String headword,
|
||||
String? reading,
|
||||
required List<String> labels,
|
||||
required String entry,
|
||||
}) : super._(
|
||||
source: source,
|
||||
sourceId: sourceId,
|
||||
headword: headword,
|
||||
reading: reading,
|
||||
labels: labels,
|
||||
entry: entry,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DictionaryHit]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
DictionaryHit copyWith({
|
||||
String? source,
|
||||
String? sourceId,
|
||||
String? headword,
|
||||
Object? reading = _Undefined,
|
||||
List<String>? labels,
|
||||
String? entry,
|
||||
}) {
|
||||
return DictionaryHit(
|
||||
source: source ?? this.source,
|
||||
sourceId: sourceId ?? this.sourceId,
|
||||
headword: headword ?? this.headword,
|
||||
reading: reading is String? ? reading : this.reading,
|
||||
labels: labels ?? this.labels.map((e0) => e0).toList(),
|
||||
entry: entry ?? this.entry,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,832 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_server/src/generated/protocol.dart' as _i5lsenq5;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
import '../dictionary/dictionary_credit.dart' as _it7o1p0g;
|
||||
|
||||
/// A dictionary the instance holds: one source in one meaning language, as
|
||||
/// loaded from its file. The app lists the credits where it names its data
|
||||
/// sources; the file's hash tells the loader whether it changed since.
|
||||
abstract class DictionarySource
|
||||
implements _is.TableRow<int?>, _is.ProtocolSerialization {
|
||||
DictionarySource._({
|
||||
this.id,
|
||||
required this.source,
|
||||
required this.sourceVersion,
|
||||
required this.language,
|
||||
required this.glossLanguage,
|
||||
required this.license,
|
||||
required this.credits,
|
||||
required this.entryCount,
|
||||
required this.fileName,
|
||||
required this.fileHash,
|
||||
required this.loadedAt,
|
||||
});
|
||||
|
||||
factory DictionarySource({
|
||||
int? id,
|
||||
required String source,
|
||||
required String sourceVersion,
|
||||
required String language,
|
||||
required String glossLanguage,
|
||||
required String license,
|
||||
required List<_it7o1p0g.DictionaryCredit> credits,
|
||||
required int entryCount,
|
||||
required String fileName,
|
||||
required String fileHash,
|
||||
required DateTime loadedAt,
|
||||
}) = _DictionarySourceImpl;
|
||||
|
||||
factory DictionarySource.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DictionarySource(
|
||||
id: jsonSerialization['id'] as int?,
|
||||
source: jsonSerialization['source'] as String,
|
||||
sourceVersion: jsonSerialization['sourceVersion'] as String,
|
||||
language: jsonSerialization['language'] as String,
|
||||
glossLanguage: jsonSerialization['glossLanguage'] as String,
|
||||
license: jsonSerialization['license'] as String,
|
||||
credits: _i5lsenq5.Protocol()
|
||||
.deserialize<List<_it7o1p0g.DictionaryCredit>>(
|
||||
jsonSerialization['credits'],
|
||||
),
|
||||
entryCount: jsonSerialization['entryCount'] as int,
|
||||
fileName: jsonSerialization['fileName'] as String,
|
||||
fileHash: jsonSerialization['fileHash'] as String,
|
||||
loadedAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['loadedAt'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = DictionarySourceTable();
|
||||
|
||||
static const db = DictionarySourceRepository._();
|
||||
|
||||
@override
|
||||
int? id;
|
||||
|
||||
/// The source (`jmdict`).
|
||||
String source;
|
||||
|
||||
/// Its version (`3.6.2 (2026-10-05)`).
|
||||
String sourceVersion;
|
||||
|
||||
/// The words' language (`ja`).
|
||||
String language;
|
||||
|
||||
/// The meanings' language (`en`).
|
||||
String glossLanguage;
|
||||
|
||||
/// The data's licence, an SPDX identifier.
|
||||
String license;
|
||||
|
||||
/// Everything it is drawn from.
|
||||
List<_it7o1p0g.DictionaryCredit> credits;
|
||||
|
||||
/// How many entries it has.
|
||||
int entryCount;
|
||||
|
||||
/// The file it was loaded from, and the file's SHA-256.
|
||||
String fileName;
|
||||
|
||||
String fileHash;
|
||||
|
||||
/// When it was loaded, in UTC.
|
||||
DateTime loadedAt;
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [DictionarySource]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
DictionarySource copyWith({
|
||||
int? id,
|
||||
String? source,
|
||||
String? sourceVersion,
|
||||
String? language,
|
||||
String? glossLanguage,
|
||||
String? license,
|
||||
List<_it7o1p0g.DictionaryCredit>? credits,
|
||||
int? entryCount,
|
||||
String? fileName,
|
||||
String? fileHash,
|
||||
DateTime? loadedAt,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DictionarySource',
|
||||
if (id != null) 'id': id,
|
||||
'source': source,
|
||||
'sourceVersion': sourceVersion,
|
||||
'language': language,
|
||||
'glossLanguage': glossLanguage,
|
||||
'license': license,
|
||||
'credits': credits.toJson(valueToJson: (v) => v.toJson()),
|
||||
'entryCount': entryCount,
|
||||
'fileName': fileName,
|
||||
'fileHash': fileHash,
|
||||
'loadedAt': loadedAt.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DictionarySource',
|
||||
if (id != null) 'id': id,
|
||||
'source': source,
|
||||
'sourceVersion': sourceVersion,
|
||||
'language': language,
|
||||
'glossLanguage': glossLanguage,
|
||||
'license': license,
|
||||
'credits': credits.toJson(valueToJson: (v) => v.toJsonForProtocol()),
|
||||
'entryCount': entryCount,
|
||||
'fileName': fileName,
|
||||
'fileHash': fileHash,
|
||||
'loadedAt': loadedAt.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static DictionarySourceInclude include() {
|
||||
return DictionarySourceInclude._();
|
||||
}
|
||||
|
||||
static DictionarySourceIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
DictionarySourceInclude? include,
|
||||
}) {
|
||||
return DictionarySourceIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DictionarySourceImpl extends DictionarySource {
|
||||
_DictionarySourceImpl({
|
||||
int? id,
|
||||
required String source,
|
||||
required String sourceVersion,
|
||||
required String language,
|
||||
required String glossLanguage,
|
||||
required String license,
|
||||
required List<_it7o1p0g.DictionaryCredit> credits,
|
||||
required int entryCount,
|
||||
required String fileName,
|
||||
required String fileHash,
|
||||
required DateTime loadedAt,
|
||||
}) : super._(
|
||||
id: id,
|
||||
source: source,
|
||||
sourceVersion: sourceVersion,
|
||||
language: language,
|
||||
glossLanguage: glossLanguage,
|
||||
license: license,
|
||||
credits: credits,
|
||||
entryCount: entryCount,
|
||||
fileName: fileName,
|
||||
fileHash: fileHash,
|
||||
loadedAt: loadedAt,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DictionarySource]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
DictionarySource copyWith({
|
||||
Object? id = _Undefined,
|
||||
String? source,
|
||||
String? sourceVersion,
|
||||
String? language,
|
||||
String? glossLanguage,
|
||||
String? license,
|
||||
List<_it7o1p0g.DictionaryCredit>? credits,
|
||||
int? entryCount,
|
||||
String? fileName,
|
||||
String? fileHash,
|
||||
DateTime? loadedAt,
|
||||
}) {
|
||||
return DictionarySource(
|
||||
id: id is int? ? id : this.id,
|
||||
source: source ?? this.source,
|
||||
sourceVersion: sourceVersion ?? this.sourceVersion,
|
||||
language: language ?? this.language,
|
||||
glossLanguage: glossLanguage ?? this.glossLanguage,
|
||||
license: license ?? this.license,
|
||||
credits: credits ?? this.credits.map((e0) => e0.copyWith()).toList(),
|
||||
entryCount: entryCount ?? this.entryCount,
|
||||
fileName: fileName ?? this.fileName,
|
||||
fileHash: fileHash ?? this.fileHash,
|
||||
loadedAt: loadedAt ?? this.loadedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DictionarySourceUpdateTable
|
||||
extends _is.UpdateTable<DictionarySourceTable> {
|
||||
DictionarySourceUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<String, String> source(String value) => _is.ColumnValue(
|
||||
table.source,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> sourceVersion(String value) =>
|
||||
_is.ColumnValue(
|
||||
table.sourceVersion,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> language(String value) => _is.ColumnValue(
|
||||
table.language,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> glossLanguage(String value) =>
|
||||
_is.ColumnValue(
|
||||
table.glossLanguage,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> license(String value) => _is.ColumnValue(
|
||||
table.license,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<
|
||||
List<_it7o1p0g.DictionaryCredit>,
|
||||
List<_it7o1p0g.DictionaryCredit>
|
||||
>
|
||||
credits(List<_it7o1p0g.DictionaryCredit> value) => _is.ColumnValue(
|
||||
table.credits,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<int, int> entryCount(int value) => _is.ColumnValue(
|
||||
table.entryCount,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> fileName(String value) => _is.ColumnValue(
|
||||
table.fileName,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> fileHash(String value) => _is.ColumnValue(
|
||||
table.fileHash,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<DateTime, DateTime> loadedAt(DateTime value) =>
|
||||
_is.ColumnValue(
|
||||
table.loadedAt,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class DictionarySourceTable extends _is.Table<int?> {
|
||||
DictionarySourceTable({super.tableRelation})
|
||||
: super(tableName: 'dictionary_source') {
|
||||
updateTable = DictionarySourceUpdateTable(this);
|
||||
source = _is.ColumnString(
|
||||
'source',
|
||||
this,
|
||||
);
|
||||
sourceVersion = _is.ColumnString(
|
||||
'sourceVersion',
|
||||
this,
|
||||
);
|
||||
language = _is.ColumnString(
|
||||
'language',
|
||||
this,
|
||||
);
|
||||
glossLanguage = _is.ColumnString(
|
||||
'glossLanguage',
|
||||
this,
|
||||
);
|
||||
license = _is.ColumnString(
|
||||
'license',
|
||||
this,
|
||||
);
|
||||
credits = _is.ColumnSerializable<List<_it7o1p0g.DictionaryCredit>>(
|
||||
'credits',
|
||||
this,
|
||||
);
|
||||
entryCount = _is.ColumnInt(
|
||||
'entryCount',
|
||||
this,
|
||||
);
|
||||
fileName = _is.ColumnString(
|
||||
'fileName',
|
||||
this,
|
||||
);
|
||||
fileHash = _is.ColumnString(
|
||||
'fileHash',
|
||||
this,
|
||||
);
|
||||
loadedAt = _is.ColumnDateTime(
|
||||
'loadedAt',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final DictionarySourceUpdateTable updateTable;
|
||||
|
||||
/// The source (`jmdict`).
|
||||
late final _is.ColumnString source;
|
||||
|
||||
/// Its version (`3.6.2 (2026-10-05)`).
|
||||
late final _is.ColumnString sourceVersion;
|
||||
|
||||
/// The words' language (`ja`).
|
||||
late final _is.ColumnString language;
|
||||
|
||||
/// The meanings' language (`en`).
|
||||
late final _is.ColumnString glossLanguage;
|
||||
|
||||
/// The data's licence, an SPDX identifier.
|
||||
late final _is.ColumnString license;
|
||||
|
||||
/// Everything it is drawn from.
|
||||
late final _is.ColumnSerializable<List<_it7o1p0g.DictionaryCredit>> credits;
|
||||
|
||||
/// How many entries it has.
|
||||
late final _is.ColumnInt entryCount;
|
||||
|
||||
/// The file it was loaded from, and the file's SHA-256.
|
||||
late final _is.ColumnString fileName;
|
||||
|
||||
late final _is.ColumnString fileHash;
|
||||
|
||||
/// When it was loaded, in UTC.
|
||||
late final _is.ColumnDateTime loadedAt;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
source,
|
||||
sourceVersion,
|
||||
language,
|
||||
glossLanguage,
|
||||
license,
|
||||
credits,
|
||||
entryCount,
|
||||
fileName,
|
||||
fileHash,
|
||||
loadedAt,
|
||||
];
|
||||
}
|
||||
|
||||
class DictionarySourceInclude extends _is.IncludeObject {
|
||||
DictionarySourceInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => DictionarySource.t;
|
||||
}
|
||||
|
||||
class DictionarySourceIncludeList extends _is.IncludeList {
|
||||
DictionarySourceIncludeList._({
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(DictionarySource.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<int?> get table => DictionarySource.t;
|
||||
}
|
||||
|
||||
class DictionarySourceRepository {
|
||||
const DictionarySourceRepository._();
|
||||
|
||||
/// Returns a list of [DictionarySource]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<DictionarySource>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<DictionarySource>(
|
||||
where: where?.call(DictionarySource.t),
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [DictionarySource] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<DictionarySource?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<DictionarySource>(
|
||||
where: where?.call(DictionarySource.t),
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [DictionarySource] by its [id] or null if no such row exists.
|
||||
Future<DictionarySource?> findById(
|
||||
_is.DatabaseSession session,
|
||||
int id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<DictionarySource>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [DictionarySource]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [DictionarySource]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionarySource> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<DictionarySource>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [DictionarySource] and returns the inserted row.
|
||||
///
|
||||
/// The returned [DictionarySource] will have its `id` field set.
|
||||
Future<DictionarySource> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionarySource row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<DictionarySource>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [DictionarySource]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [DictionarySource]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionarySource> rows, {
|
||||
required _is.ColumnSelections<DictionarySourceTable> conflictColumns,
|
||||
_is.ColumnSelections<DictionarySourceTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<DictionarySource>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(DictionarySource.t),
|
||||
updateColumns: updateColumns?.call(DictionarySource.t),
|
||||
updateWhere: updateWhere?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [DictionarySource] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [DictionarySource] will have its `id` field set.
|
||||
Future<DictionarySource?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionarySource row, {
|
||||
required _is.ColumnSelections<DictionarySourceTable> conflictColumns,
|
||||
_is.ColumnSelections<DictionarySourceTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<DictionarySource>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(DictionarySource.t),
|
||||
updateColumns: updateColumns?.call(DictionarySource.t),
|
||||
updateWhere: updateWhere?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [DictionarySource]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionarySource> rows, {
|
||||
_is.ColumnSelections<DictionarySourceTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<DictionarySource>(
|
||||
rows,
|
||||
columns: columns?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [DictionarySource]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<DictionarySource> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionarySource row, {
|
||||
_is.ColumnSelections<DictionarySourceTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<DictionarySource>(
|
||||
row,
|
||||
columns: columns?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [DictionarySource] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<DictionarySource?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
int id, {
|
||||
required _is.ColumnValueListBuilder<DictionarySourceUpdateTable>
|
||||
columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<DictionarySource>(
|
||||
id,
|
||||
columnValues: columnValues(DictionarySource.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [DictionarySource]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<DictionarySourceUpdateTable>
|
||||
columnValues,
|
||||
required _is.WhereExpressionBuilder<DictionarySourceTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<DictionarySource>(
|
||||
columnValues: columnValues(DictionarySource.t.updateTable),
|
||||
where: where(DictionarySource.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [DictionarySource]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<DictionarySource> rows, {
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<DictionarySource>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [DictionarySource].
|
||||
Future<DictionarySource> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
DictionarySource row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<DictionarySource>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<DictionarySource>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<DictionarySourceTable> where,
|
||||
_is.OrderByBuilder<DictionarySourceTable>? orderBy,
|
||||
_is.OrderByListBuilder<DictionarySourceTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<DictionarySource>(
|
||||
where: where(DictionarySource.t),
|
||||
orderBy: orderBy?.call(DictionarySource.t),
|
||||
orderByList: orderByList?.call(DictionarySource.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<DictionarySourceTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<DictionarySource>(
|
||||
where: where?.call(DictionarySource.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [DictionarySource] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<DictionarySourceTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<DictionarySource>(
|
||||
where: where(DictionarySource.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,12 +19,14 @@ import 'package:serverpod_auth_idp_server/serverpod_auth_idp_server.dart'
|
|||
as _iais;
|
||||
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
|
||||
as _izehhkf5;
|
||||
|
||||
import '../asset/asset_endpoint.dart' as _ibwtj1i8;
|
||||
import '../auth/account_endpoint.dart' as _ii6ycf6v;
|
||||
import '../auth/anonymous_idp_endpoint.dart' as _in0zita6;
|
||||
import '../auth/devices_endpoint.dart' as _iduct7ue;
|
||||
import '../auth/github_sign_in_endpoint.dart' as _iasw79m5;
|
||||
import '../auth/github_upgrade_endpoint.dart' as _ivnugbf2;
|
||||
import '../dictionary/dictionary_endpoint.dart' as _ilgtwlto;
|
||||
import '../instance/instance_endpoint.dart' as _i3g8bfc2;
|
||||
export 'future_calls.dart' show ServerpodFutureCallsGetter;
|
||||
|
||||
|
|
@ -68,6 +70,12 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
'gitHubUpgrade',
|
||||
null,
|
||||
),
|
||||
'dictionary': _ilgtwlto.DictionaryEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
'dictionary',
|
||||
null,
|
||||
),
|
||||
'instance': _i3g8bfc2.InstanceEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
|
|
@ -309,6 +317,137 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
),
|
||||
},
|
||||
);
|
||||
connectors['dictionary'] = _is.EndpointConnector(
|
||||
name: 'dictionary',
|
||||
endpoint: endpoints['dictionary']!,
|
||||
methodConnectors: {
|
||||
'dictionaries': _is.MethodConnector(
|
||||
name: 'dictionaries',
|
||||
params: {},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['dictionary'] as _ilgtwlto.DictionaryEndpoint)
|
||||
.dictionaries(session),
|
||||
),
|
||||
'search': _is.MethodConnector(
|
||||
name: 'search',
|
||||
params: {
|
||||
'language': _is.ParameterDescription(
|
||||
name: 'language',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'glossLanguage': _is.ParameterDescription(
|
||||
name: 'glossLanguage',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'query': _is.ParameterDescription(
|
||||
name: 'query',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'limit': _is.ParameterDescription(
|
||||
name: 'limit',
|
||||
type: _is.getType<int?>(),
|
||||
nullable: true,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['dictionary'] as _ilgtwlto.DictionaryEndpoint)
|
||||
.search(
|
||||
session,
|
||||
params['language'],
|
||||
params['glossLanguage'],
|
||||
params['query'],
|
||||
limit: params['limit'],
|
||||
),
|
||||
),
|
||||
'countLabelled': _is.MethodConnector(
|
||||
name: 'countLabelled',
|
||||
params: {
|
||||
'language': _is.ParameterDescription(
|
||||
name: 'language',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'glossLanguage': _is.ParameterDescription(
|
||||
name: 'glossLanguage',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'label': _is.ParameterDescription(
|
||||
name: 'label',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['dictionary'] as _ilgtwlto.DictionaryEndpoint)
|
||||
.countLabelled(
|
||||
session,
|
||||
params['language'],
|
||||
params['glossLanguage'],
|
||||
params['label'],
|
||||
),
|
||||
),
|
||||
'labelled': _is.MethodConnector(
|
||||
name: 'labelled',
|
||||
params: {
|
||||
'language': _is.ParameterDescription(
|
||||
name: 'language',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'glossLanguage': _is.ParameterDescription(
|
||||
name: 'glossLanguage',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'label': _is.ParameterDescription(
|
||||
name: 'label',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'offset': _is.ParameterDescription(
|
||||
name: 'offset',
|
||||
type: _is.getType<int?>(),
|
||||
nullable: true,
|
||||
),
|
||||
'limit': _is.ParameterDescription(
|
||||
name: 'limit',
|
||||
type: _is.getType<int?>(),
|
||||
nullable: true,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['dictionary'] as _ilgtwlto.DictionaryEndpoint)
|
||||
.labelled(
|
||||
session,
|
||||
params['language'],
|
||||
params['glossLanguage'],
|
||||
params['label'],
|
||||
offset: params['offset'],
|
||||
limit: params['limit'],
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
connectors['instance'] = _is.EndpointConnector(
|
||||
name: 'instance',
|
||||
endpoint: endpoints['instance']!,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue