Implement word search

This commit is contained in:
2025-04-25 22:47:06 +02:00
parent 1c2f90a617
commit b6410c717f
24 changed files with 1352 additions and 325 deletions
@@ -0,0 +1,64 @@
import 'package:jadb/models/jmdict/jmdict_kanji_info.dart';
import 'package:jadb/models/jmdict/jmdict_reading_info.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_sources.dart';
/// A class representing a single dictionary entry from a word search.
class WordSearchResult {
/// The ID of the entry in the database.
final int entryId;
/// The variants of the word in Japanese.
final List<WordSearchRuby> japanese;
/// Extra information about the kanji used in the word.
final Map<String, JMdictKanjiInfo> kanjiInfo;
/// Extra information about the kana used in the word.
final Map<String, JMdictReadingInfo> readingInfo;
/// The meanings of the word, including parts of speech and other information.
final List<WordSearchSense> senses;
/// A class listing the sources used to make up the data for this word search result.
final WordSearchSources sources;
const WordSearchResult({
required this.entryId,
required this.japanese,
required this.kanjiInfo,
required this.readingInfo,
required this.senses,
required this.sources,
});
Map<String, dynamic> toJson() => {
'entryId': entryId,
'japanese': japanese.map((e) => e.toJson()).toList(),
'kanjiInfo':
kanjiInfo.map((key, value) => MapEntry(key, value.toJson())),
'readingInfo':
readingInfo.map((key, value) => MapEntry(key, value.toJson())),
'senses': senses.map((e) => e.toJson()).toList(),
'sources': sources.toJson(),
};
factory WordSearchResult.fromJson(Map<String, dynamic> json) =>
WordSearchResult(
entryId: json['entryId'] as int,
japanese: (json['japanese'] as List<dynamic>)
.map((e) => WordSearchRuby.fromJson(e))
.toList(),
kanjiInfo: (json['kanjiInfo'] as Map<String, dynamic>).map(
(key, value) => MapEntry(key, JMdictKanjiInfo.fromJson(value)),
),
readingInfo: (json['readingInfo'] as Map<String, dynamic>).map(
(key, value) => MapEntry(key, JMdictReadingInfo.fromJson(value)),
),
senses: (json['senses'] as List<dynamic>)
.map((e) => WordSearchSense.fromJson(e))
.toList(),
sources: WordSearchSources.fromJson(json['sources']),
);
}
@@ -0,0 +1,23 @@
/// A pair of base and optional furigana.
class WordSearchRuby {
/// Base part. Could be a kanji or a reading.
String base;
/// Furigana, if applicable.
String? furigana;
WordSearchRuby({
required this.base,
this.furigana,
});
Map<String, dynamic> toJson() => {
'base': base,
'furigana': furigana,
};
factory WordSearchRuby.fromJson(Map<String, dynamic> json) => WordSearchRuby(
base: json['base'] as String,
furigana: json['furigana'] as String?,
);
}
@@ -0,0 +1,111 @@
import 'package:jadb/models/jmdict/jmdict_dialect.dart';
import 'package:jadb/models/jmdict/jmdict_field.dart';
import 'package:jadb/models/jmdict/jmdict_misc.dart';
import 'package:jadb/models/jmdict/jmdict_pos.dart';
import 'package:jadb/models/word_search/word_search_xref_entry.dart';
class WordSearchSense {
/// The meaning(s) of the word.
final List<String> englishDefinitions;
/// Type of word (Noun, Verb, etc.).
final List<JMdictPOS> partsOfSpeech;
/// Relevant words (might include synonyms).
final List<WordSearchXrefEntry> seeAlso;
/// Words with opposite meaning.
final List<WordSearchXrefEntry> antonyms;
/// Restrictions on which of the readings of the parent entry this sense applies to.
final List<String> restrictedToReading;
/// Restrictions on which of the kanji of the parent entry this sense applies to.
final List<String> restrictedToKanji;
/// Tags for which domains or fields of expertise that this sense is relevant to.
final List<JMdictField> fields;
/// Tags for which dialects this sense is used in.
final List<JMdictDialect> dialects;
/// Tags for miscellaneous information.
final List<JMdictMisc> misc;
/// Extra information about the sense.
final List<String> info;
// TODO: there is a lot more info to collect in the languageSource data
/// Information about the the origin of the word, if loaned from another language.
final List<String> languageSource;
// TODO: add example sentences
const WordSearchSense({
required this.englishDefinitions,
required this.partsOfSpeech,
required this.seeAlso,
required this.antonyms,
required this.restrictedToReading,
required this.restrictedToKanji,
required this.fields,
required this.dialects,
required this.misc,
required this.info,
required this.languageSource,
});
bool get isEmpty =>
englishDefinitions.isEmpty &&
partsOfSpeech.isEmpty &&
seeAlso.isEmpty &&
antonyms.isEmpty &&
restrictedToReading.isEmpty &&
restrictedToKanji.isEmpty &&
fields.isEmpty &&
dialects.isEmpty &&
misc.isEmpty &&
info.isEmpty &&
languageSource.isEmpty;
Map<String, dynamic> toJson() => {
'englishDefinitions': englishDefinitions,
'partsOfSpeech': partsOfSpeech.map((e) => e.toJson()).toList(),
'seeAlso': seeAlso.map((e) => e.toJson()).toList(),
'antonyms': antonyms.map((e) => e.toJson()).toList(),
'restrictedToReading': restrictedToReading,
'restrictedToKanji': restrictedToKanji,
'fields': fields.map((e) => e.toJson()).toList(),
'dialects': dialects.map((e) => e.toJson()).toList(),
'misc': misc.map((e) => e.toJson()).toList(),
'info': info,
'languageSource': languageSource,
};
factory WordSearchSense.fromJson(Map<String, dynamic> json) =>
WordSearchSense(
englishDefinitions: List<String>.from(json['englishDefinitions']),
partsOfSpeech: (json['partsOfSpeech'] as List)
.map((e) => JMdictPOS.fromJson(e))
.toList(),
seeAlso: (json['seeAlso'] as List)
.map((e) => WordSearchXrefEntry.fromJson(e))
.toList(),
antonyms: (json['antonyms'] as List)
.map((e) => WordSearchXrefEntry.fromJson(e))
.toList(),
restrictedToReading: List<String>.from(json['restrictedToReading']),
restrictedToKanji: List<String>.from(json['restrictedToKanji']),
fields: (json['fields'] as List)
.map((e) => JMdictField.fromJson(e))
.toList(),
dialects: (json['dialects'] as List)
.map((e) => JMdictDialect.fromJson(e))
.toList(),
misc:
(json['misc'] as List).map((e) => JMdictMisc.fromJson(e)).toList(),
info: List<String>.from(json['info']),
languageSource: List<String>.from(json['languageSource']),
);
}
@@ -0,0 +1,30 @@
/// A class representing which sources were used to make up the data for
/// a word search result.
class WordSearchSources {
/// Whether JMdict was used.
final bool jmdict;
/// Whether JMnedict was used.
final bool jmnedict;
const WordSearchSources({
this.jmdict = true,
this.jmnedict = false,
});
Map<String, Object?> get sqlValue => {
'jmdict': jmdict,
'jmnedict': jmnedict,
};
Map<String, dynamic> toJson() => {
'jmdict': jmdict,
'jmnedict': jmnedict,
};
factory WordSearchSources.fromJson(Map<String, dynamic> json) =>
WordSearchSources(
jmdict: json['jmdict'] as bool? ?? true,
jmnedict: json['jmnedict'] as bool? ?? false,
);
}
@@ -0,0 +1,25 @@
/// A cross-reference entry from one word-result to another entry.
class WordSearchXrefEntry {
/// The ID of the entry that this entry cross-references to.
final int entryId;
/// Whether the entryId was ambiguous during the creation of the
/// database (and hence might be incorrect).
final bool ambiguous;
const WordSearchXrefEntry({
required this.entryId,
required this.ambiguous,
});
Map<String, dynamic> toJson() => {
'entryId': entryId,
'ambiguous': ambiguous,
};
factory WordSearchXrefEntry.fromJson(Map<String, dynamic> json) =>
WordSearchXrefEntry(
entryId: json['entryId'] as int,
ambiguous: json['ambiguous'] as bool,
);
}