Convert more records

This commit is contained in:
Eike Kettner 2020-12-13 13:35:40 +01:00
parent 613696539f
commit fd6d09587d
15 changed files with 996 additions and 675 deletions

View File

@ -74,6 +74,10 @@ object DML {
case Setter.Increment(column, amount) => case Setter.Increment(column, amount) =>
val colFrag = SelectExprBuilder.columnNoPrefix(column) val colFrag = SelectExprBuilder.columnNoPrefix(column)
colFrag ++ fr" =" ++ colFrag ++ fr" + $amount" colFrag ++ fr" =" ++ colFrag ++ fr" + $amount"
case Setter.Decrement(column, amount) =>
val colFrag = SelectExprBuilder.columnNoPrefix(column)
colFrag ++ fr" =" ++ colFrag ++ fr" - $amount"
} }
def set(s: Setter[_], more: Setter[_]*): Nel[Setter[_]] = def set(s: Setter[_], more: Setter[_]*): Nel[Setter[_]] =

View File

@ -127,6 +127,9 @@ trait DSL extends DoobieMeta {
def increment(amount: Int): Setter[A] = def increment(amount: Int): Setter[A] =
Setter.Increment(col, amount) Setter.Increment(col, amount)
def decrement(amount: Int): Setter[A] =
Setter.Decrement(col, amount)
def asc: OrderBy = def asc: OrderBy =
OrderBy(SelectExpr.SelectColumn(col, None), OrderBy.OrderType.Asc) OrderBy(SelectExpr.SelectColumn(col, None), OrderBy.OrderType.Asc)
@ -177,6 +180,9 @@ trait DSL extends DoobieMeta {
def ===(other: Column[A]): Condition = def ===(other: Column[A]): Condition =
Condition.CompareCol(col, Operator.Eq, other) Condition.CompareCol(col, Operator.Eq, other)
def <>(other: Column[A]): Condition =
Condition.CompareCol(col, Operator.Neq, other)
} }
implicit final class ConditionOps(c: Condition) { implicit final class ConditionOps(c: Condition) {

View File

@ -13,5 +13,6 @@ object Setter {
extends Setter[A] extends Setter[A]
case class Increment[A](column: Column[A], amount: Int) extends Setter[A] case class Increment[A](column: Column[A], amount: Int) extends Setter[A]
case class Decrement[A](column: Column[A], amount: Int) extends Setter[A]
} }

View File

@ -180,14 +180,17 @@ object QAttachment {
val iId = RItem.Columns.id.prefix("i") val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") val iColl = RItem.Columns.cid.prefix("i")
val iFolder = RItem.Columns.folder.prefix("i") val iFolder = RItem.Columns.folder.prefix("i")
val cId = RCollective.Columns.id.prefix("c") val c = RCollective.as("c")
val cLang = RCollective.Columns.language.prefix("c") val cId = c.id.column
val cLang = c.language.column
val cols = Seq(aId, aItem, iColl, iFolder, cLang, aName, mContent) val cols = Seq(aId, aItem, iColl, iFolder, cLang, aName, mContent)
val from = RAttachment.table ++ fr"a INNER JOIN" ++ val from = RAttachment.table ++ fr"a INNER JOIN" ++
RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) ++ RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) ++
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem) ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem) ++
fr"INNER JOIN" ++ RCollective.table ++ fr"c ON" ++ cId.is(iColl) fr"INNER JOIN" ++ Fragment.const(RCollective.T.tableName) ++ fr"c ON" ++ cId.is(
iColl
)
val where = coll.map(cid => iColl.is(cid)).getOrElse(Fragment.empty) val where = coll.map(cid => iColl.is(cid)).getOrElse(Fragment.empty)

View File

