lib/search/word_search: add word count search
This commit is contained in:
@@ -27,10 +27,16 @@ class QueryWord extends Command {
|
||||
libsqlitePath: argResults!.option('libsqlite')!,
|
||||
);
|
||||
|
||||
final String searchWord = 'かな';
|
||||
|
||||
final time = Stopwatch()..start();
|
||||
final result = await JaDBConnection(db).searchWord('かな');
|
||||
final count = await JaDBConnection(db).searchWordCount(searchWord);
|
||||
time.stop();
|
||||
|
||||
final time2 = Stopwatch()..start();
|
||||
final result = await JaDBConnection(db).searchWord(searchWord);
|
||||
time2.stop();
|
||||
|
||||
if (result == null) {
|
||||
print("Invalid search");
|
||||
} else if (result.isEmpty) {
|
||||
@@ -42,6 +48,8 @@ class QueryWord extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
print("Query took ${time.elapsedMilliseconds}ms");
|
||||
print("Total count: ${count}");
|
||||
print("Count query took ${time.elapsedMilliseconds}ms");
|
||||
print("Query took ${time2.elapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,18 @@ class JaDBConnection {
|
||||
|
||||
const JaDBConnection(this._connection);
|
||||
|
||||
Future<KanjiSearchResult?> searchKanji(String kanji) async =>
|
||||
Future<KanjiSearchResult?> searchKanji(String kanji) =>
|
||||
searchKanjiWithDbConnection(this._connection, kanji);
|
||||
|
||||
Future<RadicalsSearchResult> searchKanjiByRadicals(
|
||||
List<String> radicals) async {
|
||||
List<String> radicals,
|
||||
) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<List<WordSearchResult>?> searchWord(String word) async =>
|
||||
Future<List<WordSearchResult>?> searchWord(String word) =>
|
||||
searchWordWithDbConnection(this._connection, word);
|
||||
|
||||
Future<int?> searchWordCount(String word) =>
|
||||
searchWordCountWithDbConnection(this._connection, word);
|
||||
}
|
||||
|
||||
@@ -30,14 +30,15 @@ SearchMode _determineSearchMode(String word) {
|
||||
|
||||
(String, List<Object?>) _kanjiReadingTemplate(
|
||||
String tableName,
|
||||
String word,
|
||||
int pageSize,
|
||||
) =>
|
||||
String word, {
|
||||
int pageSize = 10,
|
||||
bool countOnly = false,
|
||||
}) =>
|
||||
(
|
||||
'''
|
||||
WITH
|
||||
fts_results AS (
|
||||
SELECT
|
||||
SELECT DISTINCT
|
||||
"${tableName}FTS"."entryId",
|
||||
100
|
||||
+ "${tableName}"."baseScore"
|
||||
@@ -49,34 +50,39 @@ SearchMode _determineSearchMode(String word) {
|
||||
JOIN "${tableName}" USING ("entryId", "reading")
|
||||
WHERE "${tableName}FTS"."reading" MATCH ? || '*'
|
||||
ORDER BY "score" DESC
|
||||
LIMIT ?
|
||||
${!countOnly ? 'LIMIT ?' : ''}
|
||||
),
|
||||
non_fts_results AS (
|
||||
SELECT DISTINCT
|
||||
"entryId",
|
||||
50
|
||||
+ "${tableName}"."baseScore"
|
||||
- (substr(COALESCE("${TanosJLPTTableNames.jlptTag}"."jlptLevel", 'N0'), 2) * -5)
|
||||
AS "score"
|
||||
FROM "${tableName}"
|
||||
LEFT JOIN "${TanosJLPTTableNames.jlptTag}" USING ("entryId")
|
||||
WHERE "reading" LIKE '%' || ? || '%'
|
||||
AND "entryId" NOT IN (SELECT "entryId" FROM "fts_results")
|
||||
ORDER BY
|
||||
"score" DESC,
|
||||
"entryId" ASC
|
||||
${!countOnly ? 'LIMIT ?' : ''}
|
||||
)
|
||||
|
||||
SELECT *
|
||||
FROM "fts_results"
|
||||
UNION ALL
|
||||
SELECT
|
||||
"entryId",
|
||||
50
|
||||
+ "${tableName}"."baseScore"
|
||||
- (substr(COALESCE("${TanosJLPTTableNames.jlptTag}"."jlptLevel", 'N0'), 2) * -5)
|
||||
AS "score"
|
||||
FROM "${tableName}"
|
||||
LEFT JOIN "${TanosJLPTTableNames.jlptTag}" USING ("entryId")
|
||||
WHERE "reading" LIKE '%' || ? || '%'
|
||||
AND "entryId" NOT IN (SELECT "entryId" FROM "fts_results")
|
||||
ORDER BY
|
||||
"score" DESC,
|
||||
"entryId" ASC
|
||||
LIMIT ?
|
||||
${countOnly ? 'SELECT COUNT("entryId") AS count' : 'SELECT "entryId", "score"'}
|
||||
FROM (
|
||||
SELECT * FROM fts_results
|
||||
UNION ALL
|
||||
SELECT * FROM non_fts_results
|
||||
)
|
||||
'''
|
||||
.trim(),
|
||||
[
|
||||
word,
|
||||
word,
|
||||
pageSize,
|
||||
if (!countOnly) pageSize,
|
||||
word,
|
||||
pageSize,
|
||||
if (!countOnly) pageSize,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -102,7 +108,7 @@ Future<List<ScoredEntryId>> fetchEntryIds(
|
||||
final (query, args) = _kanjiReadingTemplate(
|
||||
JMdictTableNames.kanjiElement,
|
||||
word,
|
||||
pageSize,
|
||||
pageSize: pageSize,
|
||||
);
|
||||
entryIds = (await connection.rawQuery(query, args))
|
||||
.map((row) => ScoredEntryId(
|
||||
@@ -116,7 +122,7 @@ Future<List<ScoredEntryId>> fetchEntryIds(
|
||||
final (query, args) = _kanjiReadingTemplate(
|
||||
JMdictTableNames.readingElement,
|
||||
word,
|
||||
pageSize,
|
||||
pageSize: pageSize,
|
||||
);
|
||||
entryIds = (await connection.rawQuery(query, args))
|
||||
.map((row) => ScoredEntryId(
|
||||
@@ -153,3 +159,63 @@ Future<List<ScoredEntryId>> fetchEntryIds(
|
||||
|
||||
return entryIds;
|
||||
}
|
||||
|
||||
Future<int?> fetchEntryIdCount(
|
||||
DatabaseExecutor connection,
|
||||
String word,
|
||||
SearchMode searchMode,
|
||||
) async {
|
||||
if (searchMode == SearchMode.Auto) {
|
||||
searchMode = _determineSearchMode(word);
|
||||
}
|
||||
|
||||
assert(
|
||||
word.isNotEmpty,
|
||||
'Word should not be empty when fetching entry IDs',
|
||||
);
|
||||
|
||||
late final int? entryIdCount;
|
||||
|
||||
switch (searchMode) {
|
||||
case SearchMode.Kanji:
|
||||
final (query, args) = _kanjiReadingTemplate(
|
||||
JMdictTableNames.kanjiElement,
|
||||
word,
|
||||
pageSize: 1,
|
||||
countOnly: true,
|
||||
);
|
||||
entryIdCount = (await connection.rawQuery(query, args))
|
||||
.firstOrNull?['count'] as int?;
|
||||
break;
|
||||
|
||||
case SearchMode.Kana:
|
||||
final (query, args) = _kanjiReadingTemplate(
|
||||
JMdictTableNames.readingElement,
|
||||
word,
|
||||
pageSize: 1,
|
||||
countOnly: true,
|
||||
);
|
||||
entryIdCount = (await connection.rawQuery(query, args))
|
||||
.firstOrNull?['count'] as int?;
|
||||
break;
|
||||
|
||||
case SearchMode.English:
|
||||
entryIdCount = (await connection.query(
|
||||
JMdictTableNames.senseGlossary,
|
||||
columns: ['COUNT(DISTINCT entryId)'],
|
||||
where: 'english LIKE ?',
|
||||
whereArgs: ['%$word%'],
|
||||
))
|
||||
.firstOrNull?['COUNT(DISTINCT entryId)'] as int?;
|
||||
break;
|
||||
|
||||
case SearchMode.MixedKana:
|
||||
case SearchMode.MixedKanji:
|
||||
default:
|
||||
throw UnimplementedError(
|
||||
'Search mode $searchMode is not implemented',
|
||||
);
|
||||
}
|
||||
|
||||
return entryIdCount;
|
||||
}
|
||||
|
||||
@@ -77,3 +77,21 @@ Future<List<WordSearchResult>?> searchWordWithDbConnection(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<int?> searchWordCountWithDbConnection(
|
||||
DatabaseExecutor connection,
|
||||
String word, {
|
||||
SearchMode searchMode = SearchMode.Auto,
|
||||
}) async {
|
||||
if (word.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int? entryIdCount = await fetchEntryIdCount(
|
||||
connection,
|
||||
word,
|
||||
searchMode,
|
||||
);
|
||||
|
||||
return entryIdCount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user