diff --git a/app/src/main/java/com/orgzly/android/data/DataRepository.kt b/app/src/main/java/com/orgzly/android/data/DataRepository.kt index d27570804..1faba05ec 100644 --- a/app/src/main/java/com/orgzly/android/data/DataRepository.kt +++ b/app/src/main/java/com/orgzly/android/data/DataRepository.kt @@ -9,6 +9,7 @@ import android.net.Uri import android.os.Handler import android.text.TextUtils import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.localbroadcastmanager.content.LocalBroadcastManager @@ -1159,6 +1160,32 @@ class DataRepository @Inject constructor( val sqlQuery = buildSqlQuery(query) + if (query.options.searchLeafNodes) { // If search contained "h.leaf" in it, + // display only leaf nodes of the live data query + val result = MediatorLiveData>() + val parentNotesLiveData = db.noteView().runQueryLiveData(sqlQuery) + + result.addSource(parentNotesLiveData) { parentNotes -> + if (parentNotes.isNotEmpty()) { + var parentIds = parentNotes.map { it.note.id } + + val leafNotesLiveData = db.noteView().runQueryLeafNotesData(parentIds) // This is a recursive + // CTE (Common Table Expression) that searches all the child nodes of the parent ids, + // the returning result filters out the notes with ids that have children in the same selection + + result.removeSource(parentNotesLiveData) + + result.addSource(leafNotesLiveData) { leafNotes -> + result.value = leafNotes + } + } else { + result.value = emptyList() + } + } + + return result + } + return db.noteView().runQueryLiveData(sqlQuery) } diff --git a/app/src/main/java/com/orgzly/android/db/dao/NoteViewDao.kt b/app/src/main/java/com/orgzly/android/db/dao/NoteViewDao.kt index 19858d945..b974c68c8 100644 --- a/app/src/main/java/com/orgzly/android/db/dao/NoteViewDao.kt +++ b/app/src/main/java/com/orgzly/android/db/dao/NoteViewDao.kt @@ -26,6 +26,28 @@ abstract class NoteViewDao { """) abstract fun getVisibleLiveData(bookId: Long): LiveData> + @Query(""" + WITH RECURSIVE temp_notes AS ( + $QUERY + GROUP BY notes.id, event_timestamp + ), + rec_search_leaf_notes AS ( + select * from temp_notes + WHERE temp_notes.id IN (:noteIdList) + UNION ALL + SELECT + a.* + FROM temp_notes a + JOIN rec_search_leaf_notes b ON b.id = a.parent_id + ) + SELECT DISTINCT b.* + FROM rec_search_leaf_notes b + WHERE b.id NOT IN ( + SELECT parent_id FROM rec_search_leaf_notes WHERE parent_id IS NOT NULL + ) + """) + abstract fun runQueryLeafNotesData(noteIdList: List): LiveData> + @Query(""" $QUERY WHERE notes.book_id = :bookId diff --git a/app/src/main/java/com/orgzly/android/query/HierarchyType.kt b/app/src/main/java/com/orgzly/android/query/HierarchyType.kt new file mode 100644 index 000000000..00b134147 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/query/HierarchyType.kt @@ -0,0 +1,3 @@ +package com.orgzly.android.query + +enum class HierarchyType { LEAF } \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/query/Options.kt b/app/src/main/java/com/orgzly/android/query/Options.kt index e2fc76f07..4728f4a8e 100644 --- a/app/src/main/java/com/orgzly/android/query/Options.kt +++ b/app/src/main/java/com/orgzly/android/query/Options.kt @@ -1,3 +1,6 @@ package com.orgzly.android.query -data class Options(val agendaDays: Int = 0) \ No newline at end of file +data class Options( + val agendaDays: Int = 0, + val searchLeafNodes: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/query/user/DottedQueryParser.kt b/app/src/main/java/com/orgzly/android/query/user/DottedQueryParser.kt index 874ee04ae..a0dab9aae 100644 --- a/app/src/main/java/com/orgzly/android/query/user/DottedQueryParser.kt +++ b/app/src/main/java/com/orgzly/android/query/user/DottedQueryParser.kt @@ -113,6 +113,10 @@ open class DottedQueryParser : QueryParser() { OptionMatch("""^ad\.(\d+)$""") { match, options -> val days = match.groupValues[1].toInt() if (days > 0) options.copy(agendaDays = days) else null + }, + OptionMatch("""^(\.)?h\.(.+)""") { match, options -> + if (match.groupValues[2].lowercase() == HierarchyType.LEAF.toString().lowercase()) + options.copy(searchLeafNodes = true) else null } ) }