@ -633,27 +633,31 @@ object QItem {
limit: Option[Int], limit: Option[Int],
states: Set[ItemState] states: Set[ItemState]
): Fragment = { ): Fragment = {
val IC = RItem.Columns.all.map(_.prefix("i")) val IC = RItem.Columns.all.map(_.prefix("i"))
val aItem = RAttachment.Columns.itemId.prefix("a") val aItem = RAttachment.Columns.itemId.prefix("a")
val aId = RAttachment.Columns.id.prefix("a") val aId = RAttachment.Columns.id.prefix("a")
val aFileId = RAttachment.Columns.fileId.prefix("a") val aFileId = RAttachment.Columns.fileId.prefix("a")
val iId = RItem.Columns.id.prefix("i") val iId = RItem.Columns.id.prefix("i")
val iState = RItem.Columns.state.prefix("i") val iState = RItem.Columns.state.prefix("i")
val sId = RAttachmentSource.Columns.id.prefix("s") val sId = RAttachmentSource.Columns.id.prefix("s")
val sFileId = RAttachmentSource.Columns.fileId.prefix("s") val sFileId = RAttachmentSource.Columns.fileId.prefix("s")
val rId = RAttachmentArchive.Columns.id.prefix("r") val rId = RAttachmentArchive.Columns.id.prefix("r")
val rFileId = RAttachmentArchive.Columns.fileId.prefix("r") val rFileId = RAttachmentArchive.Columns.fileId.prefix("r")
val m1Id = RFileMeta.Columns.id.prefix("m1") val m1 = RFileMeta.as("m1")
val m2Id = RFileMeta.Columns.id.prefix("m2") val m2 = RFileMeta.as("m2")
val m3Id = RFileMeta.Columns.id.prefix("m3") val m3 = RFileMeta.as("m3")
val m1Id = m1.id.column
val m2Id = m2.id.column
val m3Id = m3.id.column
val filemetaTable = Fragment.const(RFileMeta.T.tableName)
val from = val from =
RItem.table ++ fr"i INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ aItem.is(iId) ++ RItem.table ++ fr"i INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ aItem.is(iId) ++
fr"INNER JOIN" ++ RAttachmentSource.table ++ fr"s ON" ++ aId.is(sId) ++ fr"INNER JOIN" ++ RAttachmentSource.table ++ fr"s ON" ++ aId.is(sId) ++
fr"INNER JOIN" ++ RFileMeta.table ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ fr"INNER JOIN" ++ filemetaTable ++ fr"m1 ON" ++ m1Id.is(aFileId) ++
fr"INNER JOIN" ++ RFileMeta.table ++ fr"m2 ON" ++ m2Id.is(sFileId) ++ fr"INNER JOIN" ++ filemetaTable ++ fr"m2 ON" ++ m2Id.is(sFileId) ++
fr"LEFT OUTER JOIN" ++ RAttachmentArchive.table ++ fr"r ON" ++ aId.is(rId) ++ fr"LEFT OUTER JOIN" ++ RAttachmentArchive.table ++ fr"r ON" ++ aId.is(rId) ++
fr"LEFT OUTER JOIN" ++ RFileMeta.table ++ fr"m3 ON" ++ m3Id.is(rFileId) fr"LEFT OUTER JOIN" ++ filemetaTable ++ fr"m3 ON" ++ m3Id.is(rFileId)
val fileCond = val fileCond =
or(m1Id.isIn(fileMetaIds), m2Id.isIn(fileMetaIds), m3Id.isIn(fileMetaIds)) or(m1Id.isIn(fileMetaIds), m2Id.isIn(fileMetaIds), m3Id.isIn(fileMetaIds))
@ -691,30 +695,33 @@ object QItem {
} }
def findByChecksum(checksum: String, collective: Ident): ConnectionIO[Vector[RItem]] = { def findByChecksum(checksum: String, collective: Ident): ConnectionIO[Vector[RItem]] = {
val IC = RItem.Columns.all.map(_.prefix("i")) val IC = RItem.Columns.all.map(_.prefix("i"))
val aItem = RAttachment.Columns.itemId.prefix("a") val aItem = RAttachment.Columns.itemId.prefix("a")
val aId = RAttachment.Columns.id.prefix("a") val aId = RAttachment.Columns.id.prefix("a")
val aFileId = RAttachment.Columns.fileId.prefix("a") val aFileId = RAttachment.Columns.fileId.prefix("a")
val iId = RItem.Columns.id.prefix("i") val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") val iColl = RItem.Columns.cid.prefix("i")
val sId = RAttachmentSource.Columns.id.prefix("s") val sId = RAttachmentSource.Columns.id.prefix("s")
val sFileId = RAttachmentSource.Columns.fileId.prefix("s") val sFileId = RAttachmentSource.Columns.fileId.prefix("s")
val rId = RAttachmentArchive.Columns.id.prefix("r") val rId = RAttachmentArchive.Columns.id.prefix("r")
val rFileId = RAttachmentArchive.Columns.fileId.prefix("r") val rFileId = RAttachmentArchive.Columns.fileId.prefix("r")
val m1Id = RFileMeta.Columns.id.prefix("m1") val m1 = RFileMeta.as("m1")
val m2Id = RFileMeta.Columns.id.prefix("m2") val m2 = RFileMeta.as("m2")
val m3Id = RFileMeta.Columns.id.prefix("m3") val m3 = RFileMeta.as("m3")
val m1Checksum = RFileMeta.Columns.checksum.prefix("m1") val m1Id = m1.id.column
val m2Checksum = RFileMeta.Columns.checksum.prefix("m2") val m2Id = m2.id.column
val m3Checksum = RFileMeta.Columns.checksum.prefix("m3") val m3Id = m3.id.column
val m1Checksum = m1.checksum.column
val m2Checksum = m2.checksum.column
val m3Checksum = m3.checksum.column
val filemetaTable = Fragment.const(RFileMeta.T.tableName)
val from = val from =
RItem.table ++ fr"i INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ aItem.is(iId) ++ RItem.table ++ fr"i INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ aItem.is(iId) ++
fr"INNER JOIN" ++ RAttachmentSource.table ++ fr"s ON" ++ aId.is(sId) ++ fr"INNER JOIN" ++ RAttachmentSource.table ++ fr"s ON" ++ aId.is(sId) ++
fr"INNER JOIN" ++ RFileMeta.table ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ fr"INNER JOIN" ++ filemetaTable ++ fr"m1 ON" ++ m1Id.is(aFileId) ++
fr"INNER JOIN" ++ RFileMeta.table ++ fr"m2 ON" ++ m2Id.is(sFileId) ++ fr"INNER JOIN" ++ filemetaTable ++ fr"m2 ON" ++ m2Id.is(sFileId) ++
fr"LEFT OUTER JOIN" ++ RAttachmentArchive.table ++ fr"r ON" ++ aId.is(rId) ++ fr"LEFT OUTER JOIN" ++ RAttachmentArchive.table ++ fr"r ON" ++ aId.is(rId) ++
fr"LEFT OUTER JOIN" ++ RFileMeta.table ++ fr"m3 ON" ++ m3Id.is(rFileId) fr"LEFT OUTER JOIN" ++ filemetaTable ++ fr"m3 ON" ++ m3Id.is(rFileId)
selectSimple( selectSimple(
IC, IC,

View File

@ -3,8 +3,8 @@ package docspell.store.queries
import cats.data.OptionT import cats.data.OptionT
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.records.RCollective.{Columns => CC} import docspell.store.qb._
import docspell.store.records.{RCollective, RRememberMe, RUser} import docspell.store.records.{RCollective, RRememberMe, RUser}
import doobie._ import doobie._
@ -22,20 +22,14 @@ object QLogin {
) )
def findUser(acc: AccountId): ConnectionIO[Option[Data]] = { def findUser(acc: AccountId): ConnectionIO[Option[Data]] = {
val user = RUser.as("u") val user = RUser.as("u")
val ucid = user.cid.column val coll = RCollective.as("c")
val login = user.login.column val sql =
val pass = user.password.column Select(
val ustate = user.state.column select(user.cid, user.login, user.password, coll.state, user.state),
val cstate = CC.state.prefix("c") from(user).innerJoin(coll, user.cid === coll.id),
val ccid = CC.id.prefix("c") user.login === acc.user && user.cid === acc.collective
).build
val sql = selectSimple(
List(ucid, login, pass, cstate, ustate),
Fragment.const(user.tableName) ++ fr"u, " ++ RCollective.table ++ fr"c",
and(ucid.is(ccid), login.is(acc.user), ucid.is(acc.collective))
)
logger.trace(s"SQL : $sql") logger.trace(s"SQL : $sql")
sql.query[Data].option sql.query[Data].option
} }

View File

@ -5,8 +5,9 @@ import cats.implicits._
import fs2.Stream import fs2.Stream
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb.TableDef
import docspell.store.qb._
import bitpeace.FileMeta import bitpeace.FileMeta
import doobie._ import doobie._
@ -22,10 +23,27 @@ case class RAttachment(
) {} ) {}
object RAttachment { object RAttachment {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "attachment"
val id = Column[Ident]("attachid", this)
val itemId = Column[Ident]("itemid", this)
val fileId = Column[Ident]("filemetaid", this)
val position = Column[Int]("position", this)
val created = Column[Timestamp]("created", this)
val name = Column[String]("name", this)
val all = NonEmptyList.of[Column[_]](id, itemId, fileId, position, created, name)
}
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
val table = fr"attachment" val table = fr"attachment"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("attachid") val id = Column("attachid")
val itemId = Column("itemid") val itemId = Column("itemid")
val fileId = Column("filemetaid") val fileId = Column("filemetaid")
@ -34,32 +52,37 @@ object RAttachment {
val name = Column("name") val name = Column("name")
val all = List(id, itemId, fileId, position, created, name) val all = List(id, itemId, fileId, position, created, name)
} }
import Columns._
def insert(v: RAttachment): ConnectionIO[Int] = def insert(v: RAttachment): ConnectionIO[Int] =
insertRow( DML.insert(
table, T,
all, T.all,
fr"${v.id},${v.itemId},${v.fileId.id},${v.position},${v.created},${v.name}" fr"${v.id},${v.itemId},${v.fileId.id},${v.position},${v.created},${v.name}"
).update.run )
def decPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] = def decPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)), where(
position.decrement(1) T.itemId === iId && T.position >= lowerBound && T.position <= upperBound
).update.run ),
DML.set(T.position.decrement(1))
)
def incPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] = def incPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)), where(
position.increment(1) T.itemId === iId && T.position >= lowerBound && T.position <= upperBound
).update.run ),
DML.set(T.position.increment(1))
)
def nextPosition(id: Ident): ConnectionIO[Int] = def nextPosition(id: Ident): ConnectionIO[Int] =
for { for {
max <- selectSimple(position.max, table, itemId.is(id)).query[Option[Int]].unique max <- Select(max(T.position).s, from(T), T.itemId === id).build
.query[Option[Int]]
.unique
} yield max.map(_ + 1).getOrElse(0) } yield max.map(_ + 1).getOrElse(0)
def updateFileIdAndName( def updateFileIdAndName(
@ -67,41 +90,49 @@ object RAttachment {
fId: Ident, fId: Ident,
fname: Option[String] fname: Option[String]
): ConnectionIO[Int] = ): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
id.is(attachId), T.id === attachId,
commas(fileId.setTo(fId), name.setTo(fname)) DML.set(T.fileId.setTo(fId), T.name.setTo(fname))
).update.run )
def updateFileId( def updateFileId(
attachId: Ident, attachId: Ident,
fId: Ident fId: Ident
): ConnectionIO[Int] = ): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
id.is(attachId), T.id === attachId,
fileId.setTo(fId) DML.set(T.fileId.setTo(fId))
).update.run )
def updatePosition(attachId: Ident, pos: Int): ConnectionIO[Int] = def updatePosition(attachId: Ident, pos: Int): ConnectionIO[Int] =
updateRow(table, id.is(attachId), position.setTo(pos)).update.run DML.update(T, T.id === attachId, DML.set(T.position.setTo(pos)))
def findById(attachId: Ident): ConnectionIO[Option[RAttachment]] = def findById(attachId: Ident): ConnectionIO[Option[RAttachment]] =
selectSimple(all, table, id.is(attachId)).query[RAttachment].option run(select(T.all), from(T), T.id === attachId).query[RAttachment].option
def findMeta(attachId: Ident): ConnectionIO[Option[FileMeta]] = { def findMeta(attachId: Ident): ConnectionIO[Option[FileMeta]] = {
import bitpeace.sql._ import bitpeace.sql._
val cols = RFileMeta.Columns.all.map(_.prefix("m")) // val cols = RFileMeta.Columns.all.map(_.prefix("m"))
val aId = id.prefix("a") // val aId = id.prefix("a")
val aFileMeta = fileId.prefix("a") // val aFileMeta = fileId.prefix("a")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.Columns.id.prefix("m")
//
val from = // val from =
table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ aFileMeta.is(mId) // table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ aFileMeta.is(mId)
val cond = aId.is(attachId) // val cond = aId.is(attachId)
//
selectSimple(cols, from, cond).query[FileMeta].option // selectSimple(cols, from, cond).query[FileMeta].option
val m = RFileMeta.as("m")
val a = RAttachment.as("a")
Select(
select(m.all),
from(a)
.innerJoin(m, a.fileId === m.id),
a.id === attachId
).build.query[FileMeta].option
} }
def updateName( def updateName(
@ -109,7 +140,7 @@ object RAttachment {
collective: Ident, collective: Ident,
aname: Option[String] aname: Option[String]
): ConnectionIO[Int] = { ): ConnectionIO[Int] = {
val update = updateRow(table, id.is(attachId), name.setTo(aname)).update.run val update = DML.update(T, T.id === attachId, DML.set(T.name.setTo(aname)))
for { for {
exists <- existsByIdAndCollective(attachId, collective) exists <- existsByIdAndCollective(attachId, collective)
n <- if (exists) update else 0.pure[ConnectionIO] n <- if (exists) update else 0.pure[ConnectionIO]
@ -119,44 +150,59 @@ object RAttachment {
def findByIdAndCollective( def findByIdAndCollective(
attachId: Ident, attachId: Ident,
collective: Ident collective: Ident
): ConnectionIO[Option[RAttachment]] = ): ConnectionIO[Option[RAttachment]] = {
selectSimple( val a = RAttachment.as("a")
all.map(_.prefix("a")), val i = RItem.as("i")
table ++ fr"a," ++ RItem.table ++ fr"i", Select(
and( select(a.all),
fr"a.itemid = i.itemid", from(a).innerJoin(i, a.itemId === i.id),
id.prefix("a").is(attachId), a.id === attachId && i.cid === collective
RItem.Columns.cid.prefix("i").is(collective) ).build.query[RAttachment].option
) }
).query[RAttachment].option
def findByItem(id: Ident): ConnectionIO[Vector[RAttachment]] = def findByItem(id: Ident): ConnectionIO[Vector[RAttachment]] =
selectSimple(all, table, itemId.is(id)).query[RAttachment].to[Vector] run(select(T.all), from(T), T.itemId === id).query[RAttachment].to[Vector]
def existsByIdAndCollective( def existsByIdAndCollective(
attachId: Ident, attachId: Ident,
collective: Ident collective: Ident
): ConnectionIO[Boolean] = { ): ConnectionIO[Boolean] = {
val aId = id.prefix("a") // val aId = id.prefix("a")
val aItem = itemId.prefix("a") // val aItem = itemId.prefix("a")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
val from = // val from =
table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) // table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId)
val cond = and(iColl.is(collective), aId.is(attachId)) // val cond = and(iColl.is(collective), aId.is(attachId))
selectCount(id, from, cond).query[Int].unique.map(_ > 0) // selectCount(id, from, cond).query[Int].unique.map(_ > 0)
val a = RAttachment.as("a")
val i = RItem.as("i")
Select(
count(a.id).s,
from(a)
.innerJoin(i, a.itemId === i.id),
i.cid === collective && a.id === attachId
).build.query[Int].unique.map(_ > 0)
} }
def findByItemAndCollective( def findByItemAndCollective(
id: Ident, id: Ident,
coll: Ident coll: Ident
): ConnectionIO[Vector[RAttachment]] = { ): ConnectionIO[Vector[RAttachment]] = {
val q = selectSimple(all.map(_.prefix("a")), table ++ fr"a", Fragment.empty) ++ // val q = selectSimple(all.map(_.prefix("a")), table ++ fr"a", Fragment.empty) ++
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ RItem.Columns.id // fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ RItem.Columns.id
.prefix("i") // .prefix("i")
.is(itemId.prefix("a")) ++ // .is(itemId.prefix("a")) ++
fr"WHERE" ++ and(itemId.prefix("a").is(id), RItem.Columns.cid.prefix("i").is(coll)) // fr"WHERE" ++ and(itemId.prefix("a").is(id), RItem.Columns.cid.prefix("i").is(coll))
q.query[RAttachment].to[Vector] // q.query[RAttachment].to[Vector]
val a = RAttachment.as("a")
val i = RItem.as("i")
Select(
select(a.all),
from(a)
.innerJoin(i, i.id === a.itemId),
a.itemId === id && i.cid === coll
).build.query[RAttachment].to[Vector]
} }
def findByItemCollectiveSource( def findByItemCollectiveSource(
@ -165,28 +211,42 @@ object RAttachment {
fileIds: NonEmptyList[Ident] fileIds: NonEmptyList[Ident]
): ConnectionIO[Vector[RAttachment]] = { ): ConnectionIO[Vector[RAttachment]] = {
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
val aItem = Columns.itemId.prefix("a") // val aItem = Columns.itemId.prefix("a")
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val aFile = Columns.fileId.prefix("a") // val aFile = Columns.fileId.prefix("a")
val sId = RAttachmentSource.Columns.id.prefix("s") // val sId = RAttachmentSource.Columns.id.prefix("s")
val sFile = RAttachmentSource.Columns.fileId.prefix("s") // val sFile = RAttachmentSource.Columns.fileId.prefix("s")
val rId = RAttachmentArchive.Columns.id.prefix("r") // val rId = RAttachmentArchive.Columns.id.prefix("r")
val rFile = RAttachmentArchive.Columns.fileId.prefix("r") // val rFile = RAttachmentArchive.Columns.fileId.prefix("r")
//
// val from = table ++ fr"a INNER JOIN" ++
// RItem.table ++ fr"i ON" ++ iId.is(aItem) ++ fr"LEFT JOIN" ++
// RAttachmentSource.table ++ fr"s ON" ++ sId.is(aId) ++ fr"LEFT JOIN" ++
// RAttachmentArchive.table ++ fr"r ON" ++ rId.is(aId)
//
// val cond = and(
// iId.is(id),
// iColl.is(coll),
// or(aFile.isIn(fileIds), sFile.isIn(fileIds), rFile.isIn(fileIds))
// )
//
// selectSimple(all.map(_.prefix("a")), from, cond).query[RAttachment].to[Vector]
val i = RItem.as("i")
val a = RAttachment.as("a")
val s = RAttachmentSource.as("s")
val r = RAttachmentArchive.as("r")
val from = table ++ fr"a INNER JOIN" ++ Select(
RItem.table ++ fr"i ON" ++ iId.is(aItem) ++ fr"LEFT JOIN" ++ select(a.all),
RAttachmentSource.table ++ fr"s ON" ++ sId.is(aId) ++ fr"LEFT JOIN" ++ from(a)
RAttachmentArchive.table ++ fr"r ON" ++ rId.is(aId) .innerJoin(i, i.id === a.itemId)
.leftJoin(s, s.id === a.id)
val cond = and( .leftJoin(r, r.id === a.id),
iId.is(id), i.id === id && i.cid === coll &&
iColl.is(coll), (a.fileId.in(fileIds) || s.fileId.in(fileIds) || r.fileId.in(fileIds))
or(aFile.isIn(fileIds), sFile.isIn(fileIds), rFile.isIn(fileIds)) ).build.query[RAttachment].to[Vector]
)
selectSimple(all.map(_.prefix("a")), from, cond).query[RAttachment].to[Vector]
} }
def findByItemAndCollectiveWithMeta( def findByItemAndCollectiveWithMeta(
@ -195,27 +255,45 @@ object RAttachment {
): ConnectionIO[Vector[(RAttachment, FileMeta)]] = { ): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
import bitpeace.sql._ import bitpeace.sql._
val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) // val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m"))
val afileMeta = fileId.prefix("a") // val afileMeta = fileId.prefix("a")
val aItem = itemId.prefix("a") // val aItem = itemId.prefix("a")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.Columns.id.prefix("m")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
val from = // val from =
table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ // table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) // fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId)
val cond = Seq(aItem.is(id), iColl.is(coll)) // val cond = Seq(aItem.is(id), iColl.is(coll))
//
selectSimple(cols, from, and(cond)).query[(RAttachment, FileMeta)].to[Vector] // selectSimple(cols, from, and(cond)).query[(RAttachment, FileMeta)].to[Vector]
val a = RAttachment.as("a")
val m = RFileMeta.as("m")
val i = RItem.as("i")
Select(
select(a.all, m.all),
from(a)
.innerJoin(m, a.fileId === m.id)
.innerJoin(i, a.itemId === i.id),
a.itemId === id && i.cid === coll
).build.query[(RAttachment, FileMeta)].to[Vector]
} }
def findByItemWithMeta(id: Ident): ConnectionIO[Vector[(RAttachment, FileMeta)]] = { def findByItemWithMeta(id: Ident): ConnectionIO[Vector[(RAttachment, FileMeta)]] = {
import bitpeace.sql._ import bitpeace.sql._
val q = // val q =
fr"SELECT a.*,m.* FROM" ++ table ++ fr"a, filemeta m WHERE a.filemetaid = m.id AND a.itemid = $id ORDER BY a.position ASC" // fr"SELECT a.*,m.* FROM" ++ table ++ fr"a, filzemeta m
q.query[(RAttachment, FileMeta)].to[Vector] // WHERE a.filemetaid = m.id AND a.itemid = $id ORDER BY a.position ASC"
val a = RAttachment.as("a")
val m = RFileMeta.as("m")
Select(
select(a.all, m.all),
from(a)
.innerJoin(m, a.fileId === m.id),
a.itemId === id
).orderBy(a.position.asc).build.query[(RAttachment, FileMeta)].to[Vector]
} }
/** Deletes the attachment and its related source and meta records. /** Deletes the attachment and its related source and meta records.
@ -225,110 +303,159 @@ object RAttachment {
n0 <- RAttachmentMeta.delete(attachId) n0 <- RAttachmentMeta.delete(attachId)
n1 <- RAttachmentSource.delete(attachId) n1 <- RAttachmentSource.delete(attachId)
n2 <- RAttachmentPreview.delete(attachId) n2 <- RAttachmentPreview.delete(attachId)
n3 <- deleteFrom(table, id.is(attachId)).update.run n3 <- DML.delete(T, T.id === attachId)
} yield n0 + n1 + n2 + n3 } yield n0 + n1 + n2 + n3
def findItemId(attachId: Ident): ConnectionIO[Option[Ident]] = def findItemId(attachId: Ident): ConnectionIO[Option[Ident]] =
selectSimple(Seq(itemId), table, id.is(attachId)).query[Ident].option Select(T.itemId.s, from(T), T.id === attachId).build.query[Ident].option
def findAll( def findAll(
coll: Option[Ident], coll: Option[Ident],
chunkSize: Int chunkSize: Int
): Stream[ConnectionIO, RAttachment] = { ): Stream[ConnectionIO, RAttachment] = {
val aItem = Columns.itemId.prefix("a") // val aItem = Columns.itemId.prefix("a")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
val cols = all.map(_.prefix("a")) // val cols = all.map(_.prefix("a"))
//
// coll match {
// case Some(cid) =>
// val join = table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem)
// val cond = iColl.is(cid)
// selectSimple(cols, join, cond)
// .query[RAttachment]
// .streamWithChunkSize(chunkSize)
// case None =>
// selectSimple(cols, table, Fragment.empty)
// .query[RAttachment]
// .streamWithChunkSize(chunkSize)
// }
val a = RAttachment.as("a")
val i = RItem.as("i")
coll match { coll match {
case Some(cid) => case Some(cid) =>
val join = table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem) Select(
val cond = iColl.is(cid) select(a.all),
selectSimple(cols, join, cond) from(a)
.query[RAttachment] .innerJoin(i, i.id === a.itemId),
.streamWithChunkSize(chunkSize) i.cid === cid
).build.query[RAttachment].streamWithChunkSize(chunkSize)
case None => case None =>
selectSimple(cols, table, Fragment.empty) Select(select(a.all), from(a)).build
.query[RAttachment] .query[RAttachment]
.streamWithChunkSize(chunkSize) .streamWithChunkSize(chunkSize)
} }
} }
def findAllWithoutPageCount(chunkSize: Int): Stream[ConnectionIO, RAttachment] = { def findAllWithoutPageCount(chunkSize: Int): Stream[ConnectionIO, RAttachment] = {
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val aCreated = Columns.created.prefix("a") // val aCreated = Columns.created.prefix("a")
val mId = RAttachmentMeta.Columns.id.prefix("m") // val mId = RAttachmentMeta.Columns.id.prefix("m")
val mPages = RAttachmentMeta.Columns.pages.prefix("m") // val mPages = RAttachmentMeta.Columns.pages.prefix("m")
//
val cols = all.map(_.prefix("a")) // val cols = all.map(_.prefix("a"))
val join = table ++ fr"a LEFT OUTER JOIN" ++ // val join = table ++ fr"a LEFT OUTER JOIN" ++
RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) // RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId)
val cond = mPages.isNull // val cond = mPages.isNull
//
(selectSimple(cols, join, cond) ++ orderBy(aCreated.desc)) // (selectSimple(cols, join, cond) ++ orderBy(aCreated.desc))
.query[RAttachment] // .query[RAttachment]
.streamWithChunkSize(chunkSize) // .streamWithChunkSize(chunkSize)
val a = RAttachment.as("a")
val m = RAttachmentMeta.as("m")
Select(
select(a.all),
from(a)
.leftJoin(m, a.id === m.id),
m.pages.isNull
).build.query[RAttachment].streamWithChunkSize(chunkSize)
} }
def findWithoutPreview( def findWithoutPreview(
coll: Option[Ident], coll: Option[Ident],
chunkSize: Int chunkSize: Int
): Stream[ConnectionIO, RAttachment] = { ): Stream[ConnectionIO, RAttachment] = {
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val aItem = Columns.itemId.prefix("a") // val aItem = Columns.itemId.prefix("a")
val aCreated = Columns.created.prefix("a") // val aCreated = Columns.created.prefix("a")
val pId = RAttachmentPreview.Columns.id.prefix("p") // val pId = RAttachmentPreview.Columns.id.prefix("p")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
// val cols = all.map(_.prefix("a"))
// val baseJoin =
// table ++ fr"a LEFT OUTER JOIN" ++
// RAttachmentPreview.table ++ fr"p ON" ++ pId.is(aId)
//
// val baseCond =
// Seq(pId.isNull)
//
// coll match {
// case Some(cid) =>
// val join = baseJoin ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem)
// val cond = and(baseCond ++ Seq(iColl.is(cid)))
// (selectSimple(cols, join, cond) ++ orderBy(aCreated.desc))
// .query[RAttachment]
// .streamWithChunkSize(chunkSize)
// case None =>
// (selectSimple(cols, baseJoin, and(baseCond)) ++ orderBy(aCreated.desc))
// .query[RAttachment]
// .streamWithChunkSize(chunkSize)
// }
val a = RAttachment.as("a")
val p = RAttachmentPreview.as("p")
val i = RItem.as("i")
val cols = all.map(_.prefix("a")) val baseJoin = from(a).leftJoin(p, p.id === a.id)
val baseJoin = Select(
table ++ fr"a LEFT OUTER JOIN" ++ select(a.all),
RAttachmentPreview.table ++ fr"p ON" ++ pId.is(aId) coll.map(_ => baseJoin.innerJoin(i, i.id === a.itemId)).getOrElse(baseJoin),
p.id.isNull &&? coll.map(cid => i.cid === cid)
val baseCond = ).orderBy(a.created.asc).build.query[RAttachment].streamWithChunkSize(chunkSize)
Seq(pId.isNull)
coll match {
case Some(cid) =>
val join = baseJoin ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem)
val cond = and(baseCond ++ Seq(iColl.is(cid)))
(selectSimple(cols, join, cond) ++ orderBy(aCreated.desc))
.query[RAttachment]
.streamWithChunkSize(chunkSize)
case None =>
(selectSimple(cols, baseJoin, and(baseCond)) ++ orderBy(aCreated.desc))
.query[RAttachment]
.streamWithChunkSize(chunkSize)
}
} }
def findNonConvertedPdf( def findNonConvertedPdf(
coll: Option[Ident], coll: Option[Ident],
chunkSize: Int chunkSize: Int
): Stream[ConnectionIO, RAttachment] = { ): Stream[ConnectionIO, RAttachment] = {
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val aItem = Columns.itemId.prefix("a") // val aItem = Columns.itemId.prefix("a")
val aFile = Columns.fileId.prefix("a") // val aFile = Columns.fileId.prefix("a")
val sId = RAttachmentSource.Columns.id.prefix("s") // val sId = RAttachmentSource.Columns.id.prefix("s")
val sFile = RAttachmentSource.Columns.fileId.prefix("s") // val sFile = RAttachmentSource.Columns.fileId.prefix("s")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.Columns.id.prefix("m")
val mType = RFileMeta.Columns.mimetype.prefix("m") // val mType = RFileMeta.Columns.mimetype.prefix("m")
val pdfType = "application/pdf%" val pdfType = "application/pdf%"
//
// val from = table ++ fr"a INNER JOIN" ++
// RAttachmentSource.table ++ fr"s ON" ++ sId.is(aId) ++ fr"INNER JOIN" ++
// RItem.table ++ fr"i ON" ++ iId.is(aItem) ++ fr"INNER JOIN" ++
// RFileMeta.table ++ fr"m ON" ++ aFile.is(mId)
// val where = coll match {
// case Some(cid) => and(iColl.is(cid), aFile.is(sFile), mType.lowerLike(pdfType))
// case None => and(aFile.is(sFile), mType.lowerLike(pdfType))
// }
// selectSimple(all.map(_.prefix("a")), from, where)
// .query[RAttachment]
// .streamWithChunkSize(chunkSize)
val a = RAttachment.as("a")
val s = RAttachmentSource.as("s")
val i = RItem.as("i")
val m = RFileMeta.as("m")
val from = table ++ fr"a INNER JOIN" ++ Select(
RAttachmentSource.table ++ fr"s ON" ++ sId.is(aId) ++ fr"INNER JOIN" ++ select(a.all),
RItem.table ++ fr"i ON" ++ iId.is(aItem) ++ fr"INNER JOIN" ++ from(a)
RFileMeta.table ++ fr"m ON" ++ aFile.is(mId) .innerJoin(s, s.id === a.id)
val where = coll match { .innerJoin(i, i.id === a.itemId)
case Some(cid) => and(iColl.is(cid), aFile.is(sFile), mType.lowerLike(pdfType)) .innerJoin(m, m.id === a.fileId),
case None => and(aFile.is(sFile), mType.lowerLike(pdfType)) a.fileId === s.fileId &&
} m.mimetype.likes(pdfType) &&?
selectSimple(all.map(_.prefix("a")), from, where) coll.map(cid => i.cid === cid)
.query[RAttachment] ).build.query[RAttachment].streamWithChunkSize(chunkSize)
.streamWithChunkSize(chunkSize)
} }
} }

View File

@ -3,8 +3,9 @@ package docspell.store.records
import cats.data.NonEmptyList import cats.data.NonEmptyList
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb.TableDef
import docspell.store.qb._
import bitpeace.FileMeta import bitpeace.FileMeta
import doobie._ import doobie._
@ -22,10 +23,25 @@ case class RAttachmentArchive(
) )
object RAttachmentArchive { object RAttachmentArchive {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "attachment_archive"
val id = Column[Ident]("id", this)
val fileId = Column[Ident]("file_id", this)
val name = Column[String]("filename", this)
val messageId = Column[String]("message_id", this)
val created = Column[Timestamp]("created", this)
val all = NonEmptyList.of[Column[_]](id, fileId, name, messageId, created)
}
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
val table = fr"attachment_archive" val table = fr"attachment_archive"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("id") val id = Column("id")
val fileId = Column("file_id") val fileId = Column("file_id")
val name = Column("filename") val name = Column("filename")
@ -35,64 +51,83 @@ object RAttachmentArchive {
val all = List(id, fileId, name, messageId, created) val all = List(id, fileId, name, messageId, created)
} }
import Columns._
def of(ra: RAttachment, mId: Option[String]): RAttachmentArchive = def of(ra: RAttachment, mId: Option[String]): RAttachmentArchive =
RAttachmentArchive(ra.id, ra.fileId, ra.name, mId, ra.created) RAttachmentArchive(ra.id, ra.fileId, ra.name, mId, ra.created)
def insert(v: RAttachmentArchive): ConnectionIO[Int] = def insert(v: RAttachmentArchive): ConnectionIO[Int] =
insertRow( DML.insert(
table, T,
all, T.all,
fr"${v.id},${v.fileId},${v.name},${v.messageId},${v.created}" fr"${v.id},${v.fileId},${v.name},${v.messageId},${v.created}"
).update.run )
def findById(attachId: Ident): ConnectionIO[Option[RAttachmentArchive]] = def findById(attachId: Ident): ConnectionIO[Option[RAttachmentArchive]] =
selectSimple(all, table, id.is(attachId)).query[RAttachmentArchive].option run(select(T.all), from(T), T.id === attachId).query[RAttachmentArchive].option
def delete(attachId: Ident): ConnectionIO[Int] = def delete(attachId: Ident): ConnectionIO[Int] =
deleteFrom(table, id.is(attachId)).update.run DML.delete(T, T.id === attachId)
def deleteAll(fId: Ident): ConnectionIO[Int] = def deleteAll(fId: Ident): ConnectionIO[Int] =
deleteFrom(table, fileId.is(fId)).update.run DML.delete(T, T.fileId === fId)
def findByIdAndCollective( def findByIdAndCollective(
attachId: Ident, attachId: Ident,
collective: Ident collective: Ident
): ConnectionIO[Option[RAttachmentArchive]] = { ): ConnectionIO[Option[RAttachmentArchive]] = {
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
// val from = table ++ fr"a INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++
// fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId)
//
// val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective))
//
// selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentArchive].option
val b = RAttachment.as("b")
val a = RAttachmentArchive.as("a")
val i = RItem.as("i")
val from = table ++ fr"a INNER JOIN" ++ Select(
RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++ select(a.all),
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId) from(a)
.innerJoin(b, b.id === a.id)
val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective)) .innerJoin(i, i.id === b.itemId),
a.id === attachId && b.id === attachId && i.cid === collective
selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentArchive].option ).build.query[RAttachmentArchive].option
} }
def findByMessageIdAndCollective( def findByMessageIdAndCollective(
messageIds: NonEmptyList[String], messageIds: NonEmptyList[String],
collective: Ident collective: Ident
): ConnectionIO[Vector[RAttachmentArchive]] = { ): ConnectionIO[Vector[RAttachmentArchive]] = {
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val aMsgId = Columns.messageId.prefix("a") // val aMsgId = Columns.messageId.prefix("a")
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
val from = table ++ fr"a INNER JOIN" ++ // val from = table ++ fr"a INNER JOIN" ++
RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++ // RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId) // fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId)
//
val where = and(aMsgId.isIn(messageIds), iColl.is(collective)) // val where = and(aMsgId.isIn(messageIds), iColl.is(collective))
//
selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentArchive].to[Vector] // selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentArchive].to[Vector]
val b = RAttachment.as("b")
val a = RAttachmentArchive.as("a")
val i = RItem.as("i")
Select(
select(a.all),
from(a)
.innerJoin(b, b.id === a.id)
.innerJoin(i, i.id === b.itemId),
a.messageId.in(messageIds) && i.cid === collective
).build.query[RAttachmentArchive].to[Vector]
} }
def findByItemWithMeta( def findByItemWithMeta(
@ -100,31 +135,49 @@ object RAttachmentArchive {
): ConnectionIO[Vector[(RAttachmentArchive, FileMeta)]] = { ): ConnectionIO[Vector[(RAttachmentArchive, FileMeta)]] = {
import bitpeace.sql._ import bitpeace.sql._
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val afileMeta = fileId.prefix("a") // val afileMeta = fileId.prefix("a")
val bPos = RAttachment.Columns.position.prefix("b") // val bPos = RAttachment.Columns.position.prefix("b")
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.as("m").id.column
//
val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) // val cols = all.map(_.prefix("a")) ++ RFileMeta.as("m").all.map(_.column).toList
val from = table ++ fr"a INNER JOIN" ++ // val from = table ++ fr"a INNER JOIN" ++
RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++ // Fragment.const(RFileMeta.T.tableName) ++ fr"m ON" ++ afileMeta.is(
RAttachment.table ++ fr"b ON" ++ aId.is(bId) // mId
val where = bItem.is(id) // ) ++ fr"INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId)
(selectSimple(cols, from, where) ++ orderBy(bPos.asc)) // val where = bItem.is(id)
.query[(RAttachmentArchive, FileMeta)] //
.to[Vector] // (selectSimple(cols, from, where) ++ orderBy(bPos.asc))
// .query[(RAttachmentArchive, FileMeta)]
// .to[Vector]
val a = RAttachmentArchive.as("a")
val b = RAttachment.as("b")
val m = RFileMeta.as("m")
Select(
select(a.all, m.all),
from(a)
.innerJoin(m, a.fileId === m.id)
.innerJoin(b, a.id === b.id),
b.itemId === id
).orderBy(b.position.asc).build.query[(RAttachmentArchive, FileMeta)].to[Vector]
} }
/** If the given attachment id has an associated archive, this returns /** If the given attachment id has an associated archive, this returns
* the number of all associated attachments. Returns 0 if there is * the number of all associated attachments. Returns 0 if there is
* no archive for the given attachment. * no archive for the given attachment.
*/ */
def countEntries(attachId: Ident): ConnectionIO[Int] = { def countEntries(attachId: Ident): ConnectionIO[Int] =
val qFileId = selectSimple(Seq(fileId), table, id.is(attachId)) // val qFileId = selectSimple(Seq(fileId), table, id.is(attachId))
val q = selectCount(id, table, fileId.isSubquery(qFileId)) // val q = selectCount(id, table, fileId.isSubquery(qFileId))
q.query[Int].unique // q.query[Int].unique
} Select(
count(T.id).s,
from(T),
T.fileId.in(Select(T.fileId.s, from(T), T.id === attachId))
).build.query[Int].unique
//TODO this looks strange, can be simplified
} }

View File

@ -1,10 +1,11 @@
package docspell.store.records package docspell.store.records
import cats.data.NonEmptyList
import cats.implicits._ import cats.implicits._
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
@ -29,9 +30,25 @@ object RAttachmentMeta {
def empty(attachId: Ident) = def empty(attachId: Ident) =
RAttachmentMeta(attachId, None, Nil, MetaProposalList.empty, None) RAttachmentMeta(attachId, None, Nil, MetaProposalList.empty, None)
val table = fr"attachmentmeta" final case class Table(alias: Option[String]) extends TableDef {
val tableName = "attachmentmeta"
val id = Column[Ident]("attachid", this)
val content = Column[String]("content", this)
val nerlabels = Column[List[NerLabel]]("nerlabels", this)
val proposals = Column[MetaProposalList]("itemproposals", this)
val pages = Column[Int]("page_count", this)
val all = NonEmptyList.of[Column[_]](id, content, nerlabels, proposals, pages)
}
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
val table = fr"attachmentmeta"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("attachid") val id = Column("attachid")
val content = Column("content") val content = Column("content")
val nerlabels = Column("nerlabels") val nerlabels = Column("nerlabels")
@ -39,23 +56,22 @@ object RAttachmentMeta {
val pages = Column("page_count") val pages = Column("page_count")
val all = List(id, content, nerlabels, proposals, pages) val all = List(id, content, nerlabels, proposals, pages)
} }
import Columns._
def insert(v: RAttachmentMeta): ConnectionIO[Int] = def insert(v: RAttachmentMeta): ConnectionIO[Int] =
insertRow( DML.insert(
table, T,
all, T.all,
fr"${v.id},${v.content},${v.nerlabels},${v.proposals},${v.pages}" fr"${v.id},${v.content},${v.nerlabels},${v.proposals},${v.pages}"
).update.run )
def exists(attachId: Ident): ConnectionIO[Boolean] = def exists(attachId: Ident): ConnectionIO[Boolean] =
selectCount(id, table, id.is(attachId)).query[Int].unique.map(_ > 0) Select(count(T.id).s, from(T), T.id === attachId).build.query[Int].unique.map(_ > 0)
def findById(attachId: Ident): ConnectionIO[Option[RAttachmentMeta]] = def findById(attachId: Ident): ConnectionIO[Option[RAttachmentMeta]] =
selectSimple(all, table, id.is(attachId)).query[RAttachmentMeta].option run(select(T.all), from(T), T.id === attachId).query[RAttachmentMeta].option
def findPageCountById(attachId: Ident): ConnectionIO[Option[Int]] = def findPageCountById(attachId: Ident): ConnectionIO[Option[Int]] =
selectSimple(Seq(pages), table, id.is(attachId)) Select(T.pages.s, from(T), T.id === attachId).build
.query[Option[Int]] .query[Option[Int]]
.option .option
.map(_.flatten) .map(_.flatten)
@ -67,37 +83,37 @@ object RAttachmentMeta {
} yield n1 } yield n1
def update(v: RAttachmentMeta): ConnectionIO[Int] = def update(v: RAttachmentMeta): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
id.is(v.id), T.id === v.id,
commas( DML.set(
content.setTo(v.content), T.content.setTo(v.content),
nerlabels.setTo(v.nerlabels), T.nerlabels.setTo(v.nerlabels),
proposals.setTo(v.proposals) T.proposals.setTo(v.proposals)
) )
).update.run )
def updateLabels(mid: Ident, labels: List[NerLabel]): ConnectionIO[Int] = def updateLabels(mid: Ident, labels: List[NerLabel]): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
id.is(mid), T.id === mid,
commas( DML.set(
nerlabels.setTo(labels) T.nerlabels.setTo(labels)
) )
).update.run )
def updateProposals(mid: Ident, plist: MetaProposalList): ConnectionIO[Int] = def updateProposals(mid: Ident, plist: MetaProposalList): ConnectionIO[Int] =
updateRow( DML.update(
table, T,
id.is(mid), T.id === mid,
commas( DML.set(
proposals.setTo(plist) T.proposals.setTo(plist)
) )
).update.run )
def updatePageCount(mid: Ident, pageCount: Option[Int]): ConnectionIO[Int] = def updatePageCount(mid: Ident, pageCount: Option[Int]): ConnectionIO[Int] =
updateRow(table, id.is(mid), pages.setTo(pageCount)).update.run DML.update(T, T.id === mid, DML.set(T.pages.setTo(pageCount)))
def delete(attachId: Ident): ConnectionIO[Int] = def delete(attachId: Ident): ConnectionIO[Int] =
deleteFrom(table, id.is(attachId)).update.run DML.delete(T, T.id === attachId)
} }

