From 6c580e95e28aa7a652152c1fd6e586f285d51e46 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 16 May 2025 17:06:00 +0200 Subject: [PATCH] lib/search/word_search: pagination --- lib/search/word_search.dart | 95 ++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/lib/search/word_search.dart b/lib/search/word_search.dart index 3cb5f31..e415700 100644 --- a/lib/search/word_search.dart +++ b/lib/search/word_search.dart @@ -13,6 +13,7 @@ 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:jadb/util/sqlite_utils.dart'; +import 'package:jadb/util/text_filtering.dart'; import 'package:sqflite_common/sqlite_api.dart'; // TODO: Support globs @@ -23,21 +24,61 @@ import 'package:sqflite_common/sqlite_api.dart'; // TODO: Support mixing kana and romaji -Future?> searchWordWithDbConnection( - DatabaseExecutor connection, - String word, { - bool isKana = true, -}) async { - if (word.isEmpty) { - return null; +enum SearchMode { + Auto, + English, + Kanji, + MixedKanji, + Kana, + MixedKana, +} + +SearchMode _determineSearchMode(String word) { + final bool containsKanji = kanjiRegex.hasMatch(word); + final bool containsAscii = RegExp(r'[A-Za-z]').hasMatch(word); + + if (containsKanji && containsAscii) { + return SearchMode.MixedKanji; + } else if (containsKanji) { + return SearchMode.Kanji; + } else if (containsAscii) { + return SearchMode.English; + } else if (word.contains(hiraganaRegex) || word.contains(katakanaRegex)) { + return SearchMode.Kana; + } else { + return SearchMode.MixedKana; } +} + +Future> _fetchEntryIds(DatabaseExecutor connection, String word, + SearchMode searchMode, int pageSize, int? offset) async { + assert( + searchMode != SearchMode.Auto, + 'Search mode should not be auto when fetching entry IDs', + ); + assert( + word.isNotEmpty, + 'Word should not be empty when fetching entry IDs', + ); late final List entryIds; - if (isKana) { + if (searchMode == SearchMode.Kanji) { + entryIds = (await connection.query( + 'JMdict_EntryByKanji', + where: 'kanji LIKE ?', + whereArgs: ['%$word%'], + limit: pageSize, + offset: offset, + )) + .map((row) => row['entryId'] as int) + .toList(); + } else if (searchMode == SearchMode.Kana) { entryIds = (await connection.query( 'JMdict_EntryByKana', where: 'kana LIKE ?', - whereArgs: ['$word%'], + whereArgs: ['%$word%'], + limit: pageSize, + offset: offset, )) .map((row) => row['entryId'] as int) .toList(); @@ -45,12 +86,42 @@ Future?> searchWordWithDbConnection( entryIds = (await connection.query( 'JMdict_EntryByEnglish', where: 'english LIKE ?', - whereArgs: ['$word%'], + whereArgs: ['%$word%'], + limit: pageSize, + offset: offset, )) .map((row) => row['entryId'] as int) .toList(); } + return entryIds; +} + +Future?> searchWordWithDbConnection( + DatabaseExecutor connection, + String word, { + SearchMode searchMode = SearchMode.Auto, + int page = 0, + int pageSize = 10, +}) async { + if (word.isEmpty) { + return null; + } + + if (searchMode == SearchMode.Auto) { + searchMode = _determineSearchMode(word); + } + + final offset = page * pageSize; + + final List entryIds = await _fetchEntryIds( + connection, + word, + searchMode, + pageSize, + offset, + ); + if (entryIds.isEmpty) { return []; } @@ -270,7 +341,7 @@ Future?> searchWordWithDbConnection( kanjiElementInfos_query.then((value) => kanjiElementInfos = value), ]); - return _regroupWordSearchResults( + final result = _regroupWordSearchResults( entryIds: entryIds, readingElements: readingElements, kanjiElements: kanjiElements, @@ -293,6 +364,8 @@ Future?> searchWordWithDbConnection( readingElementRestrictions: readingElementRestrictions, kanjiElementInfos: kanjiElementInfos, ); + + return result; } List _regroupWordSearchResults({