1
0
Fork 0

added dynamic word positions limit when searching to prevent some words being inaccessible in languages with many letters per key

This commit is contained in:
sspanak 2024-12-16 12:41:06 +02:00 committed by Dimo Karaivanov
parent f8e6668281
commit ac4e5c597c
4 changed files with 83 additions and 3 deletions

View file

@ -0,0 +1,44 @@
package io.github.sspanak.tt9.db.entities;
import androidx.annotation.NonNull;
import java.util.HashMap;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
public class LongPositionsCache {
private final HashMap<Integer, HashMap<String, Integer>> positions = new HashMap<>();
public boolean contains(@NonNull Language language) {
return positions.containsKey(language.getId());
}
public void put(@NonNull Language language, @NonNull String sequence, int wordCount) {
if (wordCount < SettingsStore.SUGGESTIONS_POSITIONS_LIMIT && !contains(language)) {
positions.put(language.getId(), null);
return;
}
HashMap<String, Integer> words = positions.get(language.getId());
if (words == null) {
words = new HashMap<>();
positions.put(language.getId(), words);
}
words.put(sequence, wordCount);
}
public int get(@NonNull Language language, @NonNull String sequence) {
if (!contains(language)) {
return SettingsStore.SUGGESTIONS_POSITIONS_LIMIT;
}
HashMap<String, Integer> words = positions.get(language.getId());
if (words == null) {
return SettingsStore.SUGGESTIONS_POSITIONS_LIMIT;
}
Integer wordCount = words.get(sequence);
return wordCount == null ? SettingsStore.SUGGESTIONS_POSITIONS_LIMIT : wordCount;
}
}

View file

@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import java.util.ArrayList;
import io.github.sspanak.tt9.db.entities.LongPositionsCache;
import io.github.sspanak.tt9.db.entities.NormalizationList;
import io.github.sspanak.tt9.db.entities.WordList;
import io.github.sspanak.tt9.db.entities.WordPositionsStringBuilder;
@ -20,10 +21,12 @@ import io.github.sspanak.tt9.db.words.SlowQueryStats;
import io.github.sspanak.tt9.db.words.WordStore;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.Logger;
public class ReadOps {
private final String LOG_TAG = "ReadOperations";
private final LongPositionsCache longPositionsCache = new LongPositionsCache();
/**
@ -253,7 +256,7 @@ public class ReadOps {
.append(sequence)
.append("' OR sequence BETWEEN '").append(sequence).append("1' AND '").append(sequence).append(rangeEnd).append("'");
sql.append(" ORDER BY `start` ");
sql.append(" LIMIT 100");
sql.append(" LIMIT ").append(longPositionsCache.get(language, sequence));
}
String positionsSql = sql.toString();
@ -336,4 +339,31 @@ public class ReadOps {
return pairs;
}
/**
* Returns the sequences that result in more words than the standard performance-balanced limit of 100.
*/
public void cacheLongPositionsIfMissing(@NonNull SQLiteDatabase db, @NonNull Language language) {
if (longPositionsCache.contains(language)) {
return;
}
String[] select = new String[]{"sequence", "`end` - `start`"};
String table = Tables.getWordPositions(language.getId());
String where = "LENGTH(sequence) > " + SettingsStore.SUGGESTIONS_POSITIONS_LIMIT;
boolean hasResults = false;
try (Cursor cursor = db.query(table, select, where, null, null, null, null)) {
while (cursor.moveToNext()) {
hasResults = true;
longPositionsCache.put(language, cursor.getString(0), cursor.getInt(1));
}
}
if (!hasResults) {
longPositionsCache.put(language, "", 0);
}
}
}

View file

@ -78,6 +78,10 @@ public class WordStore extends BaseSyncStore {
return new ArrayList<>();
}
Timer.start("cache_long_positions");
readOps.cacheLongPositionsIfMissing(sqlite.getDb(), language);
long longPositionsTime = Timer.stop("cache_long_positions");
final int minWords = Math.max(minimumWords, 0);
final int maxWords = Math.max(maximumWords, minWords);
final String filter = wordFilter == null ? "" : wordFilter;
@ -90,7 +94,7 @@ public class WordStore extends BaseSyncStore {
ArrayList<String> words = readOps.getWords(sqlite.getDb(), cancel, language, positions, filter, maxWords, false).toStringList();
long wordsTime = Timer.stop("get_words");
printLoadingSummary(sequence, words, positionsTime, wordsTime);
printLoadingSummary(sequence, words, longPositionsTime, positionsTime, wordsTime);
if (!cancel.isCanceled()) { // do not cache empty results from aborted queries
SlowQueryStats.add(SlowQueryStats.generateKey(language, sequence, wordFilter, minWords), (int) (positionsTime + wordsTime), positions);
}
@ -259,7 +263,7 @@ public class WordStore extends BaseSyncStore {
}
private void printLoadingSummary(String sequence, ArrayList<String> words, long positionIndexTime, long wordsTime) {
private void printLoadingSummary(String sequence, ArrayList<String> words, long longPositionsTime, long positionIndexTime, long wordsTime) {
if (!Logger.isDebugLevel()) {
return;
}
@ -269,6 +273,7 @@ public class WordStore extends BaseSyncStore {
.append("\nWord Count: ").append(words.size())
.append(".\nTime: ").append(positionIndexTime + wordsTime)
.append(" ms (positions: ").append(positionIndexTime)
.append(" ms, long positions: ").append(longPositionsTime)
.append(" ms, words: ").append(wordsTime).append(" ms).");
if (words.isEmpty()) {

View file

@ -32,6 +32,7 @@ public class SettingsStore extends SettingsUI {
public final static float SOFT_KEY_COMPLEX_LABEL_SUB_TITLE_RELATIVE_SIZE = 0.8f;
public final static int SUGGESTIONS_MAX = 20;
public final static int SUGGESTIONS_MIN = 8;
public final static int SUGGESTIONS_POSITIONS_LIMIT = 100;
public final static int SUGGESTIONS_SELECT_ANIMATION_DURATION = 66;
public final static int SUGGESTIONS_TRANSLATE_ANIMATION_DURATION = 0;
public final static int TEXT_INPUT_DEBOUNCE_TIME = 500; // ms