View File

@ -1,8 +1,10 @@
package docspell.store.records package docspell.store.records
import cats.data.NonEmptyList
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import bitpeace.FileMeta import bitpeace.FileMeta
import doobie._ import doobie._
@ -19,10 +21,24 @@ case class RAttachmentPreview(
) )
object RAttachmentPreview { object RAttachmentPreview {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "attachment_preview"
val id = Column[Ident]("id", this)
val fileId = Column[Ident]("file_id", this)
val name = Column[String]("filename", this)
val created = Column[Timestamp]("created", this)
val all = NonEmptyList.of[Column[_]](id, fileId, name, created)
}
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
val table = fr"attachment_preview" val table = fr"attachment_preview"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("id") val id = Column("id")
val fileId = Column("file_id") val fileId = Column("file_id")
val name = Column("filename") val name = Column("filename")
@ -31,67 +47,98 @@ object RAttachmentPreview {
val all = List(id, fileId, name, created) val all = List(id, fileId, name, created)
} }
import Columns._
def insert(v: RAttachmentPreview): ConnectionIO[Int] = def insert(v: RAttachmentPreview): ConnectionIO[Int] =
insertRow(table, all, fr"${v.id},${v.fileId},${v.name},${v.created}").update.run DML.insert(T, T.all, fr"${v.id},${v.fileId},${v.name},${v.created}")
def findById(attachId: Ident): ConnectionIO[Option[RAttachmentPreview]] = def findById(attachId: Ident): ConnectionIO[Option[RAttachmentPreview]] =
selectSimple(all, table, id.is(attachId)).query[RAttachmentPreview].option run(select(T.all), from(T), T.id === attachId).query[RAttachmentPreview].option
def delete(attachId: Ident): ConnectionIO[Int] = def delete(attachId: Ident): ConnectionIO[Int] =
deleteFrom(table, id.is(attachId)).update.run DML.delete(T, T.id === attachId)
def findByIdAndCollective( def findByIdAndCollective(
attachId: Ident, attachId: Ident,
collective: Ident collective: Ident
): ConnectionIO[Option[RAttachmentPreview]] = { ): ConnectionIO[Option[RAttachmentPreview]] = {
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
// val from = table ++ fr"a INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++
// fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId)
//
// val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective))
//
// selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentPreview].option
val b = RAttachment.as("b")
val a = RAttachmentPreview.as("a")
val i = RItem.as("i")
val from = table ++ fr"a INNER JOIN" ++ Select(
RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++ select(a.all),
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId) from(a)
.innerJoin(b, a.id === b.id)
val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective)) .innerJoin(i, i.id === b.itemId),
a.id === attachId && b.id === attachId && i.cid === collective
selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentPreview].option ).build.query[RAttachmentPreview].option
} }
def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentPreview]] = { def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentPreview]] = {
val sId = Columns.id.prefix("s") // val sId = Columns.id.prefix("s")
val aId = RAttachment.Columns.id.prefix("a") // val aId = RAttachment.Columns.id.prefix("a")
val aItem = RAttachment.Columns.itemId.prefix("a") // val aItem = RAttachment.Columns.itemId.prefix("a")
//
val from = table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId) // val from = table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId)
selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId)) // selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId))
.query[RAttachmentPreview] // .query[RAttachmentPreview]
.to[Vector] // .to[Vector]
val s = RAttachmentPreview.as("s")
val a = RAttachment.as("a")
Select(
select(s.all),
from(s)
.innerJoin(a, s.id === a.id),
a.itemId === itemId
).build.query[RAttachmentPreview].to[Vector]
} }
def findByItemAndCollective( def findByItemAndCollective(
itemId: Ident, itemId: Ident,
coll: Ident coll: Ident
): ConnectionIO[Option[RAttachmentPreview]] = { ): ConnectionIO[Option[RAttachmentPreview]] = {
val sId = Columns.id.prefix("s") // val sId = Columns.id.prefix("s")
val aId = RAttachment.Columns.id.prefix("a") // val aId = RAttachment.Columns.id.prefix("a")
val aItem = RAttachment.Columns.itemId.prefix("a") // val aItem = RAttachment.Columns.itemId.prefix("a")
val aPos = RAttachment.Columns.position.prefix("a") // val aPos = RAttachment.Columns.position.prefix("a")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
// val from =
// table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId) ++
// fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem)
//
// selectSimple(
// all.map(_.prefix("s")) ++ List(aPos),
// from,
// and(aItem.is(itemId), iColl.is(coll))
// )
// .query[(RAttachmentPreview, Int)]
// .to[Vector]
// .map(_.sortBy(_._2).headOption.map(_._1))
val s = RAttachmentPreview.as("s")
val a = RAttachment.as("a")
val i = RItem.as("i")
val from = Select(
table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId) ++ select(s.all).append(a.position.s),
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ iId.is(aItem) from(s)
.innerJoin(a, s.id === a.id)
selectSimple( .innerJoin(i, i.id === a.itemId),
all.map(_.prefix("s")) ++ List(aPos), a.itemId === itemId && i.cid === coll
from, ).build
and(aItem.is(itemId), iColl.is(coll))
)
.query[(RAttachmentPreview, Int)] .query[(RAttachmentPreview, Int)]
.to[Vector] .to[Vector]
.map(_.sortBy(_._2).headOption.map(_._1)) .map(_.sortBy(_._2).headOption.map(_._1))
@ -102,22 +149,33 @@ object RAttachmentPreview {
): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = { ): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = {
import bitpeace.sql._ import bitpeace.sql._
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val afileMeta = fileId.prefix("a") // val afileMeta = fileId.prefix("a")
val bPos = RAttachment.Columns.position.prefix("b") // val bPos = RAttachment.Columns.position.prefix("b")
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.Columns.id.prefix("m")
//
// val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m"))
// val from = table ++ fr"a INNER JOIN" ++
// RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId)
// val where = bItem.is(id)
//
// (selectSimple(cols, from, where) ++ orderBy(bPos.asc))
// .query[(RAttachmentPreview, FileMeta)]
// .to[Vector]
val a = RAttachmentPreview.as("a")
val b = RAttachment.as("b")
val m = RFileMeta.as("m")
val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) Select(
val from = table ++ fr"a INNER JOIN" ++ select(a.all, m.all),
RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++ from(a)
RAttachment.table ++ fr"b ON" ++ aId.is(bId) .innerJoin(m, a.fileId === m.id)
val where = bItem.is(id) .innerJoin(b, b.id === a.id),
b.itemId === id
(selectSimple(cols, from, where) ++ orderBy(bPos.asc)) ).orderBy(b.position.asc).build.query[(RAttachmentPreview, FileMeta)].to[Vector]
.query[(RAttachmentPreview, FileMeta)]
.to[Vector]
} }
} }

