Skip to content

Commit 4cffa0c

Browse files
leandroBorgesFerreiraLeandro Ferreira
andauthored
In document full text search (#496)
* Showing search box * Adding FTS in SQLDlight * Injecting InDocumentSearchRepository * Adding insert for Fts * First sketch working * Moving focus to search * Method rename * search in document partially working * Moving focus correctly * ktlint * moving tests --------- Co-authored-by: Leandro Ferreira <[email protected]>
1 parent 8f00996 commit 4cffa0c

23 files changed

Lines changed: 453 additions & 15 deletions

File tree

application/composeApp/src/jvmMain/kotlin/io/writeopia/desktop/MainDesktop.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ private fun ApplicationScope.App(onCloseRequest: () -> Unit = ::exitApplication)
174174
false
175175
}
176176

177+
KeyboardCommands.isSearchEvent(keyEvent) -> {
178+
sendEvent(KeyboardEvent.SEARCH)
179+
false
180+
}
181+
177182
else -> false
178183
}
179184
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.writeopia.core.folders.di
2+
3+
import io.writeopia.core.folders.repository.InDocumentSearchRepository
4+
5+
actual class InDocumentSearchInjection {
6+
actual fun provideInDocumentSearchRepo(): InDocumentSearchRepository {
7+
TODO("Not yet implemented")
8+
}
9+
10+
actual companion object {
11+
actual fun singleton(): InDocumentSearchInjection {
12+
TODO("Not yet implemented")
13+
}
14+
}
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.writeopia.core.folders.di
2+
3+
import io.writeopia.core.folders.repository.InDocumentSearchRepository
4+
5+
expect class InDocumentSearchInjection {
6+
7+
fun provideInDocumentSearchRepo(): InDocumentSearchRepository
8+
9+
companion object {
10+
fun singleton(): InDocumentSearchInjection
11+
}
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.writeopia.core.folders.repository
2+
3+
import io.writeopia.sdk.models.story.StoryStep
4+
import io.writeopia.sqldelight.dao.StoryStepFtsSqlDelightDao
5+
6+
class InDocumentSearchRepository(private val storyStepFtsSqlDelightDao: StoryStepFtsSqlDelightDao) {
7+
8+
fun searchInDocument(query: String, documentId: String): Set<Int> {
9+
return storyStepFtsSqlDelightDao.searchInDocument(query, documentId)
10+
}
11+
12+
suspend fun insertForFts(storyStep: StoryStep, documentId: String, position: Int) {
13+
storyStepFtsSqlDelightDao.insertForFts(storyStep, documentId, position)
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.writeopia.core.folders.di
2+
3+
import io.writeopia.core.folders.repository.InDocumentSearchRepository
4+
5+
actual class InDocumentSearchInjection {
6+
actual fun provideInDocumentSearchRepo(): InDocumentSearchRepository {
7+
TODO("Not yet implemented")
8+
}
9+
10+
actual companion object {
11+
actual fun singleton(): InDocumentSearchInjection {
12+
TODO("Not yet implemented")
13+
}
14+
}
15+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.writeopia.core.folders.di
2+
3+
import io.writeopia.core.folders.repository.InDocumentSearchRepository
4+
import io.writeopia.sql.WriteopiaDb
5+
import io.writeopia.sqldelight.dao.StoryStepFtsSqlDelightDao
6+
import io.writeopia.sqldelight.di.WriteopiaDbInjector
7+
8+
actual class InDocumentSearchInjection private constructor(
9+
private val writeopiaDb: WriteopiaDb?
10+
) {
11+
private var inDocumentSearchRepository: InDocumentSearchRepository? = null
12+
13+
private fun provideFtsSearchDao(): StoryStepFtsSqlDelightDao =
14+
StoryStepFtsSqlDelightDao(writeopiaDb)
15+
16+
actual fun provideInDocumentSearchRepo(): InDocumentSearchRepository =
17+
inDocumentSearchRepository ?: InDocumentSearchRepository(
18+
provideFtsSearchDao()
19+
).also {
20+
inDocumentSearchRepository = it
21+
}
22+
23+
actual companion object {
24+
private var instance: InDocumentSearchInjection? = null
25+
26+
actual fun singleton(): InDocumentSearchInjection =
27+
instance ?: InDocumentSearchInjection(WriteopiaDbInjector.singleton()?.database)
28+
}
29+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.writeopia.core.folders.di
2+
3+
import io.writeopia.core.folders.repository.InDocumentSearchRepository
4+
5+
actual class InDocumentSearchInjection {
6+
actual fun provideInDocumentSearchRepo(): InDocumentSearchRepository {
7+
TODO("Not yet implemented")
8+
}
9+
10+
actual companion object {
11+
actual fun singleton(): InDocumentSearchInjection {
12+
TODO("Not yet implemented")
13+
}
14+
}
15+
}

application/core/persistence_sqldelight/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ sqldelight {
7272
databases {
7373
create("WriteopiaDb") {
7474
packageName.set("io.writeopia.sql")
75-
dialect("app.cash.sqldelight:sqlite-3-30-dialect:2.0.2")
75+
dialect("app.cash.sqldelight:sqlite-3-30-dialect:2.1.0")
7676
dependency(project(":plugins:writeopia_persistence_sqldelight"))
7777
generateAsync.set(true)
7878
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.writeopia.sqldelight.dao
2+
3+
import io.writeopia.app.sql.StoryStepEntityFtsQueries
4+
import io.writeopia.app.sql.TokenEntityQueries
5+
import io.writeopia.sdk.models.story.StoryStep
6+
import io.writeopia.sdk.sql.StoryStepEntityQueries
7+
import io.writeopia.sql.WriteopiaDb
8+
9+
class StoryStepFtsSqlDelightDao(writeopiaDb: WriteopiaDb?) {
10+
11+
private val storyStepEntityQueries: StoryStepEntityFtsQueries? =
12+
writeopiaDb?.storyStepEntityFtsQueries
13+
14+
fun searchInDocument(query: String, documentId: String): Set<Int> =
15+
storyStepEntityQueries?.searchFts(query)
16+
?.executeAsList()
17+
?.filter { (_, documentIdFts) -> documentIdFts == documentId }
18+
?.mapNotNullTo(mutableSetOf()) { (position, _) -> position?.toInt() }
19+
?: emptySet()
20+
21+
suspend fun insertForFts(storyStep: StoryStep, documentId: String, position: Int) {
22+
storyStepEntityQueries?.insertFts(
23+
text = storyStep.text,
24+
id = storyStep.id,
25+
document_id = documentId,
26+
position = position.toLong()
27+
)
28+
}
29+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CREATE VIRTUAL TABLE storyStepEntityFts USING fts4(
2+
text TEXT,
3+
id TEXT,
4+
document_id TEXT UNINDEXED,
5+
position INTEGER UNINDEXED
6+
);
7+
8+
searchFts:
9+
SELECT position, document_id
10+
FROM storyStepEntityFts
11+
WHERE storyStepEntityFts MATCH :query || '*';
12+
13+
insertFts:
14+
INSERT INTO storyStepEntityFts(text, id, document_id, position)
15+
VALUES (?, ?, ?, ?);

0 commit comments

Comments
 (0)