Refactoring

This commit is contained in:
eikek
2022-05-31 22:42:35 +02:00
parent 78b19a0940
commit 66aab0c952
10 changed files with 244 additions and 132 deletions

View File

@ -14,11 +14,11 @@ import cats.{Functor, ~>}
import fs2.Stream import fs2.Stream
import docspell.backend.ops.OItemSearch.{ListItemWithTags, SearchSummary} import docspell.backend.ops.OItemSearch.{ListItemWithTags, SearchSummary}
import docspell.common.{AccountId, SearchMode} import docspell.common.{AccountId, Duration, SearchMode}
import docspell.ftsclient.{FtsClient, FtsQuery} import docspell.ftsclient.{FtsClient, FtsQuery}
import docspell.query.{FulltextExtract, ItemQuery, ItemQueryParser} import docspell.query.{FulltextExtract, ItemQuery, ItemQueryParser}
import docspell.store.Store import docspell.store.Store
import docspell.store.impl.TempFtsTable import docspell.store.fts.RFtsResult
import docspell.store.qb.Batch import docspell.store.qb.Batch
import docspell.store.queries._ import docspell.store.queries._
@ -89,6 +89,7 @@ object OSearch {
ftsClient: FtsClient[F] ftsClient: FtsClient[F]
): OSearch[F] = ): OSearch[F] =
new OSearch[F] { new OSearch[F] {
private[this] val logger = docspell.logging.getLogger[F]
def parseQueryString( def parseQueryString(
accountId: AccountId, accountId: AccountId,
@ -145,6 +146,7 @@ object OSearch {
fulltextQuery match { fulltextQuery match {
case Some(ftq) => case Some(ftq) =>
for { for {
timed <- Duration.stopTime[F]
ftq <- createFtsQuery(q.fix.account, batch, ftq) ftq <- createFtsQuery(q.fix.account, batch, ftq)
results <- WeakAsync.liftK[F, ConnectionIO].use { nat => results <- WeakAsync.liftK[F, ConnectionIO].use { nat =>
@ -160,14 +162,21 @@ object OSearch {
.compile .compile
.toVector .toVector
} }
duration <- timed
_ <- logger.debug(s"Simple search with fts in: ${duration.formatExact}")
} yield results } yield results
case None => case None =>
store for {
.transact(QItem.queryItems(q, today, maxNoteLen, batch, None)) timed <- Duration.stopTime[F]
.compile results <- store
.toVector .transact(QItem.queryItems(q, today, maxNoteLen, batch, None))
.compile
.toVector
duration <- timed
_ <- logger.debug(s"Simple search sql in: ${duration.formatExact}")
} yield results
} }
def searchWithDetails( def searchWithDetails(
@ -180,12 +189,15 @@ object OSearch {
): F[Vector[ListItemWithTags]] = ): F[Vector[ListItemWithTags]] =
for { for {
items <- search(maxNoteLen, today, batch)(q, fulltextQuery) items <- search(maxNoteLen, today, batch)(q, fulltextQuery)
timed <- Duration.stopTime[F]
resolved <- store resolved <- store
.transact( .transact(
QItem.findItemsWithTags(q.fix.account.collective, Stream.emits(items)) QItem.findItemsWithTags(q.fix.account.collective, Stream.emits(items))
) )
.compile .compile
.toVector .toVector
duration <- timed
_ <- logger.debug(s"Search: resolved details in: ${duration.formatExact}")
} yield resolved } yield resolved
def searchSummary( def searchSummary(
@ -222,11 +234,11 @@ object OSearch {
def temporaryFtsTable( def temporaryFtsTable(
ftq: FtsQuery, ftq: FtsQuery,
nat: F ~> ConnectionIO nat: F ~> ConnectionIO
): ConnectionIO[TempFtsTable.Table] = ): ConnectionIO[RFtsResult.Table] =
ftsClient ftsClient
.searchAll(ftq) .searchAll(ftq)
.translate(nat) .translate(nat)
.through(TempFtsTable.prepareTable(store.dbms, "fts_result")) .through(RFtsResult.prepareTable(store.dbms, "fts_result"))
.compile .compile
.lastOrError .lastOrError
} }

View File

@ -0,0 +1,30 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.store.fts
import docspell.store.impl.DoobieMeta.jsonMeta
import doobie.Meta
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
/** Highlighting context from a fulltext search.
*
* @param name
* the document name, either attachment name or "item"
* @param context
* lines with highlighting infos
*/
case class ContextEntry(name: String, context: List[String])
object ContextEntry {
implicit val jsonDecoder: Decoder[ContextEntry] = deriveDecoder
implicit val jsonEncoder: Encoder[ContextEntry] = deriveEncoder
implicit val meta: Meta[ContextEntry] =
jsonMeta[ContextEntry]
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.store.fts
import cats.Foldable
import cats.data.NonEmptyList
import cats.effect.Sync
import cats.syntax.all._
import fs2.Pipe
import docspell.common._
import docspell.ftsclient.FtsResult
import docspell.store.Db
import docspell.store.qb.DSL._
import docspell.store.qb._
import doobie._
import doobie.implicits._
/** Temporary table used to store item ids fetched from fulltext search */
case class RFtsResult(id: Ident, score: Option[Double], context: Option[ContextEntry])
object RFtsResult {
def fromResult(result: FtsResult)(m: FtsResult.ItemMatch): RFtsResult = {
val context = m.data match {
case FtsResult.AttachmentData(_, attachName) =>
result.highlight
.get(m.id)
.filter(_.nonEmpty)
.map(str => ContextEntry(attachName, str))
case FtsResult.ItemData =>
result.highlight
.get(m.id)
.filter(_.nonEmpty)
.map(str => ContextEntry("item", str))
}
RFtsResult(m.itemId, m.score.some, context)
}
def prepareTable(db: Db, name: String): Pipe[ConnectionIO, FtsResult, Table] =
TempFtsOps.prepareTable(db, name)
case class Table(tableName: String, alias: Option[String], dbms: Db) extends TableDef {
val id: Column[Ident] = Column("id", this)
val score: Column[Double] = Column("score", this)
val context: Column[ContextEntry] = Column("context", this)
val all: NonEmptyList[Column[_]] = NonEmptyList.of(id, score, context)
def as(newAlias: String): Table = copy(alias = Some(newAlias))
def distinctCte(name: String) =
dbms.fold(
TempFtsOps.distinctCtePg(this, name),
TempFtsOps.distinctCteMaria(this, name),
TempFtsOps.distinctCteH2(this, name)
)
def distinctCteSimple(name: String) =
CteBind(copy(tableName = name) -> Select(select(id), from(this)).distinct)
def insertAll[F[_]: Foldable](rows: F[RFtsResult]): ConnectionIO[Int] =
TempFtsOps.insertBatch(this, rows)
def dropTable: ConnectionIO[Int] =
TempFtsOps.dropTable(Fragment.const0(tableName)).update.run
def createIndex: ConnectionIO[Unit] = {
val analyze = dbms.fold(
TempFtsOps.analyzeTablePg(this),
cio.unit,
cio.unit
)
TempFtsOps.createIndex(this) *> analyze
}
def insert: Pipe[ConnectionIO, FtsResult, Int] =
in => in.evalMap(res => insertAll(res.results.map(RFtsResult.fromResult(res))))
}
private val cio: Sync[ConnectionIO] = Sync[ConnectionIO]
}

View File

@ -4,97 +4,25 @@
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
package docspell.store.impl package docspell.store.fts
import cats.Foldable import cats.Foldable
import cats.data.NonEmptyList
import cats.effect._
import cats.syntax.all._ import cats.syntax.all._
import fs2.{Pipe, Stream} import fs2.{Pipe, Stream}
import docspell.common.{Duration, Ident} import docspell.common.Duration
import docspell.ftsclient.FtsResult import docspell.ftsclient.FtsResult
import docspell.store.Db import docspell.store.Db
import docspell.store.fts.RFtsResult.Table
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb._ import docspell.store.qb._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
/** Temporary table used to store item ids fetched from fulltext search */ private[fts] object TempFtsOps {
object TempFtsTable {
private[this] val logger = docspell.logging.getLogger[ConnectionIO] private[this] val logger = docspell.logging.getLogger[ConnectionIO]
case class Row(id: Ident, score: Option[Double], context: Option[ContextEntry])
object Row {
def from(result: FtsResult)(m: FtsResult.ItemMatch): Row = {
val context = m.data match {
case FtsResult.AttachmentData(_, attachName) =>
result.highlight
.get(m.id)
.filter(_.nonEmpty)
.map(str => ContextEntry(attachName, str))
case FtsResult.ItemData =>
result.highlight
.get(m.id)
.filter(_.nonEmpty)
.map(str => ContextEntry("item", str))
}
Row(m.itemId, m.score.some, context)
}
}
case class ContextEntry(name: String, context: List[String])
object ContextEntry {
implicit val jsonDecoder: Decoder[ContextEntry] = deriveDecoder
implicit val jsonEncoder: Encoder[ContextEntry] = deriveEncoder
implicit val meta: Meta[ContextEntry] =
jsonMeta[ContextEntry]
}
case class Table(tableName: String, alias: Option[String], dbms: Db) extends TableDef {
val id: Column[Ident] = Column("id", this)
val score: Column[Double] = Column("score", this)
val context: Column[ContextEntry] = Column("context", this)
val all: NonEmptyList[Column[_]] = NonEmptyList.of(id, score, context)
def as(newAlias: String): Table = copy(alias = Some(newAlias))
def distinctCte(name: String) =
dbms.fold(
TempFtsTable.distinctCtePg(this, name),
TempFtsTable.distinctCteMaria(this, name),
TempFtsTable.distinctCteH2(this, name)
)
def distinctCteSimple(name: String) =
CteBind(copy(tableName = name) -> Select(select(id), from(this)).distinct)
def insertAll[F[_]: Foldable](rows: F[Row]): ConnectionIO[Int] =
insertBatch(this, rows)
def dropTable: ConnectionIO[Int] =
TempFtsTable.dropTable(Fragment.const0(tableName)).update.run
def createIndex: ConnectionIO[Unit] = {
val analyze = dbms.fold(
TempFtsTable.analyzeTablePg(this),
cio.unit,
cio.unit
)
TempFtsTable.createIndex(this) *> analyze
}
def insert: Pipe[ConnectionIO, FtsResult, Int] =
in => in.evalMap(res => insertAll(res.results.map(Row.from(res))))
}
def createTable(db: Db, name: String): ConnectionIO[Table] = { def createTable(db: Db, name: String): ConnectionIO[Table] = {
val stmt = db.fold( val stmt = db.fold(
createTablePostgreSQL(Fragment.const(name)), createTablePostgreSQL(Fragment.const(name)),
@ -119,7 +47,7 @@ object TempFtsTable {
) )
} yield tt } yield tt
private def dropTable(name: Fragment): Fragment = def dropTable(name: Fragment): Fragment =
sql"""DROP TABLE IF EXISTS $name""" sql"""DROP TABLE IF EXISTS $name"""
private def createTableH2(name: Fragment): ConnectionIO[Int] = private def createTableH2(name: Fragment): ConnectionIO[Int] =
@ -144,7 +72,7 @@ object TempFtsTable {
| context text | context text
|) ON COMMIT DROP;""".stripMargin.update.run |) ON COMMIT DROP;""".stripMargin.update.run
private def createIndex(table: Table): ConnectionIO[Unit] = { def createIndex(table: Table): ConnectionIO[Unit] = {
val tableName = Fragment.const0(table.tableName) val tableName = Fragment.const0(table.tableName)
val idIdxName = Fragment.const0(s"${table.tableName}_id_idx") val idIdxName = Fragment.const0(s"${table.tableName}_id_idx")
@ -156,21 +84,67 @@ object TempFtsTable {
sql"CREATE INDEX IF NOT EXISTS $scoreIdxName ON $tableName($score)".update.run.void sql"CREATE INDEX IF NOT EXISTS $scoreIdxName ON $tableName($score)".update.run.void
} }
private def analyzeTablePg(table: Table): ConnectionIO[Unit] = { def analyzeTablePg(table: Table): ConnectionIO[Unit] = {
val tableName = Fragment.const0(table.tableName) val tableName = Fragment.const0(table.tableName)
sql"ANALYZE $tableName".update.run.void sql"ANALYZE $tableName".update.run.void
} }
private def insertBatch[F[_]: Foldable](table: Table, rows: F[Row]) = { // // slowest (9 runs, 6000 rows each, ~170ms)
// def insertBatch2[F[_]: Foldable](table: Table, rows: F[RFtsResult]) = {
// val sql =
// s"""INSERT INTO ${table.tableName}
// | (${table.id.name}, ${table.score.name}, ${table.context.name})
// | VALUES (?, ?, ?)""".stripMargin
//
// Update[RFtsResult](sql).updateMany(rows)
// }
// // better (~115ms)
// def insertBatch3[F[_]: Foldable](
// table: Table,
// rows: F[RFtsResult]
// ): ConnectionIO[Int] = {
// val values = rows
// .foldl(List.empty[Fragment]) { (res, row) =>
// sql"(${row.id},${row.score},${row.context})" :: res
// }
//
// DML.insertMulti(table, table.all, values)
// }
// ~96ms
def insertBatch[F[_]: Foldable](
table: Table,
rows: F[RFtsResult]
): ConnectionIO[Int] = {
val values = rows
.foldl(List.empty[String]) { (res, _) =>
"(?,?,?)" :: res
}
.mkString(",")
val sql = val sql =
s"""INSERT INTO ${table.tableName} s"""INSERT INTO ${table.tableName}
| (${table.id.name}, ${table.score.name}, ${table.context.name}) | (${table.id.name}, ${table.score.name}, ${table.context.name})
| VALUES (?, ?, ?)""".stripMargin | VALUES $values""".stripMargin
Update[Row](sql).updateMany(rows) val encoder = io.circe.Encoder[ContextEntry]
doobie.free.FC.raw { conn =>
val pst = conn.prepareStatement(sql)
rows.foldl(0) { (index, row) =>
pst.setString(index + 1, row.id.id)
row.score
.map(d => pst.setDouble(index + 2, d))
.getOrElse(pst.setNull(index + 2, java.sql.Types.DOUBLE))
row.context
.map(c => pst.setString(index + 3, encoder(c).noSpaces))
.getOrElse(pst.setNull(index + 3, java.sql.Types.VARCHAR))
index + 3
}
pst.executeUpdate()
}
} }
private def distinctCtePg(table: Table, name: String): CteBind = def distinctCtePg(table: Table, name: String): CteBind =
CteBind( CteBind(
table.copy(tableName = name) -> table.copy(tableName = name) ->
Select( Select(
@ -183,7 +157,7 @@ object TempFtsTable {
).groupBy(table.id) ).groupBy(table.id)
) )
private def distinctCteMaria(table: Table, name: String): CteBind = def distinctCteMaria(table: Table, name: String): CteBind =
CteBind( CteBind(
table.copy(tableName = name) -> table.copy(tableName = name) ->
Select( Select(
@ -196,7 +170,7 @@ object TempFtsTable {
).groupBy(table.id) ).groupBy(table.id)
) )
private def distinctCteH2(table: Table, name: String): CteBind = def distinctCteH2(table: Table, name: String): CteBind =
CteBind( CteBind(
table.copy(tableName = name) -> table.copy(tableName = name) ->
Select( Select(
@ -208,6 +182,4 @@ object TempFtsTable {
from(table) from(table)
).groupBy(table.id) ).groupBy(table.id)
) )
private val cio: Sync[ConnectionIO] = Sync[ConnectionIO]
} }

View File

@ -41,6 +41,17 @@ object DML extends DoobieMeta {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
insertFragment(table, cols, values).update.run insertFragment(table, cols, values).update.run
def insertMulti(
table: TableDef,
cols: Nel[Column[_]],
values: Seq[Fragment]
): ConnectionIO[Int] =
(fr"INSERT INTO ${FromExprBuilder.buildTable(table)} (" ++
cols
.map(SelectExprBuilder.columnNoPrefix)
.reduceLeft(_ ++ comma ++ _) ++
fr") VALUES ${values.reduce(_ ++ comma ++ _)}").update.run
def insertFragment( def insertFragment(
table: TableDef, table: TableDef,
cols: Nel[Column[_]], cols: Nel[Column[_]],

View File

@ -6,7 +6,7 @@
package docspell.store.queries package docspell.store.queries
import docspell.store.impl.TempFtsTable import docspell.store.fts.RFtsResult
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.records.RItem import docspell.store.records.RItem
@ -16,7 +16,7 @@ trait FtsSupport {
implicit final class SelectOps(select: Select) { implicit final class SelectOps(select: Select) {
def joinFtsIdOnly( def joinFtsIdOnly(
itemTable: RItem.Table, itemTable: RItem.Table,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
): Select = ): Select =
ftsTable match { ftsTable match {
case Some(ftst) => case Some(ftst) =>
@ -30,7 +30,7 @@ trait FtsSupport {
def joinFtsDetails( def joinFtsDetails(
itemTable: RItem.Table, itemTable: RItem.Table,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
): Select = ): Select =
ftsTable match { ftsTable match {
case Some(ftst) => case Some(ftst) =>
@ -44,7 +44,7 @@ trait FtsSupport {
def ftsCondition( def ftsCondition(
itemTable: RItem.Table, itemTable: RItem.Table,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
): Select = ): Select =
ftsTable match { ftsTable match {
case Some(ftst) => case Some(ftst) =>
@ -55,7 +55,7 @@ trait FtsSupport {
} }
} }
def cteTable(ftsTable: TempFtsTable.Table) = def cteTable(ftsTable: RFtsResult.Table) =
ftsTable.copy(tableName = "cte_fts") ftsTable.copy(tableName = "cte_fts")
} }

View File

@ -7,7 +7,7 @@
package docspell.store.queries package docspell.store.queries
import docspell.common._ import docspell.common._
import docspell.store.impl.TempFtsTable.ContextEntry import docspell.store.fts.ContextEntry
case class ListItem( case class ListItem(
id: Ident, id: Ident,

View File

@ -18,7 +18,7 @@ import docspell.common.{FileKey, IdRef, _}
import docspell.query.ItemQuery.Expr.ValidItemStates import docspell.query.ItemQuery.Expr.ValidItemStates
import docspell.query.{ItemQuery, ItemQueryDsl} import docspell.query.{ItemQuery, ItemQueryDsl}
import docspell.store.Store import docspell.store.Store
import docspell.store.impl.TempFtsTable import docspell.store.fts.RFtsResult
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.qb.generator.{ItemQueryGenerator, Tables} import docspell.store.qb.generator.{ItemQueryGenerator, Tables}
@ -46,7 +46,7 @@ object QItem extends FtsSupport {
private val ti = RTagItem.as("ti") private val ti = RTagItem.as("ti")
private val meta = RFileMeta.as("fmeta") private val meta = RFileMeta.as("fmeta")
private def orderSelect(ftsOpt: Option[TempFtsTable.Table]): OrderSelect = private def orderSelect(ftsOpt: Option[RFtsResult.Table]): OrderSelect =
new OrderSelect { new OrderSelect {
val item = i val item = i
val fts = ftsOpt val fts = ftsOpt
@ -59,7 +59,7 @@ object QItem extends FtsSupport {
today: LocalDate, today: LocalDate,
maxNoteLen: Int, maxNoteLen: Int,
batch: Batch, batch: Batch,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
) = { ) = {
val cteFts = ftsTable.map(cteTable) val cteFts = ftsTable.map(cteTable)
val sql = val sql =
@ -170,7 +170,7 @@ object QItem extends FtsSupport {
q: Query.Fix, q: Query.Fix,
today: LocalDate, today: LocalDate,
noteMaxLen: Int, noteMaxLen: Int,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
): Select.Ordered = { ): Select.Ordered = {
val coll = q.account.collective val coll = q.account.collective
@ -322,7 +322,7 @@ object QItem extends FtsSupport {
): Stream[ConnectionIO, ListItem] = ): Stream[ConnectionIO, ListItem] =
queryItems(q, today, maxNoteLen, batch, None) queryItems(q, today, maxNoteLen, batch, None)
def searchStats(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchStats(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[SearchSummary] = ): ConnectionIO[SearchSummary] =
for { for {
@ -349,7 +349,7 @@ object QItem extends FtsSupport {
def searchTagCategorySummary( def searchTagCategorySummary(
today: LocalDate, today: LocalDate,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
)(q: Query): ConnectionIO[List[CategoryCount]] = { )(q: Query): ConnectionIO[List[CategoryCount]] = {
val tagFrom = val tagFrom =
from(ti) from(ti)
@ -374,7 +374,7 @@ object QItem extends FtsSupport {
} yield existing ++ other.map(n => CategoryCount(n.some, 0)) } yield existing ++ other.map(n => CategoryCount(n.some, 0))
} }
def searchTagSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchTagSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[TagCount]] = { ): ConnectionIO[List[TagCount]] = {
val tagFrom = val tagFrom =
@ -401,7 +401,7 @@ object QItem extends FtsSupport {
} yield existing ++ other.map(TagCount(_, 0)) } yield existing ++ other.map(TagCount(_, 0))
} }
def searchCountSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchCountSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[Int] = ): ConnectionIO[Int] =
findItemsBase(q.fix, today, 0, None).unwrap findItemsBase(q.fix, today, 0, None).unwrap
@ -412,22 +412,22 @@ object QItem extends FtsSupport {
.query[Int] .query[Int]
.unique .unique
def searchCorrOrgSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchCorrOrgSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[IdRefCount]] = ): ConnectionIO[List[IdRefCount]] =
searchIdRefSummary(org.oid, org.name, i.corrOrg, today, ftsTable)(q) searchIdRefSummary(org.oid, org.name, i.corrOrg, today, ftsTable)(q)
def searchCorrPersonSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchCorrPersonSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[IdRefCount]] = ): ConnectionIO[List[IdRefCount]] =
searchIdRefSummary(pers0.pid, pers0.name, i.corrPerson, today, ftsTable)(q) searchIdRefSummary(pers0.pid, pers0.name, i.corrPerson, today, ftsTable)(q)
def searchConcPersonSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchConcPersonSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[IdRefCount]] = ): ConnectionIO[List[IdRefCount]] =
searchIdRefSummary(pers1.pid, pers1.name, i.concPerson, today, ftsTable)(q) searchIdRefSummary(pers1.pid, pers1.name, i.concPerson, today, ftsTable)(q)
def searchConcEquipSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchConcEquipSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[IdRefCount]] = ): ConnectionIO[List[IdRefCount]] =
searchIdRefSummary(equip.eid, equip.name, i.concEquipment, today, ftsTable)(q) searchIdRefSummary(equip.eid, equip.name, i.concEquipment, today, ftsTable)(q)
@ -437,7 +437,7 @@ object QItem extends FtsSupport {
nameCol: Column[String], nameCol: Column[String],
fkCol: Column[Ident], fkCol: Column[Ident],
today: LocalDate, today: LocalDate,
ftsTable: Option[TempFtsTable.Table] ftsTable: Option[RFtsResult.Table]
)(q: Query): ConnectionIO[List[IdRefCount]] = )(q: Query): ConnectionIO[List[IdRefCount]] =
findItemsBase(q.fix, today, 0, None).unwrap findItemsBase(q.fix, today, 0, None).unwrap
.joinFtsIdOnly(i, ftsTable) .joinFtsIdOnly(i, ftsTable)
@ -450,7 +450,7 @@ object QItem extends FtsSupport {
.query[IdRefCount] .query[IdRefCount]
.to[List] .to[List]
def searchFolderSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchFolderSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[FolderCount]] = { ): ConnectionIO[List[FolderCount]] = {
val fu = RUser.as("fu") val fu = RUser.as("fu")
@ -465,7 +465,7 @@ object QItem extends FtsSupport {
.to[List] .to[List]
} }
def searchFieldSummary(today: LocalDate, ftsTable: Option[TempFtsTable.Table])( def searchFieldSummary(today: LocalDate, ftsTable: Option[RFtsResult.Table])(
q: Query q: Query
): ConnectionIO[List[FieldStats]] = { ): ConnectionIO[List[FieldStats]] = {
val fieldJoin = val fieldJoin =

View File

@ -8,7 +8,7 @@ package docspell.store.queries
import docspell.common._ import docspell.common._
import docspell.query.ItemQuery import docspell.query.ItemQuery
import docspell.store.impl.TempFtsTable import docspell.store.fts.RFtsResult
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb.{Column, OrderBy} import docspell.store.qb.{Column, OrderBy}
import docspell.store.records.RItem import docspell.store.records.RItem
@ -33,7 +33,7 @@ case class Query(fix: Query.Fix, cond: Query.QueryCond) {
object Query { object Query {
trait OrderSelect { trait OrderSelect {
def item: RItem.Table def item: RItem.Table
def fts: Option[TempFtsTable.Table] def fts: Option[RFtsResult.Table]
def byDefault: OrderBy = def byDefault: OrderBy =
OrderBy.desc(coalesce(item.itemDate.s, item.created.s).s) OrderBy.desc(coalesce(item.itemDate.s, item.created.s).s)

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
package docspell.store.impl package docspell.store.fts
import java.time.LocalDate import java.time.LocalDate
@ -18,7 +18,7 @@ import docspell.ftsclient.FtsResult
import docspell.ftsclient.FtsResult.{AttachmentData, ItemMatch} import docspell.ftsclient.FtsResult.{AttachmentData, ItemMatch}
import docspell.logging.Level import docspell.logging.Level
import docspell.store._ import docspell.store._
import docspell.store.impl.TempFtsTable.Row import docspell.store.fts.RFtsResult
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.queries.{QItem, Query} import docspell.store.queries.{QItem, Query}
@ -26,7 +26,7 @@ import docspell.store.records.{RCollective, RItem}
import doobie._ import doobie._
class TempFtsTableTest extends DatabaseTest { class TempFtsOpsTest extends DatabaseTest {
private[this] val logger = docspell.logging.getLogger[IO] private[this] val logger = docspell.logging.getLogger[IO]
override def rootMinimumLevel = Level.Info override def rootMinimumLevel = Level.Info
@ -53,12 +53,11 @@ class TempFtsTableTest extends DatabaseTest {
_ <- prepareItems(maria) _ <- prepareItems(maria)
_ <- prepareItems(h2) _ <- prepareItems(h2)
_ <- assertQueryItem(pg, ftsResults(10, 10)) _ <- assertQueryItem(pg, ftsResults(10, 10))
_ <- assertQueryItem(pg, ftsResults(3000, 500)) // _ <- assertQueryItem(pg, ftsResults(3000, 500))
_ <- assertQueryItem(pg, ftsResults(3000, 500))
_ <- assertQueryItem(maria, ftsResults(10, 10)) _ <- assertQueryItem(maria, ftsResults(10, 10))
_ <- assertQueryItem(maria, ftsResults(3000, 500)) // _ <- assertQueryItem(maria, ftsResults(3000, 500))
_ <- assertQueryItem(h2, ftsResults(10, 10)) _ <- assertQueryItem(h2, ftsResults(10, 10))
_ <- assertQueryItem(h2, ftsResults(3000, 500)) // _ <- assertQueryItem(h2, ftsResults(3000, 500))
} yield () } yield ()
} }
@ -74,19 +73,19 @@ class TempFtsTableTest extends DatabaseTest {
def assertCreateTempTable(store: Store[IO]) = { def assertCreateTempTable(store: Store[IO]) = {
val insertRows = val insertRows =
List( List(
Row(id("abc-def"), None, None), RFtsResult(id("abc-def"), None, None),
Row(id("abc-123"), Some(1.56), None), RFtsResult(id("abc-123"), Some(1.56), None),
Row(id("zyx-321"), None, None) RFtsResult(id("zyx-321"), None, None)
) )
val create = val create =
for { for {
table <- TempFtsTable.createTable(store.dbms, "tt") table <- TempFtsOps.createTable(store.dbms, "tt")
n <- table.insertAll(insertRows) n <- table.insertAll(insertRows)
_ <- table.createIndex _ <- table.createIndex
rows <- Select(select(table.all), from(table)) rows <- Select(select(table.all), from(table))
.orderBy(table.id) .orderBy(table.id)
.build .build
.query[Row] .query[RFtsResult]
.to[List] .to[List]
} yield (n, rows) } yield (n, rows)
@ -106,7 +105,7 @@ class TempFtsTableTest extends DatabaseTest {
today <- IO(LocalDate.now()) today <- IO(LocalDate.now())
account = DocspellSystem.account account = DocspellSystem.account
tempTable = ftsResults tempTable = ftsResults
.through(TempFtsTable.prepareTable(store.dbms, "fts_result")) .through(TempFtsOps.prepareTable(store.dbms, "fts_result"))
.compile .compile
.lastOrError .lastOrError
q = Query(Query.Fix(account, None, None), Query.QueryExpr(None)) q = Query(Query.Fix(account, None, None), Query.QueryExpr(None))