SimpleNotes/persistance/src/main/kotlin/notes/NoteRepositoryImpl.kt

141 lines
4.6 KiB
Kotlin

package be.simplenotes.persistance.notes
import be.simplenotes.domain.model.Note
import be.simplenotes.domain.model.PersistedNote
import be.simplenotes.domain.model.PersistedNoteMetadata
import be.simplenotes.domain.usecases.repositories.NoteRepository
import be.simplenotes.persistance.extensions.uuidBinary
import me.liuwj.ktorm.database.*
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.*
import java.time.LocalDateTime
import java.util.*
import kotlin.collections.HashMap
internal class NoteRepositoryImpl(private val db: Database) : NoteRepository {
@Throws(IllegalArgumentException::class)
override fun findAll(userId: Int, limit: Int, offset: Int, tag: String?): List<PersistedNoteMetadata> {
require(limit > 0) { "limit should be positive" }
require(offset >= 0) { "offset should not be negative" }
val uuids1: List<UUID>? = if (tag != null) {
db.from(Tags)
.leftJoin(Notes, on = Notes.uuid eq Tags.noteUuid)
.select(Notes.uuid)
.where { (Notes.userId eq userId) and (Tags.name eq tag) }
.map { it[Notes.uuid]!! }
} else null
var query = db.notes
.filterColumns { listOf(it.uuid, it.title, it.updatedAt) }
.filter { it.userId eq userId }
if (uuids1 != null) query = query.filter { it.uuid inList uuids1 }
val notes = query
.sortedByDescending { it.updatedAt }
.take(limit)
.drop(offset)
.toList()
if (notes.isEmpty()) return emptyList()
val uuids = notes.map { note -> note.uuid }
val tagsByUuid = db.tags
.filterColumns { listOf(it.noteUuid, it.name) }
.filter { it.noteUuid inList uuids }
.groupByTo(HashMap(), { it.note.uuid }, { it.name })
return notes.map { note ->
val tags = tagsByUuid[note.uuid] ?: emptyList()
note.toPersistedMetadata(tags)
}
}
override fun exists(userId: Int, uuid: UUID): Boolean {
return db.notes.any { (it.userId eq userId) and (it.uuid eq uuid) }
}
override fun create(userId: Int, note: Note): PersistedNote {
val uuid = UUID.randomUUID()
val entity = note.toEntity(uuid, userId).apply {
this.updatedAt = LocalDateTime.now()
}
db.useTransaction {
db.notes.add(entity)
db.batchInsert(Tags) {
note.meta.tags.forEach { tagName ->
item {
it.noteUuid to uuid
it.name to tagName
}
}
}
}
return entity.toPersistedNote(note.meta.tags)
}
@Suppress("UNCHECKED_CAST")
override fun find(userId: Int, uuid: UUID): PersistedNote? {
val note = db.notes
.filterColumns { it.columns - it.userId }
.filter { it.uuid eq uuid }
.find { it.userId eq userId }
?: return null
val tags = db.tags
.filter { it.noteUuid eq uuid }
.mapColumns { it.name } as List<String>
return note.toPersistedNote(tags)
}
override fun update(userId: Int, uuid: UUID, note: Note): PersistedNote? {
db.useTransaction {
val currentNote = db.notes
.find { it.uuid eq uuid and (it.userId eq userId) }
?: return null
currentNote.title = note.meta.title
currentNote.markdown = note.markdown
currentNote.html = note.html
currentNote.updatedAt = LocalDateTime.now()
currentNote.flushChanges()
// delete all tags
db.delete(Tags) {
it.noteUuid eq uuid
}
// put new ones
note.meta.tags.forEach { tagName ->
db.insert(Tags) {
it.name to tagName
it.noteUuid to uuid
}
}
return currentNote.toPersistedNote(note.meta.tags)
}
}
override fun delete(userId: Int, uuid: UUID): Boolean = db.useTransaction {
db.delete(Notes) { it.uuid eq uuid and (it.userId eq userId) } == 1
}
@Suppress("UNCHECKED_CAST")
override fun getTags(userId: Int): List<String> {
return db.sequenceOf(Tags)
.filter { it.note.userId eq userId }
.mapColumns(isDistinct = true) { it.name } as List<String>
}
override fun count(userId: Int, tag: String?): Int {
if (tag == null) return db.notes.count { it.userId eq userId }
return db.sequenceOf(Tags)
.count { it.name eq tag and (it.note.userId eq userId) }
}
}