Push before cleanup

This commit is contained in:
Al Lansley 2024-04-04 10:15:15 +11:00
parent 73f11c5a4f
commit d65705c845
3 changed files with 43 additions and 18 deletions

View File

@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.SettableFuture import org.session.libsignal.utilities.SettableFuture
import org.thoughtcrime.securesms.search.SearchRepository import org.thoughtcrime.securesms.search.SearchRepository
import org.thoughtcrime.securesms.search.model.SearchResult import org.thoughtcrime.securesms.search.model.SearchResult
@ -31,6 +32,9 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
private val _queryText: MutableStateFlow<CharSequence> = MutableStateFlow("") private val _queryText: MutableStateFlow<CharSequence> = MutableStateFlow("")
private var settableFuture = SettableFuture<SearchResult>()
fun postQuery(charSequence: CharSequence?) { fun postQuery(charSequence: CharSequence?) {
charSequence ?: return charSequence ?: return
_queryText.value = charSequence _queryText.value = charSequence
@ -41,6 +45,7 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
_queryText _queryText
.buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST) .buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST)
.mapLatest { query -> .mapLatest { query ->
// Minimum search term is 2 characters - for a single char we do nothing
if (query.trim().length < 2) { if (query.trim().length < 2) {
SearchResult.EMPTY SearchResult.EMPTY
} else { } else {
@ -48,7 +53,13 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
// this coroutine will be cancelled and expensive query will not be run if typing quickly // this coroutine will be cancelled and expensive query will not be run if typing quickly
// first query of 2 characters will be instant however // first query of 2 characters will be instant however
delay(300) delay(300)
val settableFuture = SettableFuture<SearchResult>()
// If we already have a search running then stop it
if (!settableFuture.isDone) {
Log.w("[ACL]", "Cancelling settable future..")
settableFuture.cancel(true); }
settableFuture = SettableFuture<SearchResult>()
searchRepository.query(query.toString(), settableFuture::set) searchRepository.query(query.toString(), settableFuture::set)
try { try {
// search repository doesn't play nicely with suspend functions (yet) // search repository doesn't play nicely with suspend functions (yet)
@ -64,6 +75,4 @@ class GlobalSearchViewModel @Inject constructor(private val searchRepository: Se
} }
.launchIn(executor) .launchIn(executor)
} }
} }

View File

@ -86,7 +86,15 @@ public class SearchRepository {
} }
public void query(@NonNull String query, @NonNull Callback<SearchResult> callback) { public void query(@NonNull String query, @NonNull Callback<SearchResult> callback) {
if (TextUtils.isEmpty(query)) {
Log.w("[ACL]", "Hit SearchRepository.query - query string is: \"" + query + "\"");
String cleanQuery = sanitizeQuery(query).trim();
Log.w("[ACL]", "When sanitized and trimmed this is: \"" + cleanQuery + "\"");
// If the sanitized search is empty or is less than 2 chars then abort
if (cleanQuery.isEmpty() || cleanQuery.length() < 2) {
Log.w("[ACL]", "Trimmed query is empty or less than 2 chars so returning empty SearchResult");
callback.onResult(SearchResult.EMPTY); callback.onResult(SearchResult.EMPTY);
return; return;
} }
@ -94,25 +102,36 @@ public class SearchRepository {
executor.execute(() -> { executor.execute(() -> {
Stopwatch timer = new Stopwatch("FtsQuery"); Stopwatch timer = new Stopwatch("FtsQuery");
String cleanQuery = sanitizeQuery(query); // ACL
//String cleanQuery = sanitizeQuery(query).trim();
// If the search is for a single character and it was stripped by `sanitizeQuery` then abort
// the search for an empty string to avoid SQLite error. /*
if (cleanQuery.length() == 0) if (cleanQuery.isEmpty())
{ {
Log.w("[ACL]", "Aborting empty search query.");
Log.d(TAG, "Aborting empty search query."); Log.d(TAG, "Aborting empty search query.");
timer.stop(TAG); timer.stop(TAG);
return; return;
} }
else {
Log.w("[ACL]", "Clean query is non-empty and is: \"" + cleanQuery + "\"");
}
*/
timer.split("clean"); timer.split("clean");
Log.w("[ACL]", "About to query contacts.");
Pair<CursorList<Contact>, List<String>> contacts = queryContacts(cleanQuery); Pair<CursorList<Contact>, List<String>> contacts = queryContacts(cleanQuery);
timer.split("contacts"); timer.split("contacts");
Log.w("[ACL]", "About to query conversations.");
CursorList<GroupRecord> conversations = queryConversations(cleanQuery, contacts.getSecond()); CursorList<GroupRecord> conversations = queryConversations(cleanQuery, contacts.getSecond());
timer.split("conversations"); timer.split("conversations");
Log.w("[ACL]", "About to query messages.");
CursorList<MessageResult> messages = queryMessages(cleanQuery); CursorList<MessageResult> messages = queryMessages(cleanQuery);
timer.split("messages"); timer.split("messages");
@ -123,23 +142,20 @@ public class SearchRepository {
} }
public void query(@NonNull String query, long threadId, @NonNull Callback<CursorList<MessageResult>> callback) { public void query(@NonNull String query, long threadId, @NonNull Callback<CursorList<MessageResult>> callback) {
if (TextUtils.isEmpty(query)) { // If the sanitized search query is empty or less than 2 chars then abort the search
String cleanQuery = sanitizeQuery(query).trim();
if (cleanQuery.isEmpty() || cleanQuery.length() < 2) {
callback.onResult(CursorList.emptyList()); callback.onResult(CursorList.emptyList());
return; return;
} }
executor.execute(() -> { executor.execute(() -> {
// If the sanitized search query is empty then abort the search to prevent SQLite errors.
String cleanQuery = sanitizeQuery(query).trim();
if (cleanQuery.isEmpty()) { return; }
CursorList<MessageResult> messages = queryMessages(cleanQuery, threadId); CursorList<MessageResult> messages = queryMessages(cleanQuery, threadId);
callback.onResult(messages); callback.onResult(messages);
}); });
} }
private Pair<CursorList<Contact>, List<String>> queryContacts(String query) { private Pair<CursorList<Contact>, List<String>> queryContacts(String query) {
Cursor contacts = contactDatabase.queryContactsByName(query); Cursor contacts = contactDatabase.queryContactsByName(query);
List<Address> contactList = new ArrayList<>(); List<Address> contactList = new ArrayList<>();
List<String> contactStrings = new ArrayList<>(); List<String> contactStrings = new ArrayList<>();
@ -189,9 +205,7 @@ public class SearchRepository {
membersGroupList.close(); membersGroupList.close();
} }
Cursor conversations = threadDatabase.getFilteredConversationList(new ArrayList<>(addresses)); Cursor conversations = threadDatabase.getFilteredConversationList(new ArrayList<>(addresses));
return conversations != null ? new CursorList<>(conversations, new GroupModelBuilder(threadDatabase, groupDatabase)) return conversations != null ? new CursorList<>(conversations, new GroupModelBuilder(threadDatabase, groupDatabase))
: CursorList.emptyList(); : CursorList.emptyList();
} }

View File

@ -37,13 +37,15 @@ public class Stopwatch {
for (int i = 1; i < splits.size(); i++) { for (int i = 1; i < splits.size(); i++) {
out.append(splits.get(i).label).append(": "); out.append(splits.get(i).label).append(": ");
out.append(splits.get(i).time - splits.get(i - 1).time); out.append(splits.get(i).time - splits.get(i - 1).time);
out.append(" "); out.append("ms ");
} }
out.append("total: ").append(splits.get(splits.size() - 1).time - startTime); out.append("total: ").append(splits.get(splits.size() - 1).time - startTime).append("ms.");
} }
Log.d(tag, out.toString()); Log.d(tag, out.toString());
Log.w("[ACL]", out.toString());
} }
private static class Split { private static class Split {