lib/search: make JaDBConnection into extension, add verifyTables

This commit is contained in:
2025-05-27 20:02:13 +02:00
parent ec14016ab5
commit b25cc85afe
8 changed files with 72 additions and 66 deletions

View File

@@ -1,10 +1,7 @@
import 'dart:ffi';
import 'dart:io';
import 'package:jadb/_data_ingestion/jmdict/table_names.dart';
import 'package:jadb/_data_ingestion/kanjidic/table_names.dart';
import 'package:jadb/_data_ingestion/radkfile/table_names.dart';
import 'package:jadb/_data_ingestion/tanos-jlpt/table_names.dart';
import 'package:jadb/search.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:sqlite3/open.dart';
@@ -12,7 +9,7 @@ Future<Database> openLocalDb({
String? libsqlitePath,
String? jadbPath,
bool readWrite = false,
bool assertTablesExist = true,
bool verifyTablesExist = true,
bool walMode = false,
}) async {
libsqlitePath ??= Platform.environment['LIBSQLITE_PATH'];
@@ -52,43 +49,9 @@ Future<Database> openLocalDb({
),
);
if (assertTablesExist) {
await _assertTablesExist(db);
if (verifyTablesExist) {
await db.jadbVerifyTables();
}
return db;
}
Future<void> _assertTablesExist(Database db) async {
final Set<String> tables = await db
.query(
'sqlite_master',
columns: ['name'],
where: 'type = ?',
whereArgs: ['table'],
)
.then((result) {
return result.map((row) => row['name'] as String).toSet();
});
final Set<String> expectedTables = {
...JMdictTableNames.allTables,
...KANJIDICTableNames.allTables,
...RADKFILETableNames.allTables,
...TanosJLPTTableNames.allTables,
};
final missingTables = expectedTables.difference(tables);
if (missingTables.isNotEmpty) {
throw Exception([
'Missing tables:',
missingTables.map((table) => ' - $table').join('\n'),
'',
'Found tables:\n',
tables.map((table) => ' - $table').join('\n'),
'',
'Please ensure the database is correctly set up.',
].join('\n'));
}
}

View File

@@ -35,7 +35,7 @@ class QueryKanji extends Command {
);
final time = Stopwatch()..start();
final result = await JaDBConnection(db).searchKanji(
final result = await JaDBConnection(db).jadbSearchKanji(
argResults!.option('kanji') ?? '',
);
time.stop();

View File

@@ -36,11 +36,11 @@ class QueryWord extends Command {
final String searchWord = argResults!.option('word') ?? 'かな';
final time = Stopwatch()..start();
final count = await JaDBConnection(db).searchWordCount(searchWord);
final count = await JaDBConnection(db).jadbSearchWordCount(searchWord);
time.stop();
final time2 = Stopwatch()..start();
final result = await JaDBConnection(db).searchWord(searchWord);
final result = await JaDBConnection(db).jadbSearchWord(searchWord);
time2.stop();
if (result == null) {

View File

@@ -0,0 +1,39 @@
import 'package:jadb/_data_ingestion/jmdict/table_names.dart';
import 'package:jadb/_data_ingestion/kanjidic/table_names.dart';
import 'package:jadb/_data_ingestion/radkfile/table_names.dart';
import 'package:jadb/_data_ingestion/tanos-jlpt/table_names.dart';
import 'package:sqflite_common/sqlite_api.dart';
Future<void> verifyTablesWithDbConnection(DatabaseExecutor db) async {
final Set<String> tables = await db
.query(
'sqlite_master',
columns: ['name'],
where: 'type = ?',
whereArgs: ['table'],
)
.then((result) {
return result.map((row) => row['name'] as String).toSet();
});
final Set<String> expectedTables = {
...JMdictTableNames.allTables,
...KANJIDICTableNames.allTables,
...RADKFILETableNames.allTables,
...TanosJLPTTableNames.allTables,
};
final missingTables = expectedTables.difference(tables);
if (missingTables.isNotEmpty) {
throw Exception([
'Missing tables:',
missingTables.map((table) => ' - $table').join('\n'),
'',
'Found tables:\n',
tables.map((table) => ' - $table').join('\n'),
'',
'Please ensure the database is correctly set up.',
].join('\n'));
}
}

View File

@@ -1,3 +1,4 @@
import 'package:jadb/models/verifyTables.dart';
import 'package:jadb/models/word_search/word_search_result.dart';
import 'package:jadb/models/kanji_search/kanji_search_result.dart';
import 'package:jadb/search/filter_kanji.dart';
@@ -8,31 +9,32 @@ import 'package:jadb/search/kanji_search.dart';
import 'package:sqflite_common/sqlite_api.dart';
class JaDBConnection {
final DatabaseExecutor _connection;
const JaDBConnection(this._connection);
extension JaDBConnection on DatabaseExecutor {
/// Ensure that the database contain all JaDB tables.
///
/// This will throw an exception if any of the tables are missing.
Future<void> jadbVerifyTables() => verifyTablesWithDbConnection(this);
/// Search for a kanji in the database.
Future<KanjiSearchResult?> searchKanji(String kanji) =>
searchKanjiWithDbConnection(this._connection, kanji);
Future<KanjiSearchResult?> jadbSearchKanji(String kanji) =>
searchKanjiWithDbConnection(this, kanji);
/// Filter a list of characters, and return the ones that are listed in the kanji dictionary.
Future<List<String>> filterKanji(
List<String> kanji, {
bool deduplicate = false,
}) =>
filterKanjiWithDbConnection(this._connection, kanji, deduplicate);
filterKanjiWithDbConnection(this, kanji, deduplicate);
/// Search for a word in the database.
Future<List<WordSearchResult>?> searchWord(
Future<List<WordSearchResult>?> jadbSearchWord(
String word, {
SearchMode searchMode = SearchMode.Auto,
int page = 0,
int pageSize = 10,
}) =>
searchWordWithDbConnection(
this._connection,
this,
word,
searchMode,
page,
@@ -40,21 +42,21 @@ class JaDBConnection {
);
/// Search for a word in the database, and return the count of results.
Future<int?> searchWordCount(
Future<int?> jadbSearchWordCount(
String word, {
SearchMode searchMode = SearchMode.Auto,
}) =>
searchWordCountWithDbConnection(this._connection, word, searchMode);
searchWordCountWithDbConnection(this, word, searchMode);
/// Given a list of radicals, search which kanji contains all
/// of the radicals, find their other radicals, and return those.
/// This is used to figure out which remaining combinations of radicals
/// the user can search for without getting zero results.
Future<List<String>> searchRemainingRadicals(List<String> radicals) =>
searchRemainingRadicalsWithDbConnection(this._connection, radicals);
Future<List<String>> jadbSearchRemainingRadicals(List<String> radicals) =>
searchRemainingRadicalsWithDbConnection(this, radicals);
/// Given a list of radicals, search which kanji contains all
/// of the radicals, and return those.
Future<List<String>> searchKanjiByRadicals(List<String> radicals) =>
searchKanjiByRadicalsWithDbConnection(this._connection, radicals);
Future<List<String>> jadbSearchKanjiByRadicals(List<String> radicals) =>
searchKanjiByRadicalsWithDbConnection(this, radicals);
}

View File

@@ -1,4 +1,5 @@
import 'package:jadb/const_data/kanji_grades.dart';
import 'package:jadb/search.dart';
import 'package:test/test.dart';
import 'setup_database_connection.dart';
@@ -7,7 +8,7 @@ void main() {
test("Search a kanji", () async {
final connection = await setup_database_connection();
final result = await connection.searchKanji('');
final result = await connection.jadbSearchKanji('');
expect(result, isNotNull);
});
@@ -17,7 +18,7 @@ void main() {
final connection = await setup_database_connection();
for (final character in characters) {
final result = await connection.searchKanji(character);
final result = await connection.jadbSearchKanji(character);
expect(result, isNotNull);
}
}, timeout: Timeout.factor(10));

View File

@@ -1,9 +1,9 @@
import 'dart:io';
import 'package:jadb/_data_ingestion/open_local_db.dart';
import 'package:jadb/search.dart';
import 'package:sqflite_common/sqlite_api.dart';
Future<JaDBConnection> setup_database_connection() async {
Future<Database> setup_database_connection() async {
final lib_sqlite_path = Platform.environment['LIBSQLITE_PATH'];
final jadb_path = Platform.environment['JADB_PATH'];
@@ -24,5 +24,5 @@ Future<JaDBConnection> setup_database_connection() async {
throw Exception("Failed to open database");
}
return JaDBConnection(db_connection);
return db_connection;
}

View File

@@ -1,3 +1,4 @@
import 'package:jadb/search.dart';
import 'package:test/test.dart';
import 'setup_database_connection.dart';
@@ -6,7 +7,7 @@ void main() {
test("Search a word", () async {
final connection = await setup_database_connection();
final result = await connection.searchWord("kana");
final result = await connection.jadbSearchWord("kana");
expect(result, isNotNull);
});
@@ -17,7 +18,7 @@ void main() {
// Test serializing all words
for (final letter in "aiueoksthnmyrw".split("")) {
await connection.searchWord(letter);
await connection.jadbSearchWord(letter);
}
},
timeout: Timeout.factor(100),