View File

@ -1,8 +1,10 @@
package docspell.store.records package docspell.store.records
import cats.data.NonEmptyList
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import bitpeace.FileMeta import bitpeace.FileMeta
import doobie._ import doobie._
@ -19,10 +21,24 @@ case class RAttachmentSource(
) )
object RAttachmentSource { object RAttachmentSource {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "attachment_source"
val id = Column[Ident]("id", this)
val fileId = Column[Ident]("file_id", this)
val name = Column[String]("filename", this)
val created = Column[Timestamp]("created", this)
val all = NonEmptyList.of[Column[_]](id, fileId, name, created)
}
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
val table = fr"attachment_source" val table = fr"attachment_source"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("id") val id = Column("id")
val fileId = Column("file_id") val fileId = Column("file_id")
val name = Column("filename") val name = Column("filename")
@ -31,67 +47,90 @@ object RAttachmentSource {
val all = List(id, fileId, name, created) val all = List(id, fileId, name, created)
} }
import Columns._
def of(ra: RAttachment): RAttachmentSource = def of(ra: RAttachment): RAttachmentSource =
RAttachmentSource(ra.id, ra.fileId, ra.name, ra.created) RAttachmentSource(ra.id, ra.fileId, ra.name, ra.created)
def insert(v: RAttachmentSource): ConnectionIO[Int] = def insert(v: RAttachmentSource): ConnectionIO[Int] =
insertRow(table, all, fr"${v.id},${v.fileId},${v.name},${v.created}").update.run DML.insert(T, T.all, fr"${v.id},${v.fileId},${v.name},${v.created}")
def findById(attachId: Ident): ConnectionIO[Option[RAttachmentSource]] = def findById(attachId: Ident): ConnectionIO[Option[RAttachmentSource]] =
selectSimple(all, table, id.is(attachId)).query[RAttachmentSource].option run(select(T.all), from(T), T.id === attachId).query[RAttachmentSource].option
def isSameFile(attachId: Ident, file: Ident): ConnectionIO[Boolean] = def isSameFile(attachId: Ident, file: Ident): ConnectionIO[Boolean] =
selectCount(id, table, and(id.is(attachId), fileId.is(file))) Select(count(T.id).s, from(T), T.id === attachId && T.fileId === file).build
.query[Int] .query[Int]
.unique .unique
.map(_ > 0) .map(_ > 0)
def isConverted(attachId: Ident): ConnectionIO[Boolean] = { def isConverted(attachId: Ident): ConnectionIO[Boolean] = {
val sId = Columns.id.prefix("s") val s = RAttachmentSource.as("s")
val sFile = Columns.fileId.prefix("s") val a = RAttachment.as("a")
val aId = RAttachment.Columns.id.prefix("a") Select(
val aFile = RAttachment.Columns.fileId.prefix("a") count(a.id).s,
from(s).innerJoin(a, a.id === s.id),
a.id === attachId && a.fileId <> s.fileId
).build.query[Int].unique.map(_ > 0)
val from = table ++ fr"s INNER JOIN" ++ // val sId = Columns.id.prefix("s")
RAttachment.table ++ fr"a ON" ++ aId.is(sId) // val sFile = Columns.fileId.prefix("s")
// val aId = RAttachment.Columns.id.prefix("a")
selectCount(aId, from, and(aId.is(attachId), aFile.isNot(sFile))) // val aFile = RAttachment.Columns.fileId.prefix("a")
.query[Int] //
.unique // val from = table ++ fr"s INNER JOIN" ++
.map(_ > 0) // RAttachment.table ++ fr"a ON" ++ aId.is(sId)
//
// selectCount(aId, from, and(aId.is(attachId), aFile.isNot(sFile)))
// .query[Int]
// .unique
// .map(_ > 0)
} }
def delete(attachId: Ident): ConnectionIO[Int] = def delete(attachId: Ident): ConnectionIO[Int] =
deleteFrom(table, id.is(attachId)).update.run DML.delete(T, T.id === attachId)
def findByIdAndCollective( def findByIdAndCollective(
attachId: Ident, attachId: Ident,
collective: Ident collective: Ident
): ConnectionIO[Option[RAttachmentSource]] = { ): ConnectionIO[Option[RAttachmentSource]] = {
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val iId = RItem.Columns.id.prefix("i") // val iId = RItem.Columns.id.prefix("i")
val iColl = RItem.Columns.cid.prefix("i") // val iColl = RItem.Columns.cid.prefix("i")
//
// val from = table ++ fr"a INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++
// fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId)
//
// val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective))
//
// selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentSource].option
val b = RAttachment.as("b")
val a = RAttachmentSource.as("a")
val i = RItem.as("i")
val from = table ++ fr"a INNER JOIN" ++ Select(
RAttachment.table ++ fr"b ON" ++ aId.is(bId) ++ select(a.all),
fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ bItem.is(iId) from(a)
.innerJoin(b, a.id === b.id)
val where = and(aId.is(attachId), bId.is(attachId), iColl.is(collective)) .innerJoin(i, i.id === b.itemId),
a.id === attachId && b.id === attachId && i.cid === collective
selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentSource].option ).build.query[RAttachmentSource].option
} }
def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentSource]] = { def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentSource]] = {
val sId = Columns.id.prefix("s") // val sId = Columns.id.prefix("s")
val aId = RAttachment.Columns.id.prefix("a") // val aId = RAttachment.Columns.id.prefix("a")
val aItem = RAttachment.Columns.itemId.prefix("a") // val aItem = RAttachment.Columns.itemId.prefix("a")
//
// val from = table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId)
// selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId))
// .query[RAttachmentSource]
// .to[Vector]
val from = table ++ fr"s INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ sId.is(aId) val s = RAttachmentSource.as("s")
selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId)) val a = RAttachment.as("a")
Select(select(s.all), from(s).innerJoin(a, a.id === s.id), a.itemId === itemId).build
.query[RAttachmentSource] .query[RAttachmentSource]
.to[Vector] .to[Vector]
} }
@ -101,22 +140,33 @@ object RAttachmentSource {
): ConnectionIO[Vector[(RAttachmentSource, FileMeta)]] = { ): ConnectionIO[Vector[(RAttachmentSource, FileMeta)]] = {
import bitpeace.sql._ import bitpeace.sql._
val aId = Columns.id.prefix("a") // val aId = Columns.id.prefix("a")
val afileMeta = fileId.prefix("a") // val afileMeta = fileId.prefix("a")
val bPos = RAttachment.Columns.position.prefix("b") // val bPos = RAttachment.Columns.position.prefix("b")
val bId = RAttachment.Columns.id.prefix("b") // val bId = RAttachment.Columns.id.prefix("b")
val bItem = RAttachment.Columns.itemId.prefix("b") // val bItem = RAttachment.Columns.itemId.prefix("b")
val mId = RFileMeta.Columns.id.prefix("m") // val mId = RFileMeta.Columns.id.prefix("m")
//
// val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m"))
// val from = table ++ fr"a INNER JOIN" ++
// RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++
// RAttachment.table ++ fr"b ON" ++ aId.is(bId)
// val where = bItem.is(id)
//
// (selectSimple(cols, from, where) ++ orderBy(bPos.asc))
// .query[(RAttachmentSource, FileMeta)]
// .to[Vector]
val a = RAttachmentSource.as("a")
val b = RAttachment.as("b")
val m = RFileMeta.as("m")
val cols = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) Select(
val from = table ++ fr"a INNER JOIN" ++ select(a.all, m.all),
RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ fr"INNER JOIN" ++ from(a)
RAttachment.table ++ fr"b ON" ++ aId.is(bId) .innerJoin(m, a.fileId === m.id)
val where = bItem.is(id) .innerJoin(b, b.id === a.id),
b.itemId === id
(selectSimple(cols, from, where) ++ orderBy(bPos.asc)) ).orderBy(b.position.asc).build.query[(RAttachmentSource, FileMeta)].to[Vector]
.query[(RAttachmentSource, FileMeta)]
.to[Vector]
} }
} }

