1 Commits

Author SHA1 Message Date
oysteikt 8df3db4c77 WIP: add kanjivg data
Build and test / build (push) Successful in 8m1s
2026-04-09 16:13:02 +09:00
43 changed files with 531 additions and 1273 deletions
+3 -2
View File
@@ -16,14 +16,15 @@ jobs:
uses: https://github.com/cachix/install-nix-action@v31 uses: https://github.com/cachix/install-nix-action@v31
with: with:
extra_nix_config: | extra_nix_config: |
experimental-features = nix-command flakes
show-trace = true show-trace = true
max-jobs = auto max-jobs = auto
trusted-users = root trusted-users = root
experimental-features = nix-command flakes experimental-features = nix-command flakes
build-users-group = build-users-group =
# - name: Update database inputs - name: Update database inputs
# run: nix flake update datasources run: nix flake update datasources
- name: Build database - name: Build database
run: nix build .#database -L run: nix build .#database -L
+2 -4
View File
@@ -1,7 +1,5 @@
import './search/english_word_search.dart'; import './search/word_search.dart';
import './search/japanese_word_search.dart';
Future<void> main() async { Future<void> main() async {
await EnglishWordSearchBenchmark.main(); await WordSearchBenchmark.main();
await JapaneseWordSearchBenchmark.main();
} }
@@ -1,49 +0,0 @@
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:jadb/search.dart';
import 'package:sqflite_common/sqlite_api.dart';
import '../../test/search/setup_database_connection.dart';
class JapaneseWordSearchBenchmark extends AsyncBenchmarkBase {
Database? connection;
static final List<String> searchTerms = [
'仮名',
'漢字',
'かわいい',
'すし',
'ラメン',
];
JapaneseWordSearchBenchmark() : super('JapaneseWordSearchBenchmark');
static Future<void> main() async {
print('Running JapaneseWordSearchBenchmark...');
await JapaneseWordSearchBenchmark().report();
print('Finished JapaneseWordSearchBenchmark');
}
@override
Future<void> setup() async {
connection = await setupDatabaseConnection();
}
@override
Future<void> run() async {
for (final term in searchTerms) {
final result = await connection!.jadbSearchWord(term);
assert(
result?.isNotEmpty ?? false,
'Expected search results for term "$term"',
);
}
}
@override
Future<void> teardown() async {
await connection?.close();
}
// @override
// Future<void> exercise() => run();
}
@@ -4,23 +4,23 @@ import 'package:sqflite_common/sqlite_api.dart';
import '../../test/search/setup_database_connection.dart'; import '../../test/search/setup_database_connection.dart';
class EnglishWordSearchBenchmark extends AsyncBenchmarkBase { class WordSearchBenchmark extends AsyncBenchmarkBase {
Database? connection; Database? connection;
static final List<String> searchTerms = [ static final List<String> searchTerms = [
'kana', 'kana',
'kanji', 'kanji',
'cute', 'kawaii',
'sushi', 'sushi',
'ramen', 'ramen',
]; ];
EnglishWordSearchBenchmark() : super('EnglishWordSearchBenchmark'); WordSearchBenchmark() : super('WordSearchBenchmark');
static Future<void> main() async { static Future<void> main() async {
print('Running EnglishWordSearchBenchmark...'); print('Running WordSearchBenchmark...');
await EnglishWordSearchBenchmark().report(); await WordSearchBenchmark().report();
print('Finished EnglishWordSearchBenchmark'); print('Finished WordSearchBenchmark');
} }
@override @override
@@ -31,11 +31,7 @@ class EnglishWordSearchBenchmark extends AsyncBenchmarkBase {
@override @override
Future<void> run() async { Future<void> run() async {
for (final term in searchTerms) { for (final term in searchTerms) {
final result = await connection!.jadbSearchWord(term); await connection!.jadbSearchWord(term);
assert(
result?.isNotEmpty ?? false,
'Expected search results for term "$term"',
);
} }
} }
+1 -2
View File
@@ -25,5 +25,4 @@ The `JMdict_EntryScore` table is used to store the score of each entry, which is
The table is automatically generated from other tables via triggers, and should be considered as a materialized view. The table is automatically generated from other tables via triggers, and should be considered as a materialized view.
<s>There is a score row for every single entry in both `JMdict_KanjiElement` and `JMdict_ReadingElement`, split by the `type` field.</s> There is a score row for every single entry in both `JMdict_KanjiElement` and `JMdict_ReadingElement`, split by the `type` field.
This is no longer true, we now only store the rows for which the score is not `0`. The `type` field is now also virtual, since the `elementId` fields for both kanji and readings are unique to each other.
Generated
+24 -7
View File
@@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1780302182, "lastModified": 1775550160,
"narHash": "sha256-IfC+dpdjjlkzrWlm+p851T43GsR04wMAPqGn63jisJ4=", "narHash": "sha256-bgvKrMGUPaDY4EZv+82z1ccYoxwaergdVw/3PZhc2Fc=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "c116674dd1e0b879660e6237e54904aa825d4511", "rev": "f46229af3678124c5ea7c8dff3292747d0274f69",
"revCount": 29, "revCount": 8,
"type": "git", "type": "git",
"url": "https://git.pvv.ntnu.no/Mugiten/datasources.git" "url": "https://git.pvv.ntnu.no/Mugiten/datasources.git"
}, },
@@ -20,13 +20,29 @@
"url": "https://git.pvv.ntnu.no/Mugiten/datasources.git" "url": "https://git.pvv.ntnu.no/Mugiten/datasources.git"
} }
}, },
"kanjivg-src": {
"flake": false,
"locked": {
"lastModified": 1775218066,
"narHash": "sha256-iYv9xakgoGt/JwwdKDUCpSAF36hBtKlX9oN7xiLowjs=",
"ref": "refs/heads/master",
"rev": "544d319f79348c092d567b662f27f33dacfa60cd",
"revCount": 2215,
"type": "git",
"url": "https://git.pvv.ntnu.no/mugiten/kanjivg.git"
},
"original": {
"type": "git",
"url": "https://git.pvv.ntnu.no/mugiten/kanjivg.git"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1779560665, "lastModified": 1775423009,
"narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", "narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", "rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -38,6 +54,7 @@
"root": { "root": {
"inputs": { "inputs": {
"datasources": "datasources", "datasources": "datasources",
"kanjivg-src": "kanjivg-src",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
} }
+6
View File
@@ -8,12 +8,18 @@
url = "git+https://git.pvv.ntnu.no/Mugiten/datasources.git"; url = "git+https://git.pvv.ntnu.no/Mugiten/datasources.git";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
kanjivg-src = {
url = "git+https://git.pvv.ntnu.no/mugiten/kanjivg.git";
flake = false;
};
}; };
outputs = { outputs = {
self, self,
nixpkgs, nixpkgs,
datasources, datasources,
kanjivg-src,
}: let }: let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
systems = [ systems = [
+8 -16
View File
@@ -45,7 +45,9 @@ class KanjiElement extends Element {
}); });
@override @override
Map<String, Object?> get sqlValue => {...super.sqlValue}; Map<String, Object?> get sqlValue => {
...super.sqlValue,
};
} }
class ReadingElement extends Element { class ReadingElement extends Element {
@@ -104,8 +106,9 @@ class Glossary extends SQLWritable {
@override @override
Map<String, Object?> get sqlValue => { Map<String, Object?> get sqlValue => {
// 'language': language, 'language': language,
'phrase': phrase, 'phrase': phrase,
'type': type,
}; };
} }
@@ -127,19 +130,6 @@ class XRefParts {
'readingRef': readingRef, 'readingRef': readingRef,
'senseOrderNum': senseOrderNum, 'senseOrderNum': senseOrderNum,
}; };
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is XRefParts &&
other.kanjiRef == kanjiRef &&
other.readingRef == readingRef &&
other.senseOrderNum == senseOrderNum;
}
@override
int get hashCode => Object.hash(kanjiRef, readingRef, senseOrderNum);
} }
class XRef { class XRef {
@@ -179,7 +169,9 @@ class Sense extends SQLWritable {
}); });
@override @override
Map<String, Object?> get sqlValue => {'senseId': senseId}; Map<String, Object?> get sqlValue => {
'senseId': senseId,
};
bool get isEmpty => bool get isEmpty =>
antonyms.isEmpty && antonyms.isEmpty &&
+13 -89
View File
@@ -1,5 +1,4 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:jadb/_data_ingestion/jmdict/objects.dart'; import 'package:jadb/_data_ingestion/jmdict/objects.dart';
@@ -15,12 +14,6 @@ class ResolvedXref {
const ResolvedXref(this.entry, this.ambiguous); const ResolvedXref(this.entry, this.ambiguous);
} }
// A constant map of xref parts to jmdict id for unresolvable xrefs.
final xrefExceptions = {
// NOTE: see https://www.edrdg.org/jmwsgi/entr.py?svc=jmdict&g=2870981.1~2369718 for details
XRefParts(kanjiRef: 'プレストレスト', readingRef: 'コンクリート'): 2472380,
};
/// Resolves an xref (pair of kanji, optionally reading, and optionally sense number) to an a specific /// Resolves an xref (pair of kanji, optionally reading, and optionally sense number) to an a specific
/// JMdict entry, if possible. /// JMdict entry, if possible.
/// ///
@@ -33,65 +26,15 @@ ResolvedXref resolveXref(
SplayTreeMap<String, Set<Entry>> entriesByReading, SplayTreeMap<String, Set<Entry>> entriesByReading,
XRefParts xref, XRefParts xref,
) { ) {
late List<Entry> candidateEntries; List<Entry> candidateEntries = switch ((xref.kanjiRef, xref.readingRef)) {
(null, null) => throw Exception(
if (xrefExceptions.containsKey(xref)) { 'Xref $xref has no kanji or reading reference',
final exceptionEntryId = xrefExceptions[xref]!; ),
// NOTE: this is slow, but we have few exceptions. Let's wait for JMdict XML-NG to be released so we can delete this :) (final String k, null) => entriesByKanji[k]!.toList(),
final exceptionEntry = (null, final String r) => entriesByReading[r]!.toList(),
entriesByKanji.values (final String k, final String r) =>
.expand((set) => set) entriesByKanji[k]!.intersection(entriesByReading[r]!).toList(),
.firstWhereOrNull((entry) => entry.entryId == exceptionEntryId) ?? };
entriesByReading.values
.expand((set) => set)
.firstWhereOrNull((entry) => entry.entryId == exceptionEntryId);
if (exceptionEntry != null) {
return ResolvedXref(exceptionEntry, false);
} else {
throw Exception(
'Xref $xref matches an exception entry ID $exceptionEntryId, but that entry was not found among the candidates.',
);
}
}
switch ((xref.kanjiRef, xref.readingRef)) {
case (null, null):
throw Exception('Xref $xref has no kanji or reading reference');
case (final String k, null):
if (!entriesByKanji.containsKey(k)) {
throw Exception(
'Xref $xref has kanji reference "$k" but no entries found with that kanji',
);
}
candidateEntries = entriesByKanji[k]!.toList();
break;
case (null, final String r):
if (!entriesByReading.containsKey(r)) {
throw Exception(
'Xref $xref has reading reference "$r" but no entries found with that reading',
);
}
candidateEntries = entriesByReading[r]!.toList();
break;
case (final String k, final String r):
if (!entriesByKanji.containsKey(k)) {
throw Exception(
'Xref $xref has kanji reference "$k" but no entries found with that kanji',
);
}
if (!entriesByReading.containsKey(r)) {
throw Exception(
'Xref $xref has reading reference "$r" but no entries found with that reading',
);
}
candidateEntries = entriesByKanji[k]!
.intersection(entriesByReading[r]!)
.toList();
}
// Filter out entries that don't have the number of senses specified in the xref // Filter out entries that don't have the number of senses specified in the xref
if (xref.senseOrderNum != null) { if (xref.senseOrderNum != null) {
@@ -141,21 +84,6 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
print(' [JMdict] Batch 1 - Kanji and readings'); print(' [JMdict] Batch 1 - Kanji and readings');
Batch b = db.batch(); Batch b = db.batch();
if (Platform.environment['JMDICT_VERSION'] != null &&
Platform.environment['JMDICT_DATE'] != null &&
Platform.environment['JMDICT_HASH'] != null) {
b.insert(JMdictTableNames.version, {
'version': Platform.environment['JMDICT_VERSION']!,
'date': Platform.environment['JMDICT_DATE']!,
'hash': Platform.environment['JMDICT_HASH']!,
});
} else {
print(
'WARNING: JMDICT version information not found in environment variables. '
'This may cause issues with future updates.',
);
}
for (final e in entries) { for (final e in entries) {
b.insert(JMdictTableNames.entry, e.sqlValue); b.insert(JMdictTableNames.entry, e.sqlValue);
@@ -244,14 +172,6 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
JMdictTableNames.senseGlossary, JMdictTableNames.senseGlossary,
g.sqlValue..addAll({'senseId': s.senseId}), g.sqlValue..addAll({'senseId': s.senseId}),
); );
if (g.type != null) {
b.insert(JMdictTableNames.senseGlossaryType, {
'senseId': s.senseId,
'phrase': g.phrase,
'type': g.type!,
});
}
} }
} }
} }
@@ -299,6 +219,8 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
b.insert(JMdictTableNames.senseSeeAlso, { b.insert(JMdictTableNames.senseSeeAlso, {
'senseId': s.senseId, 'senseId': s.senseId,
'xrefEntryId': resolvedEntry.entry.entryId, 'xrefEntryId': resolvedEntry.entry.entryId,
'seeAlsoKanji': xref.kanjiRef,
'seeAlsoReading': xref.readingRef,
'seeAlsoSense': xref.senseOrderNum != null 'seeAlsoSense': xref.senseOrderNum != null
? xref.senseOrderNum! - 1 ? xref.senseOrderNum! - 1
: null, : null,
@@ -326,6 +248,8 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
b.insert(JMdictTableNames.senseAntonyms, { b.insert(JMdictTableNames.senseAntonyms, {
'senseId': s.senseId, 'senseId': s.senseId,
'xrefEntryId': resolvedEntry.entry.entryId, 'xrefEntryId': resolvedEntry.entry.entryId,
'antonymKanji': ant.kanjiRef,
'antonymReading': ant.readingRef,
'antonymSense': ant.senseOrderNum != null 'antonymSense': ant.senseOrderNum != null
? ant.senseOrderNum! - 1 ? ant.senseOrderNum! - 1
: null, : null,
+4 -19
View File
@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:jadb/table_names/kanjidic.dart'; import 'package:jadb/table_names/kanjidic.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
@@ -7,22 +5,6 @@ import 'objects.dart';
Future<void> seedKANJIDICData(List<Character> characters, Database db) async { Future<void> seedKANJIDICData(List<Character> characters, Database db) async {
final b = db.batch(); final b = db.batch();
if (Platform.environment['KANJIDIC_VERSION'] != null &&
Platform.environment['KANJIDIC_DATE'] != null &&
Platform.environment['KANJIDIC_HASH'] != null) {
b.insert(KANJIDICTableNames.version, {
'version': Platform.environment['KANJIDIC_VERSION']!,
'date': Platform.environment['KANJIDIC_DATE']!,
'hash': Platform.environment['KANJIDIC_HASH']!,
});
} else {
print(
'WARNING: KANJIDIC version information not found in environment variables. '
'This may cause issues with future updates.',
);
}
for (final c in characters) { for (final c in characters) {
// if (c.dictionaryReferences.any((e) => // if (c.dictionaryReferences.any((e) =>
// c.dictionaryReferences // c.dictionaryReferences
@@ -48,7 +30,10 @@ Future<void> seedKANJIDICData(List<Character> characters, Database db) async {
} }
if (c.jlpt != null) { if (c.jlpt != null) {
b.insert(KANJIDICTableNames.jlpt, {'kanji': c.literal, 'jlpt': c.jlpt!}); b.insert(KANJIDICTableNames.jlpt, {
'kanji': c.literal,
'jlpt': c.jlpt!,
});
} }
for (final n in c.radicalName) { for (final n in c.radicalName) {
+92
View File
@@ -0,0 +1,92 @@
import 'package:jadb/_data_ingestion/sql_writable.dart';
/// Enum set in the kvg:position attribute, used by `<g>` elements in the KanjiVG SVG files.
enum KanjiPathGroupPosition {
bottom,
kamae,
kamaec,
left,
middle,
nyo,
nyoc,
right,
tare,
tarec,
top,
}
/// Contents of a \<g> element in the KanjiVG SVG files.
class KanjiPathGroupTreeNode extends SQLWritable {
final String id;
final List<KanjiPathGroupTreeNode> children;
final String? element;
final String? original;
final KanjiPathGroupPosition? position;
final String? radical;
final int? part;
KanjiPathGroupTreeNode({
required this.id,
this.children = const [],
this.element,
this.original,
this.position,
this.radical,
this.part,
});
@override
Map<String, Object?> get sqlValue => {
'id': id,
'element': element,
'original': original,
'position': position?.name,
'radical': radical,
'part': part,
};
}
/// Contents of a `<text>` element in the StrokeNumber's group in the KanjiVG SVG files
class KanjiStrokeNumber extends SQLWritable {
final int num;
final double x;
final double y;
KanjiStrokeNumber(this.num, this.x, this.y);
@override
Map<String, Object?> get sqlValue => {'num': num, 'x': x, 'y': y};
}
/// Contents of a `<path>` element in the KanjiVG SVG files
class KanjiVGPath extends SQLWritable {
final String id;
final String type;
final String svgPath;
KanjiVGPath({required this.id, required this.type, required this.svgPath});
@override
Map<String, Object?> get sqlValue => {
'id': id,
'type': type,
'svgPath': svgPath,
};
}
class KanjiVGItem extends SQLWritable {
final String character;
final List<KanjiVGPath> paths;
final List<KanjiStrokeNumber> strokeNumbers;
final List<KanjiPathGroupTreeNode> pathGroups;
KanjiVGItem({
required this.character,
required this.paths,
required this.strokeNumbers,
required this.pathGroups,
});
@override
Map<String, Object?> get sqlValue => {'character': character};
}
@@ -0,0 +1,7 @@
import 'package:sqflite_common/sqflite.dart';
Future<void> seedKanjiVGData(Iterable<String> xmlContents, Database db) async {
final b = db.batch();
await b.commit(noResult: true);
}
@@ -1,26 +1,9 @@
import 'dart:io';
import 'package:jadb/table_names/radkfile.dart'; import 'package:jadb/table_names/radkfile.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
Future<void> seedRADKFILEData(Iterable<String> blocks, Database db) async { Future<void> seedRADKFILEData(Iterable<String> blocks, Database db) async {
final b = db.batch(); final b = db.batch();
if (Platform.environment['RADKFILE_VERSION'] != null &&
Platform.environment['RADKFILE_DATE'] != null &&
Platform.environment['RADKFILE_HASH'] != null) {
b.insert(RADKFILETableNames.version, {
'version': Platform.environment['RADKFILE_VERSION']!,
'date': Platform.environment['RADKFILE_DATE']!,
'hash': Platform.environment['RADKFILE_HASH']!,
});
} else {
print(
'WARNING: RADKFILE version information not found in environment variables. '
'This may cause issues with future updates.',
);
}
for (final block in blocks) { for (final block in blocks) {
final String radical = block[1]; final String radical = block[1];
final List<String> kanjiList = final List<String> kanjiList =
@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:jadb/table_names/tanos_jlpt.dart'; import 'package:jadb/table_names/tanos_jlpt.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
@@ -9,21 +7,6 @@ Future<void> seedTanosJLPTData(
) async { ) async {
final Batch b = db.batch(); final Batch b = db.batch();
if (Platform.environment['TANOS_JLPT_VERSION'] != null &&
Platform.environment['TANOS_JLPT_DATE'] != null &&
Platform.environment['TANOS_JLPT_HASH'] != null) {
b.insert(TanosJLPTTableNames.version, {
'version': Platform.environment['TANOS_JLPT_VERSION']!,
'date': Platform.environment['TANOS_JLPT_DATE']!,
'hash': Platform.environment['TANOS_JLPT_HASH']!,
});
} else {
print(
'WARNING: Tanos JLPT version information not found in environment variables. '
'This may cause issues with future updates.',
);
}
for (final jlptLevel in resolvedEntries.entries) { for (final jlptLevel in resolvedEntries.entries) {
final level = jlptLevel.key; final level = jlptLevel.key;
final entryIds = jlptLevel.value; final entryIds = jlptLevel.value;
+204 -297
View File
@@ -1,310 +1,217 @@
import 'package:collection/collection.dart'; const Map<int, List<String>> radicals = {
1: ['', '', '', '', '', ''],
class RadkfileRadical {
/// The formal variant of the radical
///
/// This is the one you want to use for searching.
final String formalVariant;
/// The informal variant of the radical (if it differs from the formal one).
///
/// This is the one you should display to the user.
final String? informalVariant;
/// The number of strokes in this radical.
final int strokeCount;
const RadkfileRadical({
required this.formalVariant,
this.informalVariant,
required this.strokeCount,
});
}
const Map<int, List<RadkfileRadical>> radicals = {
1: [
RadkfileRadical(formalVariant: '', strokeCount: 1),
RadkfileRadical(formalVariant: '', strokeCount: 1),
RadkfileRadical(formalVariant: '', strokeCount: 1),
RadkfileRadical(formalVariant: '', strokeCount: 1),
RadkfileRadical(formalVariant: '', strokeCount: 1),
RadkfileRadical(formalVariant: '', strokeCount: 1),
],
2: [ 2: [
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', informalVariant: '𠆢', strokeCount: 2), '𠆢',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '',
RadkfileRadical(formalVariant: '', strokeCount: 2), '𠂉',
], ],
3: [ 3: [
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '广', strokeCount: 3), '广',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
RadkfileRadical(formalVariant: '', strokeCount: 3), '',
], ],
4: [ 4: [
RadkfileRadical(formalVariant: '', informalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
RadkfileRadical(formalVariant: '', strokeCount: 4), '',
], ],
5: [ 5: [
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
RadkfileRadical(formalVariant: '', strokeCount: 5), '',
], ],
6: [ 6: [
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '', strokeCount: 6), '',
RadkfileRadical(formalVariant: '西', strokeCount: 6), '西',
], ],
7: [ 7: [
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
RadkfileRadical(formalVariant: '', strokeCount: 7), '',
], ],
8: [ 8: ['', '', '', '', '', '', '', '', '', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 9: ['', '', '', '', '', '', '', '', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 10: ['', '', '', '', '', '', '', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 11: ['', '', '', '鹿', '', '', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 12: ['', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 13: ['', '', '', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 14: ['', ''],
RadkfileRadical(formalVariant: '', strokeCount: 8), 17: [''],
RadkfileRadical(formalVariant: '', strokeCount: 8),
RadkfileRadical(formalVariant: '', strokeCount: 8),
RadkfileRadical(formalVariant: '', strokeCount: 8),
RadkfileRadical(formalVariant: '', strokeCount: 8),
RadkfileRadical(formalVariant: '', strokeCount: 8),
],
9: [
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
RadkfileRadical(formalVariant: '', strokeCount: 9),
],
10: [
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
RadkfileRadical(formalVariant: '', strokeCount: 10),
],
11: [
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '鹿', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
RadkfileRadical(formalVariant: '', strokeCount: 11),
],
12: [
RadkfileRadical(formalVariant: '', strokeCount: 12),
RadkfileRadical(formalVariant: '', strokeCount: 12),
RadkfileRadical(formalVariant: '', strokeCount: 12),
RadkfileRadical(formalVariant: '', strokeCount: 12),
],
13: [
RadkfileRadical(formalVariant: '', strokeCount: 13),
RadkfileRadical(formalVariant: '', strokeCount: 13),
RadkfileRadical(formalVariant: '', strokeCount: 13),
RadkfileRadical(formalVariant: '', strokeCount: 13),
],
14: [
RadkfileRadical(formalVariant: '', strokeCount: 14),
RadkfileRadical(formalVariant: '', strokeCount: 14),
],
17: [RadkfileRadical(formalVariant: '', strokeCount: 17)],
};
final Map<String, RadkfileRadical> radicalsByFormalVariant = {
for (final r in radicals.values.flattened) r.formalVariant: r,
}; };
+2
View File
@@ -1,5 +1,6 @@
import 'package:jadb/table_names/jmdict.dart'; import 'package:jadb/table_names/jmdict.dart';
import 'package:jadb/table_names/kanjidic.dart'; import 'package:jadb/table_names/kanjidic.dart';
import 'package:jadb/table_names/kanjivg.dart';
import 'package:jadb/table_names/radkfile.dart'; import 'package:jadb/table_names/radkfile.dart';
import 'package:jadb/table_names/tanos_jlpt.dart'; import 'package:jadb/table_names/tanos_jlpt.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
@@ -21,6 +22,7 @@ Future<void> verifyTablesWithDbConnection(DatabaseExecutor db) async {
...KANJIDICTableNames.allTables, ...KANJIDICTableNames.allTables,
...RADKFILETableNames.allTables, ...RADKFILETableNames.allTables,
...TanosJLPTTableNames.allTables, ...TanosJLPTTableNames.allTables,
...KanjiVGTableNames.allTables,
}; };
final missingTables = expectedTables.difference(tables); final missingTables = expectedTables.difference(tables);
@@ -33,7 +33,6 @@ class WordSearchMatchSpan {
Map<String, Object?> toJson() => { Map<String, Object?> toJson() => {
'spanType': spanType.toString().split('.').last, 'spanType': spanType.toString().split('.').last,
'index': index, 'index': index,
'subIndex': subIndex,
'start': start, 'start': start,
'end': end, 'end': end,
}; };
@@ -44,13 +43,12 @@ class WordSearchMatchSpan {
(e) => e.toString().split('.').last == json['spanType'], (e) => e.toString().split('.').last == json['spanType'],
), ),
index: json['index'] as int, index: json['index'] as int,
subIndex: json['subIndex'] as int? ?? 0,
start: json['start'] as int, start: json['start'] as int,
end: json['end'] as int, end: json['end'] as int,
); );
@override @override
int get hashCode => Object.hash(spanType, index, subIndex, start, end); int get hashCode => Object.hash(spanType, index, start, end);
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
@@ -58,7 +56,6 @@ class WordSearchMatchSpan {
return other is WordSearchMatchSpan && return other is WordSearchMatchSpan &&
other.spanType == spanType && other.spanType == spanType &&
other.index == index && other.index == index &&
other.subIndex == subIndex &&
other.start == start && other.start == start &&
other.end == end; other.end == end;
} }
@@ -81,7 +81,7 @@ class WordSearchSense {
'dialects': dialects.map((e) => e.toJson()).toList(), 'dialects': dialects.map((e) => e.toJson()).toList(),
'misc': misc.map((e) => e.toJson()).toList(), 'misc': misc.map((e) => e.toJson()).toList(),
'info': info, 'info': info,
'languageSource': languageSource.map((e) => e.toJson()).toList(), 'languageSource': languageSource,
}; };
factory WordSearchSense.fromJson(Map<String, dynamic> json) => factory WordSearchSense.fromJson(Map<String, dynamic> json) =>
@@ -16,8 +16,6 @@ class WordSearchXrefEntry {
final bool ambiguous; final bool ambiguous;
/// The result of the cross-reference, may or may not be included in the query. /// The result of the cross-reference, may or may not be included in the query.
///
/// Be careful not to introduce circular references when using this field.
final WordSearchResult? xrefResult; final WordSearchResult? xrefResult;
const WordSearchXrefEntry({ const WordSearchXrefEntry({
@@ -42,10 +40,6 @@ class WordSearchXrefEntry {
ambiguous: json['ambiguous'] as bool, ambiguous: json['ambiguous'] as bool,
baseWord: json['baseWord'] as String, baseWord: json['baseWord'] as String,
furigana: json['furigana'] as String?, furigana: json['furigana'] as String?,
xrefResult: json['xrefResult'] != null xrefResult: null,
? WordSearchResult.fromJson(
Map<String, dynamic>.from(json['xrefResult'] as Map),
)
: null,
); );
} }
+7 -16
View File
@@ -1,11 +1,9 @@
import 'package:jadb/const_data/radicals.dart';
import 'package:jadb/models/kanji_search/kanji_search_result.dart'; import 'package:jadb/models/kanji_search/kanji_search_result.dart';
import 'package:jadb/models/verify_tables.dart'; import 'package:jadb/models/verify_tables.dart';
import 'package:jadb/models/word_search/word_search_result.dart'; import 'package:jadb/models/word_search/word_search_result.dart';
import 'package:jadb/search/filter_kanji.dart'; import 'package:jadb/search/filter_kanji.dart';
import 'package:jadb/search/kanji_search.dart'; import 'package:jadb/search/kanji_search.dart';
import 'package:jadb/search/radical_search.dart'; import 'package:jadb/search/radical_search.dart';
import 'package:jadb/search/versions.dart';
import 'package:jadb/search/word_search/word_search.dart'; import 'package:jadb/search/word_search/word_search.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
@@ -20,13 +18,12 @@ extension JaDBConnection on DatabaseExecutor {
searchKanjiWithDbConnection(this, kanji); searchKanjiWithDbConnection(this, kanji);
/// Search for a kanji in the database. /// Search for a kanji in the database.
Future<Map<String, KanjiSearchResult>> jadbGetManyKanji( Future<Map<String, KanjiSearchResult>> jadbGetManyKanji(Set<String> kanji) =>
Iterable<String> kanji, searchManyKanjiWithDbConnection(this, kanji);
) => searchManyKanjiWithDbConnection(this, kanji);
/// Filter a list of characters, and return the ones that are listed in the kanji dictionary. /// Filter a list of characters, and return the ones that are listed in the kanji dictionary.
Future<List<String>> filterKanji( Future<List<String>> filterKanji(
Iterable<String> kanji, { List<String> kanji, {
bool deduplicate = false, bool deduplicate = false,
}) => filterKanjiWithDbConnection(this, kanji, deduplicate); }) => filterKanjiWithDbConnection(this, kanji, deduplicate);
@@ -64,17 +61,11 @@ extension JaDBConnection on DatabaseExecutor {
/// of the radicals, find their other radicals, and return those. /// of the radicals, find their other radicals, and return those.
/// This is used to figure out which remaining combinations of radicals /// This is used to figure out which remaining combinations of radicals
/// the user can search for without getting zero results. /// the user can search for without getting zero results.
Future<List<RadkfileRadical>> jadbSearchRemainingRadicals( Future<List<String>> jadbSearchRemainingRadicals(List<String> radicals) =>
List<RadkfileRadical> radicals, searchRemainingRadicalsWithDbConnection(this, radicals);
) => searchRemainingRadicalsWithDbConnection(this, radicals);
/// Given a list of radicals, search which kanji contains all /// Given a list of radicals, search which kanji contains all
/// of the radicals, and return those. /// of the radicals, and return those.
Future<List<String>> jadbSearchKanjiByRadicals( Future<List<String>> jadbSearchKanjiByRadicals(List<String> radicals) =>
List<RadkfileRadical> radicals, searchKanjiByRadicalsWithDbConnection(this, radicals);
) => searchKanjiByRadicalsWithDbConnection(this, radicals);
/// Retrieve the version information for all datasources in the database.
Future<DatasourceVersions> jadbGetDatasourceVersions() =>
getDatasourceVersions(this);
} }
+2 -2
View File
@@ -6,7 +6,7 @@ import 'package:sqflite_common/sqflite.dart';
/// If [deduplicate] is true, the returned list will deduplicate the input kanji list before returning the filtered results. /// If [deduplicate] is true, the returned list will deduplicate the input kanji list before returning the filtered results.
Future<List<String>> filterKanjiWithDbConnection( Future<List<String>> filterKanjiWithDbConnection(
DatabaseExecutor connection, DatabaseExecutor connection,
Iterable<String> kanji, List<String> kanji,
bool deduplicate, bool deduplicate,
) async { ) async {
final Set<String> filteredKanji = await connection final Set<String> filteredKanji = await connection
@@ -14,7 +14,7 @@ Future<List<String>> filterKanjiWithDbConnection(
SELECT "literal" SELECT "literal"
FROM "${KANJIDICTableNames.character}" FROM "${KANJIDICTableNames.character}"
WHERE "literal" IN (${kanji.map((_) => '?').join(',')}) WHERE "literal" IN (${kanji.map((_) => '?').join(',')})
''', kanji.toList()) ''', kanji)
.then((value) => value.map((e) => e['literal'] as String).toSet()); .then((value) => value.map((e) => e['literal'] as String).toSet());
if (deduplicate) { if (deduplicate) {
+1 -1
View File
@@ -274,7 +274,7 @@ Future<KanjiSearchResult?> searchKanjiWithDbConnection(
/// Searches for multiple kanji at once, returning a map of kanji to their search results. /// Searches for multiple kanji at once, returning a map of kanji to their search results.
Future<Map<String, KanjiSearchResult>> searchManyKanjiWithDbConnection( Future<Map<String, KanjiSearchResult>> searchManyKanjiWithDbConnection(
DatabaseExecutor connection, DatabaseExecutor connection,
Iterable<String> kanji, Set<String> kanji,
) async { ) async {
if (kanji.isEmpty) { if (kanji.isEmpty) {
return {}; return {};
+5 -9
View File
@@ -1,4 +1,3 @@
import 'package:jadb/const_data/radicals.dart';
import 'package:jadb/table_names/radkfile.dart'; import 'package:jadb/table_names/radkfile.dart';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
@@ -8,9 +7,9 @@ import 'package:sqflite_common/sqlite_api.dart';
/// ///
/// This can be used to limit the choices of additional radicals provided to a user, /// This can be used to limit the choices of additional radicals provided to a user,
/// so that any choice they make will still yield at least one kanji. /// so that any choice they make will still yield at least one kanji.
Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection( Future<List<String>> searchRemainingRadicalsWithDbConnection(
DatabaseExecutor connection, DatabaseExecutor connection,
List<RadkfileRadical> radicals, List<String> radicals,
) async { ) async {
final distinctRadicals = radicals.toSet(); final distinctRadicals = radicals.toSet();
@@ -26,14 +25,11 @@ Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection(
HAVING COUNT(DISTINCT "radical") = ? HAVING COUNT(DISTINCT "radical") = ?
) )
''', ''',
[...distinctRadicals.map((r) => r.formalVariant), distinctRadicals.length], [...distinctRadicals, distinctRadicals.length],
); );
final remainingRadicals = queryResult final remainingRadicals = queryResult
.map((row) => row['radical'] as String) .map((row) => row['radical'] as String)
// TODO: maybe we should do some runtime checking here, just to throw a sensible error
// if something goes horribly wrong?
.map((r) => radicalsByFormalVariant[r]!)
.toList(); .toList();
return remainingRadicals; return remainingRadicals;
@@ -42,7 +38,7 @@ Future<List<RadkfileRadical>> searchRemainingRadicalsWithDbConnection(
/// Returns a list of kanji that contain all of the input radicals. /// Returns a list of kanji that contain all of the input radicals.
Future<List<String>> searchKanjiByRadicalsWithDbConnection( Future<List<String>> searchKanjiByRadicalsWithDbConnection(
DatabaseExecutor connection, DatabaseExecutor connection,
List<RadkfileRadical> radicals, List<String> radicals,
) async { ) async {
final distinctRadicals = radicals.toSet(); final distinctRadicals = radicals.toSet();
@@ -54,7 +50,7 @@ Future<List<String>> searchKanjiByRadicalsWithDbConnection(
GROUP BY "kanji" GROUP BY "kanji"
HAVING COUNT(DISTINCT "radical") = ? HAVING COUNT(DISTINCT "radical") = ?
''', ''',
[...distinctRadicals.map((r) => r.formalVariant), distinctRadicals.length], [...distinctRadicals, distinctRadicals.length],
); );
final kanji = queryResult.map((row) => row['kanji'] as String).toList(); final kanji = queryResult.map((row) => row['kanji'] as String).toList();
-84
View File
@@ -1,84 +0,0 @@
import 'package:jadb/table_names/jmdict.dart';
import 'package:jadb/table_names/kanjidic.dart';
import 'package:jadb/table_names/radkfile.dart';
import 'package:jadb/table_names/tanos_jlpt.dart';
import 'package:sqflite_common/sqlite_api.dart';
class DatasourceVersions {
final String jmdictVersion;
final DateTime jmdictDate;
final String jmdictHash;
final String kanjidic2Version;
final DateTime kanjidic2Date;
final String kanjidic2Hash;
final String radkfileVersion;
final DateTime radkfileDate;
final String radkfileHash;
final String tanosJlptVersion;
final DateTime tanosJlptDate;
final String tanosJlptHash;
const DatasourceVersions({
required this.jmdictVersion,
required this.jmdictDate,
required this.jmdictHash,
required this.kanjidic2Version,
required this.kanjidic2Date,
required this.kanjidic2Hash,
required this.radkfileVersion,
required this.radkfileDate,
required this.radkfileHash,
required this.tanosJlptVersion,
required this.tanosJlptDate,
required this.tanosJlptHash,
});
}
DateTime _parseDateTime(String dateString) {
try {
return DateTime.parse(dateString);
} catch (e) {
if (RegExp(r'^\d{4}-\d{2}$').hasMatch(dateString)) {
return DateTime.parse('$dateString-01');
} else if (RegExp(r'^\d{4}$').hasMatch(dateString)) {
return DateTime.parse('$dateString-01-01');
} else {
throw FormatException('Invalid date format: $dateString');
}
}
}
Future<DatasourceVersions> getDatasourceVersions(
final DatabaseExecutor connection,
) async {
final jmdictVersion = await connection
.query(JMdictTableNames.version)
.then((rows) => rows.first);
final kanjidic2Version = await connection
.query(KANJIDICTableNames.version)
.then((rows) => rows.first);
final radkfileVersion = await connection
.query(RADKFILETableNames.version)
.then((rows) => rows.first);
final tanosJlptVersion = await connection
.query(TanosJLPTTableNames.version)
.then((rows) => rows.first);
return DatasourceVersions(
jmdictVersion: jmdictVersion['version'] as String,
jmdictDate: _parseDateTime(jmdictVersion['date'].toString()),
jmdictHash: jmdictVersion['hash'] as String,
kanjidic2Version: kanjidic2Version['version'] as String,
kanjidic2Date: _parseDateTime(kanjidic2Version['date'].toString()),
kanjidic2Hash: kanjidic2Version['hash'] as String,
radkfileVersion: radkfileVersion['version'] as String,
radkfileDate: _parseDateTime(radkfileVersion['date'].toString()),
radkfileHash: radkfileVersion['hash'] as String,
tanosJlptVersion: tanosJlptVersion['version'] as String,
tanosJlptDate: _parseDateTime(tanosJlptVersion['date'].toString()),
tanosJlptHash: tanosJlptVersion['hash'] as String,
);
}
+11 -11
View File
@@ -68,25 +68,25 @@ String _filterFTSSensitiveCharacters(String word) {
"$tableName"."entryId", "$tableName"."entryId",
100 100
+ (("${tableName}FTS"."reading" = ?) * 10000) + (("${tableName}FTS"."reading" = ?) * 10000)
+ (("$tableName"."orderNum" = 0) * 20) + "JMdict_EntryScore"."score"
+ COALESCE("JMdict_EntryScore"."score", 0)
AS "score" AS "score"
FROM "${tableName}FTS" FROM "${tableName}FTS"
JOIN "$tableName" USING ("elementId") JOIN "$tableName" USING ("elementId")
LEFT JOIN "JMdict_EntryScore" USING ("elementId") JOIN "JMdict_EntryScore" USING ("elementId")
WHERE "${tableName}FTS"."reading" MATCH ? || '*' WHERE "${tableName}FTS"."reading" MATCH ? || '*'
AND "JMdict_EntryScore"."elementId" ${tableName == JMdictTableNames.kanjiElement ? '<' : '>='} 1000000000
), ),
non_fts_results AS ( non_fts_results AS (
SELECT DISTINCT SELECT DISTINCT
"$tableName"."entryId", "$tableName"."entryId",
50 50
+ (("$tableName"."orderNum" = 0) * 20) + "JMdict_EntryScore"."score"
+ COALESCE("JMdict_EntryScore"."score", 0)
AS "score" AS "score"
FROM "$tableName" FROM "$tableName"
LEFT JOIN "JMdict_EntryScore" USING ("elementId") JOIN "JMdict_EntryScore" USING ("elementId")
WHERE "reading" LIKE '%' || ? || '%' WHERE "reading" LIKE '%' || ? || '%'
AND "$tableName"."entryId" NOT IN (SELECT "entryId" FROM "fts_results") AND "$tableName"."entryId" NOT IN (SELECT "entryId" FROM "fts_results")
AND "JMdict_EntryScore"."elementId" ${tableName == JMdictTableNames.kanjiElement ? '<' : '>='} 1000000000
) )
SELECT ${countOnly ? 'COUNT(DISTINCT "entryId") AS count' : '"entryId", MAX("score") AS "score"'} SELECT ${countOnly ? 'COUNT(DISTINCT "entryId") AS count' : '"entryId", MAX("score") AS "score"'}
@@ -198,16 +198,16 @@ Future<List<ScoredEntryId>> _queryEnglish(
''' '''
SELECT SELECT
"${JMdictTableNames.sense}"."entryId", "${JMdictTableNames.sense}"."entryId",
COALESCE(MAX("JMdict_EntryScore"."score"), 0) MAX("JMdict_EntryScore"."score")
+ (("${JMdictTableNames.senseGlossary}"."phrase" = ?1 AND "${JMdictTableNames.sense}"."orderNum" = 0) * 50) + (("${JMdictTableNames.senseGlossary}"."phrase" = ?1 AND "${JMdictTableNames.sense}"."orderNum" = 0) * 50)
+ (("${JMdictTableNames.senseGlossary}"."phrase" = ?1 AND "${JMdictTableNames.sense}"."orderNum" = 1) * 30) + (("${JMdictTableNames.senseGlossary}"."phrase" = ?1 AND "${JMdictTableNames.sense}"."orderNum" = 1) * 30)
+ (("${JMdictTableNames.senseGlossary}"."phrase" = ?1 AND "${JMdictTableNames.sense}"."orderNum" > 1) * 20) + (("${JMdictTableNames.senseGlossary}"."phrase" = ?1) * 20)
as "score" as "score"
FROM "${JMdictTableNames.senseGlossary}" FROM "${JMdictTableNames.senseGlossary}"
JOIN "${JMdictTableNames.sense}" USING ("senseId") JOIN "${JMdictTableNames.sense}" USING ("senseId")
LEFT JOIN "JMdict_EntryScore" USING ("entryId") JOIN "JMdict_EntryScore" USING ("entryId")
WHERE "${JMdictTableNames.senseGlossary}"."phrase" LIKE ?2 WHERE "${JMdictTableNames.senseGlossary}"."phrase" LIKE ?2
GROUP BY "${JMdictTableNames.sense}"."entryId" GROUP BY "JMdict_EntryScore"."entryId"
ORDER BY ORDER BY
"score" DESC, "score" DESC,
"${JMdictTableNames.sense}"."entryId" ASC "${JMdictTableNames.sense}"."entryId" ASC
@@ -215,7 +215,7 @@ Future<List<ScoredEntryId>> _queryEnglish(
${offset != null ? 'OFFSET ?4' : ''} ${offset != null ? 'OFFSET ?4' : ''}
''' '''
.trim(), .trim(),
[word, '%${word.replaceAll('%', '')}%', ?pageSize, ?offset], [word, '%${word.replaceAll('%', '')}%', if (pageSize != null) pageSize, if (offset != null) offset],
); );
return result return result
-2
View File
@@ -11,7 +11,6 @@ abstract class JMdictTableNames {
static const String senseDialect = 'JMdict_SenseDialect'; static const String senseDialect = 'JMdict_SenseDialect';
static const String senseField = 'JMdict_SenseField'; static const String senseField = 'JMdict_SenseField';
static const String senseGlossary = 'JMdict_SenseGlossary'; static const String senseGlossary = 'JMdict_SenseGlossary';
static const String senseGlossaryType = 'JMdict_SenseGlossaryType';
static const String senseInfo = 'JMdict_SenseInfo'; static const String senseInfo = 'JMdict_SenseInfo';
static const String senseMisc = 'JMdict_SenseMisc'; static const String senseMisc = 'JMdict_SenseMisc';
static const String sensePOS = 'JMdict_SensePOS'; static const String sensePOS = 'JMdict_SensePOS';
@@ -34,7 +33,6 @@ abstract class JMdictTableNames {
senseDialect, senseDialect,
senseField, senseField,
senseGlossary, senseGlossary,
senseGlossaryType,
senseInfo, senseInfo,
senseMisc, senseMisc,
sensePOS, sensePOS,
+9
View File
@@ -0,0 +1,9 @@
abstract class KanjiVGTableNames {
static const String version = 'KanjiVG_Version';
static const String entry = 'KanjiVG_Entry';
static const String path = 'KanjiVG_Path';
static const String strokeNumber = 'KanjiVG_StrokeNumber';
static const String pathGroup = 'KanjiVG_PathGroup';
static Set<String> get allTables => {version, entry, path, strokeNumber, pathGroup};
}
-1
View File
@@ -1 +0,0 @@
const int jadbSchemaVersion = 1;
+13 -13
View File
@@ -139,6 +139,8 @@ CREATE TABLE "JMdict_SenseRestrictedToReading" (
CREATE TABLE "JMdict_SenseSeeAlso" ( CREATE TABLE "JMdict_SenseSeeAlso" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"xrefEntryId" INTEGER NOT NULL, "xrefEntryId" INTEGER NOT NULL,
"seeAlsoReading" TEXT,
"seeAlsoKanji" TEXT,
"seeAlsoSense" INTEGER, "seeAlsoSense" INTEGER,
-- For some entries, the cross reference is ambiguous. This means that while the ingestion -- For some entries, the cross reference is ambiguous. This means that while the ingestion
-- has determined some xrefEntryId, it is not guaranteed to be the correct one. -- has determined some xrefEntryId, it is not guaranteed to be the correct one.
@@ -151,14 +153,18 @@ CREATE TABLE "JMdict_SenseSeeAlso" (
END END
) VIRTUAL, ) VIRTUAL,
FOREIGN KEY ("xrefEntryId", "seeAlsoKanji") REFERENCES "JMdict_KanjiElement"("entryId", "reading"),
FOREIGN KEY ("xrefEntryId", "seeAlsoReading") REFERENCES "JMdict_ReadingElement"("entryId", "reading"),
FOREIGN KEY ("seeAlsoSenseKey") REFERENCES "JMdict_Sense"("senseId"), FOREIGN KEY ("seeAlsoSenseKey") REFERENCES "JMdict_Sense"("senseId"),
PRIMARY KEY ("senseId", "xrefEntryId", "seeAlsoSense") UNIQUE("senseId", "xrefEntryId", "seeAlsoReading", "seeAlsoKanji", "seeAlsoSense")
); );
CREATE TABLE "JMdict_SenseAntonym" ( CREATE TABLE "JMdict_SenseAntonym" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"xrefEntryId" INTEGER NOT NULL, "xrefEntryId" INTEGER NOT NULL,
"antonymReading" TEXT,
"antonymKanji" TEXT,
"antonymSense" INTEGER, "antonymSense" INTEGER,
-- For some entries, the cross reference is ambiguous. This means that while the ingestion -- For some entries, the cross reference is ambiguous. This means that while the ingestion
-- has determined some xrefEntryId, it is not guaranteed to be the correct one. -- has determined some xrefEntryId, it is not guaranteed to be the correct one.
@@ -171,9 +177,11 @@ CREATE TABLE "JMdict_SenseAntonym" (
END END
) VIRTUAL, ) VIRTUAL,
FOREIGN KEY ("xrefEntryId", "antonymKanji") REFERENCES "JMdict_KanjiElement"("entryId", "reading"),
FOREIGN KEY ("xrefEntryId", "antonymReading") REFERENCES "JMdict_ReadingElement"("entryId", "reading"),
FOREIGN KEY ("antonymSenseKey") REFERENCES "JMdict_Sense"("senseId"), FOREIGN KEY ("antonymSenseKey") REFERENCES "JMdict_Sense"("senseId"),
PRIMARY KEY ("senseId", "xrefEntryId", "antonymSense") UNIQUE("senseId", "xrefEntryId", "antonymReading", "antonymKanji", "antonymSense")
); );
-- These cross references are going to be mostly accessed from a sense -- These cross references are going to be mostly accessed from a sense
@@ -224,21 +232,13 @@ CREATE TABLE "JMdict_SenseDialect" (
CREATE TABLE "JMdict_SenseGlossary" ( CREATE TABLE "JMdict_SenseGlossary" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"phrase" TEXT NOT NULL, "phrase" TEXT NOT NULL,
-- "language" CHAR(3) NOT NULL DEFAULT "eng", "language" CHAR(3) NOT NULL DEFAULT "eng",
-- PRIMARY KEY ("senseId", "language", "phrase") "type" TEXT,
PRIMARY KEY ("senseId", "phrase") PRIMARY KEY ("senseId", "language", "phrase")
) WITHOUT ROWID; ) WITHOUT ROWID;
CREATE INDEX "JMdict_SenseGlossary_byPhrase" ON JMdict_SenseGlossary("phrase"); CREATE INDEX "JMdict_SenseGlossary_byPhrase" ON JMdict_SenseGlossary("phrase");
CREATE TABLE "JMdict_SenseGlossaryType" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"phrase" TEXT NOT NULL,
"type" TEXT NOT NULL,
PRIMARY KEY ("senseId", "phrase", "type"),
FOREIGN KEY ("senseId", "phrase") REFERENCES "JMdict_SenseGlossary"("senseId", "phrase")
) WITHOUT ROWID;
CREATE TABLE "JMdict_SenseInfo" ( CREATE TABLE "JMdict_SenseInfo" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"info" TEXT NOT NULL, "info" TEXT NOT NULL,
+7 -20
View File
@@ -1,6 +1,6 @@
CREATE TABLE "JMdict_EntryScore" ( CREATE TABLE "JMdict_EntryScore" (
"elementId" INTEGER PRIMARY KEY, "elementId" INTEGER PRIMARY KEY,
"score" INTEGER NOT NULL, "score" INTEGER NOT NULL DEFAULT 0,
"common" BOOLEAN NOT NULL DEFAULT FALSE, "common" BOOLEAN NOT NULL DEFAULT FALSE,
"entryId" INTEGER NOT NULL GENERATED ALWAYS AS (("elementId" / 100) % 10000000) STORED, "entryId" INTEGER NOT NULL GENERATED ALWAYS AS (("elementId" / 100) % 10000000) STORED,
@@ -15,7 +15,8 @@ CREATE TABLE "JMdict_EntryScore" (
CREATE INDEX "JMdict_EntryScore_byElementId_byScore" ON "JMdict_EntryScore"("elementId", "score"); CREATE INDEX "JMdict_EntryScore_byElementId_byScore" ON "JMdict_EntryScore"("elementId", "score");
CREATE INDEX "JMdict_EntryScore_byScore" ON "JMdict_EntryScore"("score"); CREATE INDEX "JMdict_EntryScore_byScore" ON "JMdict_EntryScore"("score");
CREATE INDEX "JMdict_EntryScore_byCommon" ON "JMdict_EntryScore"("common") WHERE "common" = TRUE; CREATE INDEX "JMdict_EntryScore_byElementId_byCommon" ON "JMdict_EntryScore"("elementId", "common");
CREATE INDEX "JMdict_EntryScore_byCommon" ON "JMdict_EntryScore"("common");
-- NOTE: these views are deduplicated in order not to perform an unnecessary -- NOTE: these views are deduplicated in order not to perform an unnecessary
-- UNION on every trigger -- UNION on every trigger
@@ -45,7 +46,7 @@ SELECT
+ (("spec" IS 2) * 5) + (("spec" IS 2) * 5)
+ (("gai" IS 1) * 10) + (("gai" IS 1) * 10)
+ (("gai" IS 2) * 5) + (("gai" IS 2) * 5)
-- + (("orderNum" IS 0) * 20) + (("orderNum" IS 0) * 20)
- (substr(COALESCE("JMdict_JLPTTag"."jlptLevel", 'N0'), 2) * -5) - (substr(COALESCE("JMdict_JLPTTag"."jlptLevel", 'N0'), 2) * -5)
AS "score" AS "score"
FROM "JMdict_ReadingElement" FROM "JMdict_ReadingElement"
@@ -76,7 +77,7 @@ SELECT
+ (("spec" IS 2) * 5) + (("spec" IS 2) * 5)
+ (("gai" IS 1) * 10) + (("gai" IS 1) * 10)
+ (("gai" IS 2) * 5) + (("gai" IS 2) * 5)
-- + (("orderNum" IS 0) * 20) + (("orderNum" IS 0) * 20)
- (substr(COALESCE("JMdict_JLPTTag"."jlptLevel", 'N0'), 2) * -5) - (substr(COALESCE("JMdict_JLPTTag"."jlptLevel", 'N0'), 2) * -5)
AS "score" AS "score"
FROM "JMdict_KanjiElement" FROM "JMdict_KanjiElement"
@@ -102,8 +103,7 @@ BEGIN
) )
SELECT "elementId", "score", "common" SELECT "elementId", "score", "common"
FROM "JMdict_EntryScoreView_Reading" FROM "JMdict_EntryScoreView_Reading"
WHERE "elementId" = NEW."elementId" WHERE "elementId" = NEW."elementId";
AND "score" > 0;
END; END;
CREATE TRIGGER "JMdict_EntryScore_Update_JMdict_ReadingElement" CREATE TRIGGER "JMdict_EntryScore_Update_JMdict_ReadingElement"
@@ -116,10 +116,6 @@ BEGIN
"common" = "JMdict_EntryScoreView_Reading"."common" "common" = "JMdict_EntryScoreView_Reading"."common"
FROM "JMdict_EntryScoreView_Reading" FROM "JMdict_EntryScoreView_Reading"
WHERE "elementId" = NEW."elementId"; WHERE "elementId" = NEW."elementId";
DELETE FROM "JMdict_EntryScore"
WHERE "elementId" = NEW."elementId"
AND "score" <= 0;
END; END;
CREATE TRIGGER "JMdict_EntryScore_Delete_JMdict_ReadingElement" CREATE TRIGGER "JMdict_EntryScore_Delete_JMdict_ReadingElement"
@@ -141,8 +137,7 @@ BEGIN
) )
SELECT "elementId", "score", "common" SELECT "elementId", "score", "common"
FROM "JMdict_EntryScoreView_Kanji" FROM "JMdict_EntryScoreView_Kanji"
WHERE "elementId" = NEW."elementId" WHERE "elementId" = NEW."elementId";
AND "score" > 0;
END; END;
CREATE TRIGGER "JMdict_EntryScore_Update_JMdict_KanjiElement" CREATE TRIGGER "JMdict_EntryScore_Update_JMdict_KanjiElement"
@@ -155,10 +150,6 @@ BEGIN
"common" = "JMdict_EntryScoreView_Kanji"."common" "common" = "JMdict_EntryScoreView_Kanji"."common"
FROM "JMdict_EntryScoreView_Kanji" FROM "JMdict_EntryScoreView_Kanji"
WHERE "elementId" = NEW."elementId"; WHERE "elementId" = NEW."elementId";
DELETE FROM "JMdict_EntryScore"
WHERE "elementId" = NEW."elementId"
AND "score" <= 0;
END; END;
CREATE TRIGGER "JMdict_EntryScore_Delete_JMdict_KanjiElement" CREATE TRIGGER "JMdict_EntryScore_Delete_JMdict_KanjiElement"
@@ -208,8 +199,4 @@ BEGIN
WHERE "JMdict_EntryScoreView"."entryId" = OLD."entryId" WHERE "JMdict_EntryScoreView"."entryId" = OLD."entryId"
AND "JMdict_EntryScore"."entryId" = OLD."entryId" AND "JMdict_EntryScore"."entryId" = OLD."entryId"
AND "JMdict_EntryScoreView"."elementId" = "JMdict_EntryScore"."elementId"; AND "JMdict_EntryScoreView"."elementId" = "JMdict_EntryScore"."elementId";
DELETE FROM "JMdict_EntryScore"
WHERE "elementId" = OLD."elementId"
AND "score" <= 0;
END; END;
+1
View File
@@ -18,3 +18,4 @@ CREATE TABLE "RADKFILE" (
) WITHOUT ROWID; ) WITHOUT ROWID;
CREATE INDEX "RADK" ON "RADKFILE"("radical"); CREATE INDEX "RADK" ON "RADKFILE"("radical");
CREATE INDEX "KRAD" ON "RADKFILE"("kanji");
@@ -4,6 +4,7 @@ CREATE TABLE "XREF__KANJIDIC_Radical__RADKFILE"(
PRIMARY KEY ("radicalId", "radicalSymbol") PRIMARY KEY ("radicalId", "radicalSymbol")
) WITHOUT ROWID; ) WITHOUT ROWID;
CREATE INDEX "XREF__KANJIDIC_Radical__RADKFILE__byRadicalId" ON "XREF__KANJIDIC_Radical__RADKFILE"("radicalId");
CREATE INDEX "XREF__KANJIDIC_Radical__RADKFILE__byRadicalSymbol" ON "XREF__KANJIDIC_Radical__RADKFILE"("radicalSymbol"); CREATE INDEX "XREF__KANJIDIC_Radical__RADKFILE__byRadicalSymbol" ON "XREF__KANJIDIC_Radical__RADKFILE"("radicalSymbol");
/* Source: https://ctext.org/kangxi-zidian */ /* Source: https://ctext.org/kangxi-zidian */
@@ -1,10 +1,10 @@
-- CREATE TABLE "XREF__JMdict_KanjiElement__KANJIDIC_Character"( CREATE TABLE "XREF__JMdict_KanjiElement__KANJIDIC_Character"(
-- "entryId" INTEGER NOT NULL, "entryId" INTEGER NOT NULL,
-- "reading" TEXT NOT NULL, "reading" TEXT NOT NULL,
-- "kanji" CHAR(1) NOT NULL REFERENCES "KANJIDIC_Character"("literal"), "kanji" CHAR(1) NOT NULL REFERENCES "KANJIDIC_Character"("literal"),
-- PRIMARY KEY ("entryId", "reading", "kanji"), PRIMARY KEY ("entryId", "reading", "kanji"),
-- FOREIGN KEY ("entryId", "reading") REFERENCES "JMdict_KanjiElement"("entryId", "reading") FOREIGN KEY ("entryId", "reading") REFERENCES "JMdict_KanjiElement"("entryId", "reading")
-- ) WITHOUT ROWID; ) WITHOUT ROWID;
-- CREATE INDEX "XREF__JMdict_KanjiElement__KANJIDIC_Character__byEntryId_byReading" ON "XREF__JMdict_KanjiElement__KANJIDIC_Character"("entryId", "reading"); CREATE INDEX "XREF__JMdict_KanjiElement__KANJIDIC_Character__byEntryId_byReading" ON "XREF__JMdict_KanjiElement__KANJIDIC_Character"("entryId", "reading");
-- CREATE INDEX "XREF__JMdict_KanjiElement__KANJIDIC_Character__byKanji" ON "XREF__JMdict_KanjiElement__KANJIDIC_Character"("kanji"); CREATE INDEX "XREF__JMdict_KanjiElement__KANJIDIC_Character__byKanji" ON "XREF__JMdict_KanjiElement__KANJIDIC_Character"("kanji");
+7 -1
View File
@@ -75,7 +75,13 @@ SELECT DISTINCT "radical" FROM "RADKFILE";
CREATE VIEW "JMdict_CombinedEntryScore" CREATE VIEW "JMdict_CombinedEntryScore"
AS AS
SELECT SELECT
CASE
WHEN "JMdict_EntryScore"."type" = 'k'
THEN (SELECT entryId FROM "JMdict_KanjiElement" WHERE "elementId" = "JMdict_EntryScore"."elementId")
WHEN "JMdict_EntryScore"."type" = 'r'
THEN (SELECT entryId FROM "JMdict_ReadingElement" WHERE "elementId" = "JMdict_EntryScore"."elementId")
END AS "entryId",
MAX("JMdict_EntryScore"."score") AS "score", MAX("JMdict_EntryScore"."score") AS "score",
MAX("JMdict_EntryScore"."common") AS "common" MAX("JMdict_EntryScore"."common") AS "common"
FROM "JMdict_EntryScore" FROM "JMdict_EntryScore"
GROUP BY "JMdict_EntryScore"."entryId"; GROUP BY "entryId";
+45
View File
@@ -0,0 +1,45 @@
CREATE TABLE "KanjiVG_Version" (
"version" VARCHAR(10) PRIMARY KEY NOT NULL,
"date" DATE NOT NULL,
"hash" VARCHAR(64) NOT NULL
) WITHOUT ROWID;
CREATE TRIGGER "KanjiVG_Version_SingleRow"
BEFORE INSERT ON "KanjiVG_Version"
WHEN (SELECT COUNT(*) FROM "KanjiVG_Version") >= 1
BEGIN
SELECT RAISE(FAIL, 'Only one row allowed in KanjiVG_Version');
END;
CREATE TABLE "KanjiVG_Entry" (
"character" CHAR(1) PRIMARY KEY NOT NULL
) WITHOUT ROWID;
CREATE TABLE "KanjiVG_StrokeNumber" (
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
"strokeNum" INTEGER NOT NULL,
"x" REAL NOT NULL,
"y" REAL NOT NULL,
PRIMARY KEY ("character", "strokeNum")
) WITHOUT ROWID;
CREATE TABLE "KanjiVG_Path" (
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
"pathId" TEXT NOT NULL,
"type" VARCHAR(10) NOT NULL,
"svgPath" TEXT NOT NULL,
PRIMARY KEY ("character", "pathId")
) WITHOUT ROWID;
CREATE TABLE "KanjiVG_PathGroup" (
"character" CHAR(1) NOT NULL REFERENCES "KanjiVG_Entry"("character"),
"groupId" TEXT NOT NULL,
"parentGroupId" TEXT REFERENCES "KanjiVG_PathGroup"("groupId"),
"element" TEXT,
"original" TEXT,
"position" VARCHAR(10),
"radical" TEXT,
"part" INTEGER,
PRIMARY KEY ("character", "groupId"),
CHECK ("position" IN ('bottom', 'kamae', 'kamaec', 'left', 'middle', 'nyo', 'nyoc', 'right', 'tare', 'tarec', 'top') OR "position" IS NULL)
) WITHOUT ROWID;
+1 -19
View File
@@ -19,31 +19,13 @@ stdenvNoCC.mkDerivation {
sqlite sqlite
]; ];
env = {
JMDICT_VERSION = jmdict.version;
JMDICT_DATE = jmdict.date;
JMDICT_HASH = jmdict.hash;
KANJIDIC_VERSION = kanjidic2.version;
KANJIDIC_DATE = kanjidic2.date;
KANJIDIC_HASH = kanjidic2.hash;
RADKFILE_VERSION = radkfile.version;
RADKFILE_DATE = radkfile.date;
RADKFILE_HASH = radkfile.hash;
TANOS_JLPT_VERSION = tanos-jlpt.version;
TANOS_JLPT_DATE = tanos-jlpt.date;
TANOS_JLPT_HASH = tanos-jlpt.hash;
};
buildPhase = '' buildPhase = ''
runHook preBuild runHook preBuild
mkdir -p data mkdir -p data
ln -s '${jmdict}'/* data/ ln -s '${jmdict}'/* data/
ln -s '${kanjidic2}'/* data/
ln -s '${radkfile}'/* data/ ln -s '${radkfile}'/* data/
ln -s '${kanjidic2}'/* data/
ln -s '${tanos-jlpt}' data/tanos-jlpt ln -s '${tanos-jlpt}' data/tanos-jlpt
for migration in migrations/*.sql; do for migration in migrations/*.sql; do
+31 -39
View File
@@ -5,18 +5,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b sha256: "8d718c5c58904f9937290fd5dbf2d6a0e02456867706bfb6cd7b81d394e738d5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "100.0.0" version: "98.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf" sha256: "6141ad5d092d1e1d13929c0504658bbeccc1703505830d7c26e859908f5efc88"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "12.0.0"
args: args:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -61,10 +61,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: code_assets name: code_assets
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.0.0"
collection: collection:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -149,10 +149,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: hooks name: hooks
sha256: "62ae9bb76d02526c7c2110a19b6e6ad788fe28d35e553e35efb02a41a46ab43a" sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "1.0.2"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
@@ -197,10 +197,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.20" version: "0.12.19"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -221,10 +221,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: native_toolchain_c name: native_toolchain_c
sha256: f59351d28f49520cd3a74eb1f41c5f19ae15e53c65a3231d14af672e46510a96 sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.19.1" version: "0.17.6"
node_preamble: node_preamble:
dependency: transitive dependency: transitive
description: description:
@@ -273,14 +273,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
record_use:
dependency: transitive
description:
name: record_use
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@@ -341,26 +333,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqflite_common name: sqflite_common
sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465" sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.8" version: "2.5.6"
sqflite_common_ffi: sqflite_common_ffi:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqflite_common_ffi name: sqflite_common_ffi
sha256: cd0c7f7de39a08f2d54ef144d9058c46eca8461879aaa648025643455c1e5a20 sha256: c59fcdc143839a77581f7a7c4de018e53682408903a0a0800b95ef2dc4033eff
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0+3" version: "2.4.0+2"
sqlite3: sqlite3:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqlite3 name: sqlite3
sha256: "9488c7d2cdb1091c91cacf7e207cff81b28bff8e366f042bad3afe7d34afe189" sha256: caa693ad15a587a2b4fde093b728131a1827903872171089dedb16f7665d3a91
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.2" version: "3.2.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -389,10 +381,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: synchronized name: synchronized
sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5" sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.0+1" version: "3.4.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -405,26 +397,26 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: test name: test
sha256: ca578dc12bb8b2f40b67b7d3bd2fac4f31c01a6ff7130a14e2597b919934507f sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.31.1" version: "1.31.0"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.12" version: "0.7.11"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: d2e98ec12998368dc59ddd47ab709f2cd55acd6b66dc7db764455a44082f4bc5 sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.18" version: "0.6.17"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -437,10 +429,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.2.0" version: "15.0.2"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@@ -485,10 +477,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: xml name: xml
sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4" sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "6.6.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@@ -498,4 +490,4 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.11.0 <4.0.0" dart: ">=3.10.1 <4.0.0"
+1 -1
View File
@@ -15,7 +15,7 @@ dependencies:
sqflite_common: ^2.5.0 sqflite_common: ^2.5.0
sqflite_common_ffi: ^2.3.0 sqflite_common_ffi: ^2.3.0
sqlite3: ^3.1.6 sqlite3: ^3.1.6
xml: '>=6.0.0 < 8.0.0' xml: ^6.5.0
dev_dependencies: dev_dependencies:
benchmark_harness: ^2.4.0 benchmark_harness: ^2.4.0
-93
View File
@@ -1,93 +0,0 @@
import 'dart:convert';
import 'package:jadb/models/kanji_search/kanji_search_radical.dart';
import 'package:jadb/models/kanji_search/kanji_search_result.dart';
import 'package:test/test.dart';
Object? _roundTripJson(Object? value) => jsonDecode(jsonEncode(value));
Map<String, dynamic> _roundTripMap(Object? json) =>
Map<String, dynamic>.from(_roundTripJson(json) as Map);
void main() {
test('KanjiSearchRadical JSON serialization roundtrip', () {
const radical = KanjiSearchRadical(
symbol: '',
names: ['ひと', 'にんべん'],
forms: [''],
meanings: ['person', 'human'],
);
final restored = KanjiSearchRadical.fromJson(
_roundTripMap(radical.toJson()),
);
expect(restored, equals(radical));
});
test('KanjiSearchResult JSON serialization roundtrip', () {
const result = KanjiSearchResult(
kanji: '',
taughtIn: 1,
jlptLevel: 'N5',
newspaperFrequencyRank: 1234,
strokeCount: 6,
meanings: ['rest', 'day off'],
kunyomi: ['やす.む', 'やす.まる'],
onyomi: ['キュウ'],
radical: KanjiSearchRadical(
symbol: '',
names: ['ひと', 'にんべん'],
forms: [''],
meanings: ['person', 'human'],
),
parts: ['', ''],
codepoints: {'ucs': '4F11', 'jis208': '1-22-57'},
nanori: ['やす'],
alternativeLanguageReadings: {
'korean': [''],
'pinyin': ['xiū'],
},
strokeMiscounts: [5, 7],
queryCodes: {
'skip': ['1-2-4'],
'fourCorner': ['2429.0'],
},
dictionaryReferences: {'nelson_c': '122', 'heisig': '457'},
);
final restored = KanjiSearchResult.fromJson(_roundTripMap(result.toJson()));
expect(restored, equals(result));
});
test(
'KanjiSearchResult JSON serialization roundtrip - nullable and empty fields',
() {
const result = KanjiSearchResult(
kanji: '',
taughtIn: null,
jlptLevel: null,
newspaperFrequencyRank: null,
strokeCount: 3,
meanings: ['iteration mark'],
kunyomi: [],
onyomi: [],
radical: null,
parts: [],
codepoints: {'ucs': '3005'},
nanori: [],
alternativeLanguageReadings: {},
strokeMiscounts: [],
queryCodes: {},
dictionaryReferences: {},
);
final restored = KanjiSearchResult.fromJson(
_roundTripMap(result.toJson()),
);
expect(restored, equals(result));
},
);
}
-282
View File
@@ -1,282 +0,0 @@
import 'dart:convert';
import 'package:jadb/models/common/jlpt_level.dart';
import 'package:jadb/models/jmdict/jmdict_dialect.dart';
import 'package:jadb/models/jmdict/jmdict_field.dart';
import 'package:jadb/models/jmdict/jmdict_kanji_info.dart';
import 'package:jadb/models/jmdict/jmdict_misc.dart';
import 'package:jadb/models/jmdict/jmdict_pos.dart';
import 'package:jadb/models/jmdict/jmdict_reading_info.dart';
import 'package:jadb/models/word_search/word_search_match_span.dart';
import 'package:jadb/models/word_search/word_search_result.dart';
import 'package:jadb/models/word_search/word_search_ruby.dart';
import 'package:jadb/models/word_search/word_search_sense.dart';
import 'package:jadb/models/word_search/word_search_sense_language_source.dart';
import 'package:jadb/models/word_search/word_search_sources.dart';
import 'package:jadb/models/word_search/word_search_xref_entry.dart';
import 'package:test/test.dart';
Object? _roundTripJson(Object? value) => jsonDecode(jsonEncode(value));
Map<String, dynamic> _roundTripMap(Object? json) =>
Map<String, dynamic>.from(_roundTripJson(json) as Map);
Map<String, Object?> _roundTripObjectMap(Object? json) =>
Map<String, Object?>.from(_roundTripJson(json) as Map);
void _expectScalarRoundTrip<T>({
required Iterable<T> values,
required Object? Function(T value) toJson,
required T Function(Object? json) fromJson,
}) {
for (final value in values) {
expect(
fromJson(_roundTripJson(toJson(value))),
equals(value),
reason: 'Roundtrip failed for $value',
);
}
}
void _expectMapRoundTrip<T>({
required Iterable<T> values,
required Map<String, Object?> Function(T value) toJson,
required T Function(Map<String, Object?> json) fromJson,
}) {
for (final value in values) {
expect(
fromJson(_roundTripObjectMap(toJson(value))),
equals(value),
reason: 'Roundtrip failed for $value',
);
}
}
WordSearchResult _buildNestedXrefResult() => WordSearchResult(
score: 7,
entryId: 300,
isCommon: false,
japanese: [WordSearchRuby(base: '補助', furigana: 'ほじょ')],
kanjiInfo: const {},
readingInfo: const {},
senses: const [],
jlptLevel: JlptLevel.none,
sources: const WordSearchSources(jmdict: true, jmnedict: false),
);
WordSearchSense _buildSense() => WordSearchSense(
englishDefinitions: ['kana', 'syllabary'],
partsOfSpeech: [JMdictPOS.n],
seeAlso: [
WordSearchXrefEntry(
entryId: 300,
ambiguous: false,
baseWord: '仮名遣い',
furigana: 'かなづかい',
xrefResult: _buildNestedXrefResult(),
),
],
antonyms: const [
WordSearchXrefEntry(
entryId: 301,
ambiguous: true,
baseWord: '漢字',
furigana: 'かんじ',
xrefResult: null,
),
],
restrictedToReading: ['かな'],
restrictedToKanji: ['仮名'],
fields: [JMdictField.linguistics],
dialects: [JMdictDialect.kansai],
misc: [JMdictMisc.onlyKana, JMdictMisc.rare],
info: ['Typically written using kana alone.'],
languageSource: const [
WordSearchSenseLanguageSource(
language: 'por',
phrase: 'canoa',
fullyDescribesSense: false,
constructedFromSmallerWords: true,
),
],
);
WordSearchResult _buildWordSearchResult({
List<WordSearchMatchSpan>? matchSpans,
}) => WordSearchResult(
score: 42,
entryId: 123,
isCommon: true,
japanese: [
WordSearchRuby(base: '仮名', furigana: 'かな'),
WordSearchRuby(base: 'かな'),
],
kanjiInfo: {'仮名': JMdictKanjiInfo.rK, '': JMdictKanjiInfo.ateji},
readingInfo: {'かな': JMdictReadingInfo.gikun, 'カナ': JMdictReadingInfo.rk},
senses: [_buildSense()],
jlptLevel: JlptLevel.n5,
sources: const WordSearchSources(jmdict: true, jmnedict: true),
matchSpans: matchSpans,
);
void main() {
test('JlptLevel JSON serialization roundtrip', () {
_expectScalarRoundTrip<JlptLevel>(
values: JlptLevel.values,
toJson: (value) => value.toJson(),
fromJson: JlptLevel.fromJson,
);
});
test('JMdictDialect JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictDialect>(
values: JMdictDialect.values,
toJson: (value) => value.toJson(),
fromJson: JMdictDialect.fromJson,
);
});
test('JMdictField JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictField>(
values: JMdictField.values,
toJson: (value) => value.toJson(),
fromJson: JMdictField.fromJson,
);
});
test('JMdictKanjiInfo JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictKanjiInfo>(
values: JMdictKanjiInfo.values,
toJson: (value) => value.toJson(),
fromJson: JMdictKanjiInfo.fromJson,
);
});
test('JMdictMisc JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictMisc>(
values: JMdictMisc.values,
toJson: (value) => value.toJson(),
fromJson: JMdictMisc.fromJson,
);
});
test('JMdictPOS JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictPOS>(
values: JMdictPOS.values,
toJson: (value) => value.toJson(),
fromJson: JMdictPOS.fromJson,
);
});
test('JMdictReadingInfo JSON serialization roundtrip', () {
_expectMapRoundTrip<JMdictReadingInfo>(
values: JMdictReadingInfo.values,
toJson: (value) => value.toJson(),
fromJson: JMdictReadingInfo.fromJson,
);
});
test('WordSearchMatchSpan JSON serialization roundtrip', () {
final span = WordSearchMatchSpan(
spanType: WordSearchMatchSpanType.sense,
index: 2,
subIndex: 3,
start: 4,
end: 8,
);
final restored = WordSearchMatchSpan.fromJson(_roundTripMap(span.toJson()));
expect(restored, equals(span));
expect(restored.subIndex, equals(span.subIndex));
});
test('WordSearchRuby JSON serialization roundtrip', () {
final ruby = WordSearchRuby(base: '仮名', furigana: 'かな');
final restored = WordSearchRuby.fromJson(_roundTripMap(ruby.toJson()));
expect(restored.toJson(), equals(ruby.toJson()));
});
test('WordSearchSenseLanguageSource JSON serialization roundtrip', () {
const source = WordSearchSenseLanguageSource(
language: 'por',
phrase: 'canoa',
fullyDescribesSense: false,
constructedFromSmallerWords: true,
);
final restored = WordSearchSenseLanguageSource.fromJson(
_roundTripMap(source.toJson()),
);
expect(restored.toJson(), equals(source.toJson()));
});
test('WordSearchSources JSON serialization roundtrip', () {
const sources = WordSearchSources(jmdict: false, jmnedict: true);
final restored = WordSearchSources.fromJson(
_roundTripMap(sources.toJson()),
);
expect(restored.toJson(), equals(sources.toJson()));
expect(restored.sqlValue, equals(sources.sqlValue));
});
test('WordSearchXrefEntry JSON serialization roundtrip', () {
final entry = WordSearchXrefEntry(
entryId: 300,
ambiguous: false,
baseWord: '仮名遣い',
furigana: 'かなづかい',
xrefResult: _buildNestedXrefResult(),
);
final restored = WordSearchXrefEntry.fromJson(
_roundTripMap(entry.toJson()),
);
expect(restored.toJson(), equals(entry.toJson()));
expect(restored.xrefResult?.toJson(), equals(entry.xrefResult?.toJson()));
});
test('WordSearchSense JSON serialization roundtrip', () {
final sense = _buildSense();
final restored = WordSearchSense.fromJson(_roundTripMap(sense.toJson()));
expect(restored.toJson(), equals(sense.toJson()));
});
test('WordSearchResult JSON serialization roundtrip', () {
final result = _buildWordSearchResult();
final restored = WordSearchResult.fromJson(_roundTripMap(result.toJson()));
expect(restored.toJson(), equals(result.toJson()));
expect(restored.matchSpans, isNull);
});
test('WordSearchResult leaves matchSpans out of JSON', () {
final result = _buildWordSearchResult(
matchSpans: [
WordSearchMatchSpan(
spanType: WordSearchMatchSpanType.sense,
index: 0,
subIndex: 1,
start: 0,
end: 4,
),
],
);
final json = _roundTripMap(result.toJson());
final restored = WordSearchResult.fromJson(json);
expect(json.containsKey('matchSpans'), isFalse);
expect(restored.matchSpans, isNull);
expect(restored.toJson(), equals(result.toJson()));
});
}
-12
View File
@@ -1,12 +0,0 @@
import 'package:jadb/search.dart';
import 'package:test/test.dart';
import 'setup_database_connection.dart';
void main() {
test('Retrieve datasource versions', () async {
final connection = await setupDatabaseConnection();
final result = await connection.jadbGetDatasourceVersions();
expect(result, isNotNull);
});
}
-85
View File
@@ -1,85 +0,0 @@
import 'package:collection/collection.dart';
import 'package:jadb/const_data/radicals.dart';
import 'package:jadb/search.dart';
import 'package:jadb/table_names/radkfile.dart';
import 'package:test/test.dart';
import 'setup_database_connection.dart';
void main() {
test('All constant radicals should exist in the database', () async {
final connection = await setupDatabaseConnection();
final allRadicalsInDb = await connection.query(
RADKFILETableNames.radkfile,
columns: ['radical'],
distinct: true,
);
final radicalsInDb = allRadicalsInDb
.map((e) => e['radical'] as String)
.toSet();
final missingRadicals = radicals.values.flattenedToSet
.map((e) => e.formalVariant)
.toSet()
.difference(radicalsInDb);
expect(
missingRadicals,
isEmpty,
reason: 'Missing radicals in database: $missingRadicals',
);
});
test(
'All radicals in database should be in the constant radical list',
() async {
final connection = await setupDatabaseConnection();
final allRadicalsInDb = await connection.query(
RADKFILETableNames.radkfile,
columns: ['radical'],
distinct: true,
);
final radicalsInDb = allRadicalsInDb
.map((e) => e['radical'] as String)
.toSet();
final extraRadicals = radicalsInDb.difference(
radicals.values.flattenedToSet.map((e) => e.formalVariant).toSet(),
);
expect(
extraRadicals,
isEmpty,
reason:
'Extra radicals in database missing in the constant list: $extraRadicals',
);
},
);
test('All constant radicals are located in the correct stroke count group', () {
for (final mapEntry in radicals.entries) {
final strokeCount = mapEntry.key;
final radicalsInGroup = mapEntry.value;
for (final radical in radicalsInGroup) {
expect(
strokeCount,
radical.strokeCount,
reason:
'Radical ${radical.formalVariant} should have stroke count $strokeCount but has ${radical.strokeCount}',
);
}
}
});
group('All radicals should return results', () {
for (final radical in radicals.values.flattened) {
test(' - $radical', () async {
final connection = await setupDatabaseConnection();
final result = await connection.jadbSearchKanjiByRadicals([radical]);
expect(result, isNotEmpty);
});
}
});
}
+4 -31
View File
@@ -8,7 +8,7 @@ void main() {
expect(result, 'かたまり'); expect(result, 'かたまり');
}); });
test('Basic test with dakuten', () { test('Basic test with diacritics', () {
final result = transliterateLatinToHiragana('gadamari'); final result = transliterateLatinToHiragana('gadamari');
expect(result, 'がだまり'); expect(result, 'がだまり');
}); });
@@ -54,7 +54,7 @@ void main() {
test('Basic test', expectSpans('katamari', ['', '', '', ''])); test('Basic test', expectSpans('katamari', ['', '', '', '']));
test( test(
'Basic test with dakuten', 'Basic test with diacritics',
expectSpans('gadamari', ['', '', '', '']), expectSpans('gadamari', ['', '', '', '']),
); );
test('wi and we', expectSpans('wiwe', ['うぃ', 'うぇ'])); test('wi and we', expectSpans('wiwe', ['うぃ', 'うぇ']));
@@ -72,7 +72,7 @@ void main() {
expect(result, 'katamari'); expect(result, 'katamari');
}); });
test('Basic test with dakuten', () { test('Basic test with diacritics', () {
final result = transliterateHiraganaToLatin('がだまり'); final result = transliterateHiraganaToLatin('がだまり');
expect(result, 'gadamari'); expect(result, 'gadamari');
}); });
@@ -91,21 +91,6 @@ void main() {
final result = transliterateHiraganaToLatin('かっぱ'); final result = transliterateHiraganaToLatin('かっぱ');
expect(result, 'kappa'); expect(result, 'kappa');
}); });
test('Iteration mark', () {
final result = transliterateHiraganaToLatin('さゝき');
expect(result, 'sasaki');
}, skip: 'Not yet implemented');
test('Iteration mark with dakuten', () {
final result = transliterateHiraganaToLatin('あひゞき');
expect(result, 'ahibiki');
}, skip: 'Not yet implemented');
test('Yori', () {
final result = transliterateHiraganaToLatin('');
expect(result, 'yori');
}, skip: 'Not yet implemented');
}); });
group('Hiragana -> Romaji Spans', () { group('Hiragana -> Romaji Spans', () {
@@ -125,7 +110,7 @@ void main() {
test('Basic test', expectSpans('かたまり', ['ka', 'ta', 'ma', 'ri'])); test('Basic test', expectSpans('かたまり', ['ka', 'ta', 'ma', 'ri']));
test( test(
'Basic test with dakuten', 'Basic test with diacritics',
expectSpans('がだまり', ['ga', 'da', 'ma', 'ri']), expectSpans('がだまり', ['ga', 'da', 'ma', 'ri']),
); );
test('wi and we', expectSpans('うぃうぇ', ['whi', 'whe'])); test('wi and we', expectSpans('うぃうぇ', ['whi', 'whe']));
@@ -133,17 +118,5 @@ void main() {
// TODO: fix the implementation // TODO: fix the implementation
// test('Double consonant', expectSpans('かっぱ', ['ka', 'ppa'])); // test('Double consonant', expectSpans('かっぱ', ['ka', 'ppa']));
test(
'Iteration mark',
expectSpans('さゝき', ['sa', 'sa', 'ki']),
skip: 'Not yet implemented',
);
test(
'Iteration mark with dakuten',
expectSpans('あひゞき', ['a', 'hi', 'bi', 'ki']),
skip: 'Not yet implemented',
);
test('Yori', expectSpans('', ['yori']), skip: 'Not yet implemented');
}); });
} }