View File

@ -1,10 +1,11 @@
package docspell.store.records package docspell.store.records
import cats.data.NonEmptyList
import cats.implicits._ import cats.implicits._
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import com.github.eikek.calev._ import com.github.eikek.calev._
import doobie._ import doobie._
@ -21,71 +22,69 @@ case class RClassifierSetting(
) {} ) {}
object RClassifierSetting { object RClassifierSetting {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "classifier_setting"
val table = fr"classifier_setting" val cid = Column[Ident]("cid", this)
val enabled = Column[Boolean]("enabled", this)
object Columns { val schedule = Column[CalEvent]("schedule", this)
val cid = Column("cid") val category = Column[String]("category", this)
val enabled = Column("enabled") val itemCount = Column[Int]("item_count", this)
val schedule = Column("schedule") val fileId = Column[Ident]("file_id", this)
val category = Column("category") val created = Column[Timestamp]("created", this)
val itemCount = Column("item_count") val all = NonEmptyList
val fileId = Column("file_id") .of[Column[_]](cid, enabled, schedule, category, itemCount, fileId, created)
val created = Column("created")
val all = List(cid, enabled, schedule, category, itemCount, fileId, created)
}
import Columns._
def insert(v: RClassifierSetting): ConnectionIO[Int] = {
val sql =
insertRow(
table,
all,
fr"${v.cid},${v.enabled},${v.schedule},${v.category},${v.itemCount},${v.fileId},${v.created}"
)
sql.update.run
} }
def updateAll(v: RClassifierSetting): ConnectionIO[Int] = { val T = Table(None)
val sql = updateRow( def as(alias: String): Table =
table, Table(Some(alias))
cid.is(v.cid),
commas( def insert(v: RClassifierSetting): ConnectionIO[Int] =
enabled.setTo(v.enabled), DML.insert(
schedule.setTo(v.schedule), T,
category.setTo(v.category), T.all,
itemCount.setTo(v.itemCount), fr"${v.cid},${v.enabled},${v.schedule},${v.category},${v.itemCount},${v.fileId},${v.created}"
fileId.setTo(v.fileId) )
def updateAll(v: RClassifierSetting): ConnectionIO[Int] =
DML.update(
T,
T.cid === v.cid,
DML.set(
T.enabled.setTo(v.enabled),
T.schedule.setTo(v.schedule),
T.category.setTo(v.category),
T.itemCount.setTo(v.itemCount),
T.fileId.setTo(v.fileId)
) )
) )
sql.update.run
}
def updateFile(coll: Ident, fid: Ident): ConnectionIO[Int] = def updateFile(coll: Ident, fid: Ident): ConnectionIO[Int] =
updateRow(table, cid.is(coll), fileId.setTo(fid)).update.run DML.update(T, T.cid === coll, DML.set(T.fileId.setTo(fid)))
def updateSettings(v: RClassifierSetting): ConnectionIO[Int] = def updateSettings(v: RClassifierSetting): ConnectionIO[Int] =
for { for {
n1 <- updateRow( n1 <- DML.update(
table, T,
cid.is(v.cid), T.cid === v.cid,
commas( DML.set(
enabled.setTo(v.enabled), T.enabled.setTo(v.enabled),
schedule.setTo(v.schedule), T.schedule.setTo(v.schedule),
itemCount.setTo(v.itemCount), T.itemCount.setTo(v.itemCount),
category.setTo(v.category) T.category.setTo(v.category)
) )
).update.run )
n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO] n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO]
} yield n1 + n2 } yield n1 + n2
def findById(id: Ident): ConnectionIO[Option[RClassifierSetting]] = { def findById(id: Ident): ConnectionIO[Option[RClassifierSetting]] = {
val sql = selectSimple(all, table, cid.is(id)) val sql = run(select(T.all), from(T), T.cid === id)
sql.query[RClassifierSetting].option sql.query[RClassifierSetting].option
} }
def delete(coll: Ident): ConnectionIO[Int] = def delete(coll: Ident): ConnectionIO[Int] =
deleteFrom(table, cid.is(coll)).update.run DML.delete(T, T.cid === coll)
case class Classifier( case class Classifier(
enabled: Boolean, enabled: Boolean,

View File

@ -1,10 +1,11 @@
package docspell.store.records package docspell.store.records
import cats.data.NonEmptyList
import fs2.Stream import fs2.Stream
import docspell.common._ import docspell.common._
import docspell.store.impl.Column import docspell.store.qb.DSL._
import docspell.store.impl.Implicits._ import docspell.store.qb._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
@ -18,58 +19,54 @@ case class RCollective(
) )
object RCollective { object RCollective {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "collective"
val table = fr"collective" val id = Column[Ident]("cid", this)
val state = Column[CollectiveState]("state", this)
val language = Column[Language]("doclang", this)
val integration = Column[Boolean]("integration_enabled", this)
val created = Column[Timestamp]("created", this)
object Columns { val all = NonEmptyList.of[Column[_]](id, state, language, integration, created)
val id = Column("cid")
val state = Column("state")
val language = Column("doclang")
val integration = Column("integration_enabled")
val created = Column("created")
val all = List(id, state, language, integration, created)
} }
import Columns._ val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
def insert(value: RCollective): ConnectionIO[Int] = { def insert(value: RCollective): ConnectionIO[Int] =
val sql = insertRow( DML.insert(
table, T,
Columns.all, T.all,
fr"${value.id},${value.state},${value.language},${value.integrationEnabled},${value.created}" fr"${value.id},${value.state},${value.language},${value.integrationEnabled},${value.created}"
) )
sql.update.run
}
def update(value: RCollective): ConnectionIO[Int] = { def update(value: RCollective): ConnectionIO[Int] =
val sql = updateRow( DML.update(
table, T,
id.is(value.id), T.id === value.id,
commas( DML.set(
state.setTo(value.state) T.state.setTo(value.state)
) )
) )
sql.update.run
}
def findLanguage(cid: Ident): ConnectionIO[Option[Language]] = def findLanguage(cid: Ident): ConnectionIO[Option[Language]] =
selectSimple(List(language), table, id.is(cid)).query[Option[Language]].unique Select(T.language.s, from(T), T.id === cid).build.query[Option[Language]].unique
def updateLanguage(cid: Ident, lang: Language): ConnectionIO[Int] = def updateLanguage(cid: Ident, lang: Language): ConnectionIO[Int] =
updateRow(table, id.is(cid), language.setTo(lang)).update.run DML.update(T, T.id === cid, DML.set(T.language.setTo(lang)))
def updateSettings(cid: Ident, settings: Settings): ConnectionIO[Int] = def updateSettings(cid: Ident, settings: Settings): ConnectionIO[Int] =
for { for {
n1 <- updateRow( n1 <- DML.update(
table, T,
id.is(cid), T.id === cid,
commas( DML.set(
language.setTo(settings.language), T.language.setTo(settings.language),
integration.setTo(settings.integrationEnabled) T.integration.setTo(settings.integrationEnabled)
) )
).update.run )
cls <- cls <-
Timestamp Timestamp
.current[ConnectionIO] .current[ConnectionIO]
@ -83,66 +80,64 @@ object RCollective {
} yield n1 + n2 } yield n1 + n2
def getSettings(coll: Ident): ConnectionIO[Option[Settings]] = { def getSettings(coll: Ident): ConnectionIO[Option[Settings]] = {
val cId = id.prefix("c") val c = RCollective.as("c")
val CS = RClassifierSetting.Columns val cs = RClassifierSetting.as("cs")
val csCid = CS.cid.prefix("cs")
val cols = Seq( Select(
language.prefix("c"), select(
integration.prefix("c"), c.language.s,
CS.enabled.prefix("cs"), c.integration.s,
CS.schedule.prefix("cs"), cs.enabled.s,
CS.itemCount.prefix("cs"), cs.schedule.s,
CS.category.prefix("cs") cs.itemCount.s,
) cs.category.s
val from = table ++ fr"c LEFT JOIN" ++ ),
RClassifierSetting.table ++ fr"cs ON" ++ csCid.is(cId) from(c).leftJoin(cs, cs.cid === c.id),
c.id === coll
selectSimple(cols, from, cId.is(coll)) ).build.query[Settings].option
.query[Settings]
.option
} }
def findById(cid: Ident): ConnectionIO[Option[RCollective]] = { def findById(cid: Ident): ConnectionIO[Option[RCollective]] = {
val sql = selectSimple(all, table, id.is(cid)) val sql = run(select(T.all), from(T), T.id === cid)
sql.query[RCollective].option sql.query[RCollective].option
} }
def findByItem(itemId: Ident): ConnectionIO[Option[RCollective]] = { def findByItem(itemId: Ident): ConnectionIO[Option[RCollective]] = {
val iColl = RItem.Columns.cid.prefix("i") val i = RItem.as("i")
val iId = RItem.Columns.id.prefix("i") val c = RCollective.as("c")
val cId = id.prefix("c") Select(
val from = RItem.table ++ fr"i INNER JOIN" ++ table ++ fr"c ON" ++ iColl.is(cId) select(c.all),
selectSimple(all.map(_.prefix("c")), from, iId.is(itemId)).query[RCollective].option from(i).innerJoin(c, i.cid === c.id),
i.id === itemId
).build.query[RCollective].option
} }
def existsById(cid: Ident): ConnectionIO[Boolean] = { def existsById(cid: Ident): ConnectionIO[Boolean] = {
val sql = selectCount(id, table, id.is(cid)) val sql = Select(count(T.id).s, from(T), T.id === cid).build
sql.query[Int].unique.map(_ > 0) sql.query[Int].unique.map(_ > 0)
} }
def findAll(order: Columns.type => Column): ConnectionIO[Vector[RCollective]] = { def findAll(order: Table => Column[_]): ConnectionIO[Vector[RCollective]] = {
val sql = selectSimple(all, table, Fragment.empty) ++ orderBy(order(Columns).f) val sql = Select(select(T.all), from(T)).orderBy(order(T))
sql.query[RCollective].to[Vector] sql.build.query[RCollective].to[Vector]
} }
def streamAll(order: Columns.type => Column): Stream[ConnectionIO, RCollective] = { def streamAll(order: Table => Column[_]): Stream[ConnectionIO, RCollective] = {
val sql = selectSimple(all, table, Fragment.empty) ++ orderBy(order(Columns).f) val sql = Select(select(T.all), from(T)).orderBy(order(T))
sql.query[RCollective].stream sql.build.query[RCollective].stream
} }
def findByAttachment(attachId: Ident): ConnectionIO[Option[RCollective]] = { def findByAttachment(attachId: Ident): ConnectionIO[Option[RCollective]] = {
val iColl = RItem.Columns.cid.prefix("i") val i = RItem.as("i")
val iId = RItem.Columns.id.prefix("i") val a = RAttachment.as("a")
val aItem = RAttachment.Columns.itemId.prefix("a") val c = RCollective.as("c")
val aId = RAttachment.Columns.id.prefix("a") Select(
val cId = Columns.id.prefix("c") select(c.all),
from(c)
val from = table ++ fr"c INNER JOIN" ++ .innerJoin(i, c.id === i.cid)
RItem.table ++ fr"i ON" ++ cId.is(iColl) ++ fr"INNER JOIN" ++ .innerJoin(a, a.itemId === i.id),
RAttachment.table ++ fr"a ON" ++ aItem.is(iId) a.id === attachId
).build.query[RCollective].option
selectSimple(all.map(_.prefix("c")), from, aId.is(attachId)).query[RCollective].option
} }
case class Settings( case class Settings(

View File

@ -1,11 +1,13 @@
package docspell.store.records package docspell.store.records
import java.time.Instant
import cats.data.NonEmptyList import cats.data.NonEmptyList
import cats.implicits._ import cats.implicits._
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import docspell.store.syntax.MimeTypes._ import docspell.store.syntax.MimeTypes._
import bitpeace.FileMeta import bitpeace.FileMeta
@ -14,26 +16,30 @@ import doobie._
import doobie.implicits._ import doobie.implicits._
object RFileMeta { object RFileMeta {
final case class Table(alias: Option[String]) extends TableDef {
val tableName = "filemeta"
val table = fr"filemeta" val id = Column[Ident]("id", this)
val timestamp = Column[Instant]("timestamp", this)
val mimetype = Column[Mimetype]("mimetype", this)
val length = Column[Long]("length", this)
val checksum = Column[String]("checksum", this)
val chunks = Column[Int]("chunks", this)
val chunksize = Column[Int]("chunksize", this)
object Columns { val all = NonEmptyList
val id = Column("id") .of[Column[_]](id, timestamp, mimetype, length, checksum, chunks, chunksize)
val timestamp = Column("timestamp")
val mimetype = Column("mimetype")
val length = Column("length")
val checksum = Column("checksum")
val chunks = Column("chunks")
val chunksize = Column("chunksize")
val all = List(id, timestamp, mimetype, length, checksum, chunks, chunksize)
} }
val T = Table(None)
def as(alias: String): Table =
Table(Some(alias))
def findById(fid: Ident): ConnectionIO[Option[FileMeta]] = { def findById(fid: Ident): ConnectionIO[Option[FileMeta]] = {
import bitpeace.sql._ import bitpeace.sql._
selectSimple(Columns.all, table, Columns.id.is(fid)).query[FileMeta].option run(select(T.all), from(T), T.id === fid).query[FileMeta].option
} }
def findByIds(ids: List[Ident]): ConnectionIO[Vector[FileMeta]] = { def findByIds(ids: List[Ident]): ConnectionIO[Vector[FileMeta]] = {
@ -41,7 +47,7 @@ object RFileMeta {
NonEmptyList.fromList(ids) match { NonEmptyList.fromList(ids) match {
case Some(nel) => case Some(nel) =>
selectSimple(Columns.all, table, Columns.id.isIn(nel)).query[FileMeta].to[Vector] run(select(T.all), from(T), T.id.in(nel)).query[FileMeta].to[Vector]
case None => case None =>
Vector.empty[FileMeta].pure[ConnectionIO] Vector.empty[FileMeta].pure[ConnectionIO]
} }
@ -50,7 +56,7 @@ object RFileMeta {
def findMime(fid: Ident): ConnectionIO[Option[MimeType]] = { def findMime(fid: Ident): ConnectionIO[Option[MimeType]] = {
import bitpeace.sql._ import bitpeace.sql._
selectSimple(Seq(Columns.mimetype), table, Columns.id.is(fid)) run(select(T.mimetype), from(T), T.id === fid)
.query[Mimetype] .query[Mimetype]
.option .option
.map(_.map(_.toLocal)) .map(_.map(_.toLocal))

View File

@ -5,9 +5,8 @@ import cats.effect.Sync
import cats.implicits._ import cats.implicits._
import docspell.common._ import docspell.common._
import docspell.store.impl.Implicits._ import docspell.store.qb.DSL._
import docspell.store.impl._ import docspell.store.qb._
import docspell.store.qb.{Select, TableDef}
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
@ -110,8 +109,9 @@ object RItem {
Table(Some(alias)) Table(Some(alias))
val table = fr"item" val table = fr"item"
object Columns { object Columns {
import docspell.store.impl._
val id = Column("itemid") val id = Column("itemid")
val cid = Column("cid") val cid = Column("cid")
val name = Column("name") val name = Column("name")
@ -149,19 +149,21 @@ object RItem {
folder folder
) )
} }
import Columns._
private val currentTime =
Timestamp.current[ConnectionIO]
def insert(v: RItem): ConnectionIO[Int] = def insert(v: RItem): ConnectionIO[Int] =
insertRow( DML.insert(
table, T,
all, T.all,
fr"${v.id},${v.cid},${v.name},${v.itemDate},${v.source},${v.direction},${v.state}," ++ fr"${v.id},${v.cid},${v.name},${v.itemDate},${v.source},${v.direction},${v.state}," ++
fr"${v.corrOrg},${v.corrPerson},${v.concPerson},${v.concEquipment},${v.inReplyTo},${v.dueDate}," ++ fr"${v.corrOrg},${v.corrPerson},${v.concPerson},${v.concEquipment},${v.inReplyTo},${v.dueDate}," ++
fr"${v.created},${v.updated},${v.notes},${v.folderId}" fr"${v.created},${v.updated},${v.notes},${v.folderId}"
).update.run )
def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] = def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] =
selectSimple(List(cid), table, id.is(itemId)).query[Ident].option Select(T.cid.s, from(T), T.id === itemId).build.query[Ident].option
def updateState( def updateState(
itemId: Ident, itemId: Ident,
@ -170,11 +172,11 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.is(itemId), state.isIn(existing)), T.id === itemId && T.state.in(existing),
commas(state.setTo(itemState), updated.setTo(t)) DML.set(T.state.setTo(itemState), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateStateForCollective( def updateStateForCollective(
@ -184,11 +186,11 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(state.setTo(itemState), updated.setTo(t)) DML.set(T.state.setTo(itemState), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateDirection( def updateDirection(
@ -198,11 +200,11 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(incoming.setTo(dir), updated.setTo(t)) DML.set(T.incoming.setTo(dir), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateCorrOrg( def updateCorrOrg(
@ -212,21 +214,21 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(corrOrg.setTo(org), updated.setTo(t)) DML.set(T.corrOrg.setTo(org), T.updated.setTo(t))
).update.run )
} yield n } yield n
def removeCorrOrg(coll: Ident, currentOrg: Ident): ConnectionIO[Int] = def removeCorrOrg(coll: Ident, currentOrg: Ident): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(cid.is(coll), corrOrg.is(Some(currentOrg))), T.cid === coll && T.corrOrg === currentOrg,
commas(corrOrg.setTo(None: Option[Ident]), updated.setTo(t)) DML.set(T.corrOrg.setTo(None: Option[Ident]), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateCorrPerson( def updateCorrPerson(
@ -236,21 +238,21 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(corrPerson.setTo(person), updated.setTo(t)) DML.set(T.corrPerson.setTo(person), T.updated.setTo(t))
).update.run )
} yield n } yield n
def removeCorrPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] = def removeCorrPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(cid.is(coll), corrPerson.is(Some(currentPerson))), T.cid === coll && T.corrPerson === currentPerson,
commas(corrPerson.setTo(None: Option[Ident]), updated.setTo(t)) DML.set(T.corrPerson.setTo(None: Option[Ident]), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateConcPerson( def updateConcPerson(
@ -260,21 +262,21 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(concPerson.setTo(person), updated.setTo(t)) DML.set(T.concPerson.setTo(person), T.updated.setTo(t))
).update.run )
} yield n } yield n
def removeConcPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] = def removeConcPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(cid.is(coll), concPerson.is(Some(currentPerson))), T.cid === coll && T.concPerson === currentPerson,
commas(concPerson.setTo(None: Option[Ident]), updated.setTo(t)) DML.set(T.concPerson.setTo(None: Option[Ident]), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateConcEquip( def updateConcEquip(
@ -284,21 +286,21 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(concEquipment.setTo(equip), updated.setTo(t)) DML.set(T.concEquipment.setTo(equip), T.updated.setTo(t))
).update.run )
} yield n } yield n
def removeConcEquip(coll: Ident, currentEquip: Ident): ConnectionIO[Int] = def removeConcEquip(coll: Ident, currentEquip: Ident): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(cid.is(coll), concEquipment.is(Some(currentEquip))), T.cid === coll && T.concEquipment === currentEquip,
commas(concEquipment.setTo(None: Option[Ident]), updated.setTo(t)) DML.set(T.concEquipment.setTo(None: Option[Ident]), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateFolder( def updateFolder(
@ -308,31 +310,31 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(cid.is(coll), id.is(itemId)), T.cid === coll && T.id === itemId,
commas(folder.setTo(folderId), updated.setTo(t)) DML.set(T.folder.setTo(folderId), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateNotes(itemId: Ident, coll: Ident, text: Option[String]): ConnectionIO[Int] = def updateNotes(itemId: Ident, coll: Ident, text: Option[String]): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.is(itemId), cid.is(coll)), T.id === itemId && T.cid === coll,
commas(notes.setTo(text), updated.setTo(t)) DML.set(T.notes.setTo(text), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateName(itemId: Ident, coll: Ident, itemName: String): ConnectionIO[Int] = def updateName(itemId: Ident, coll: Ident, itemName: String): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.is(itemId), cid.is(coll)), T.id === itemId && T.cid === coll,
commas(name.setTo(itemName), updated.setTo(t)) DML.set(T.name.setTo(itemName), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateDate( def updateDate(
@ -342,11 +344,11 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(itemDate.setTo(date), updated.setTo(t)) DML.set(T.itemDate.setTo(date), T.updated.setTo(t))
).update.run )
} yield n } yield n
def updateDueDate( def updateDueDate(
@ -356,50 +358,50 @@ object RItem {
): ConnectionIO[Int] = ): ConnectionIO[Int] =
for { for {
t <- currentTime t <- currentTime
n <- updateRow( n <- DML.update(
table, T,
and(id.isIn(itemIds), cid.is(coll)), T.id.in(itemIds) && T.cid === coll,
commas(dueDate.setTo(date), updated.setTo(t)) DML.set(T.dueDate.setTo(date), T.updated.setTo(t))
).update.run )
} yield n } yield n
def deleteByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Int] = def deleteByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Int] =
deleteFrom(table, and(id.is(itemId), cid.is(coll))).update.run DML.delete(T, T.id === itemId && T.cid === coll)
def existsById(itemId: Ident): ConnectionIO[Boolean] = def existsById(itemId: Ident): ConnectionIO[Boolean] =
selectCount(id, table, id.is(itemId)).query[Int].unique.map(_ > 0) Select(count(T.id).s, from(T), T.id === itemId).build.query[Int].unique.map(_ > 0)
def existsByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Boolean] = def existsByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Boolean] =
selectCount(id, table, and(id.is(itemId), cid.is(coll))).query[Int].unique.map(_ > 0) Select(count(T.id).s, from(T), T.id === itemId && T.cid === coll).build
.query[Int]
.unique
.map(_ > 0)
def existsByIdsAndCollective( def existsByIdsAndCollective(
itemIds: NonEmptyList[Ident], itemIds: NonEmptyList[Ident],
coll: Ident coll: Ident
): ConnectionIO[Boolean] = ): ConnectionIO[Boolean] =
selectCount(id, table, and(id.isIn(itemIds), cid.is(coll))) Select(count(T.id).s, from(T), T.id.in(itemIds) && T.cid === coll).build
.query[Int] .query[Int]
.unique .unique
.map(_ == itemIds.size) .map(_ == itemIds.size)
def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] = def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] =
selectSimple(all, table, and(id.is(itemId), cid.is(coll))).query[RItem].option run(select(T.all), from(T), T.id === itemId && T.cid === coll).query[RItem].option
def findById(itemId: Ident): ConnectionIO[Option[RItem]] = def findById(itemId: Ident): ConnectionIO[Option[RItem]] =
selectSimple(all, table, id.is(itemId)).query[RItem].option run(select(T.all), from(T), T.id === itemId).query[RItem].option
def checkByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[Ident]] = def checkByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[Ident]] =
selectSimple(Seq(id), table, and(id.is(itemId), cid.is(coll))).query[Ident].option Select(T.id.s, from(T), T.id === itemId && T.cid === coll).build.query[Ident].option
def removeFolder(folderId: Ident): ConnectionIO[Int] = { def removeFolder(folderId: Ident): ConnectionIO[Int] = {
val empty: Option[Ident] = None val empty: Option[Ident] = None
updateRow(table, folder.is(folderId), folder.setTo(empty)).update.run DML.update(T, T.folder === folderId, DML.set(T.folder.setTo(empty)))
} }
def filterItemsFragment(items: NonEmptyList[Ident], coll: Ident): Select = { def filterItemsFragment(items: NonEmptyList[Ident], coll: Ident): Select =
import docspell.store.qb.DSL._
Select(select(T.id), from(T), T.cid === coll && T.id.in(items)) Select(select(T.id), from(T), T.cid === coll && T.id.in(items))
}
def filterItems(items: NonEmptyList[Ident], coll: Ident): ConnectionIO[Vector[Ident]] = def filterItems(items: NonEmptyList[Ident], coll: Ident): ConnectionIO[Vector[Ident]] =
filterItemsFragment(items, coll).build.query[Ident].to[Vector] filterItemsFragment(items, coll).build.query[Ident].to[Vector]