mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-10-30 21:40:12 +00:00 
			
		
		
		
	Convert more records
This commit is contained in:
		| @@ -74,6 +74,10 @@ object DML { | ||||
|       case Setter.Increment(column, amount) => | ||||
|         val colFrag = SelectExprBuilder.columnNoPrefix(column) | ||||
|         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[_]] = | ||||
|   | ||||
| @@ -127,6 +127,9 @@ trait DSL extends DoobieMeta { | ||||
|     def increment(amount: Int): Setter[A] = | ||||
|       Setter.Increment(col, amount) | ||||
|  | ||||
|     def decrement(amount: Int): Setter[A] = | ||||
|       Setter.Decrement(col, amount) | ||||
|  | ||||
|     def asc: OrderBy = | ||||
|       OrderBy(SelectExpr.SelectColumn(col, None), OrderBy.OrderType.Asc) | ||||
|  | ||||
| @@ -177,6 +180,9 @@ trait DSL extends DoobieMeta { | ||||
|  | ||||
|     def ===(other: Column[A]): Condition = | ||||
|       Condition.CompareCol(col, Operator.Eq, other) | ||||
|  | ||||
|     def <>(other: Column[A]): Condition = | ||||
|       Condition.CompareCol(col, Operator.Neq, other) | ||||
|   } | ||||
|  | ||||
|   implicit final class ConditionOps(c: Condition) { | ||||
|   | ||||
| @@ -13,5 +13,6 @@ object Setter { | ||||
|       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] | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -180,14 +180,17 @@ object QAttachment { | ||||
|     val iId      = RItem.Columns.id.prefix("i") | ||||
|     val iColl    = RItem.Columns.cid.prefix("i") | ||||
|     val iFolder  = RItem.Columns.folder.prefix("i") | ||||
|     val cId      = RCollective.Columns.id.prefix("c") | ||||
|     val cLang    = RCollective.Columns.language.prefix("c") | ||||
|     val c        = RCollective.as("c") | ||||
|     val cId      = c.id.column | ||||
|     val cLang    = c.language.column | ||||
|  | ||||
|     val cols = Seq(aId, aItem, iColl, iFolder, cLang, aName, mContent) | ||||
|     val from = RAttachment.table ++ fr"a INNER JOIN" ++ | ||||
|       RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) ++ | ||||
|       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) | ||||
|  | ||||
|   | ||||
| @@ -633,27 +633,31 @@ object QItem { | ||||
|       limit: Option[Int], | ||||
|       states: Set[ItemState] | ||||
|   ): Fragment = { | ||||
|     val IC      = RItem.Columns.all.map(_.prefix("i")) | ||||
|     val aItem   = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aId     = RAttachment.Columns.id.prefix("a") | ||||
|     val aFileId = RAttachment.Columns.fileId.prefix("a") | ||||
|     val iId     = RItem.Columns.id.prefix("i") | ||||
|     val iState  = RItem.Columns.state.prefix("i") | ||||
|     val sId     = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFileId = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val rId     = RAttachmentArchive.Columns.id.prefix("r") | ||||
|     val rFileId = RAttachmentArchive.Columns.fileId.prefix("r") | ||||
|     val m1Id    = RFileMeta.Columns.id.prefix("m1") | ||||
|     val m2Id    = RFileMeta.Columns.id.prefix("m2") | ||||
|     val m3Id    = RFileMeta.Columns.id.prefix("m3") | ||||
|     val IC            = RItem.Columns.all.map(_.prefix("i")) | ||||
|     val aItem         = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aId           = RAttachment.Columns.id.prefix("a") | ||||
|     val aFileId       = RAttachment.Columns.fileId.prefix("a") | ||||
|     val iId           = RItem.Columns.id.prefix("i") | ||||
|     val iState        = RItem.Columns.state.prefix("i") | ||||
|     val sId           = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFileId       = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val rId           = RAttachmentArchive.Columns.id.prefix("r") | ||||
|     val rFileId       = RAttachmentArchive.Columns.fileId.prefix("r") | ||||
|     val m1            = RFileMeta.as("m1") | ||||
|     val m2            = RFileMeta.as("m2") | ||||
|     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 = | ||||
|       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" ++ RFileMeta.table ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ | ||||
|         fr"INNER JOIN" ++ RFileMeta.table ++ fr"m2 ON" ++ m2Id.is(sFileId) ++ | ||||
|         fr"INNER JOIN" ++ filemetaTable ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ | ||||
|         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" ++ RFileMeta.table ++ fr"m3 ON" ++ m3Id.is(rFileId) | ||||
|         fr"LEFT OUTER JOIN" ++ filemetaTable ++ fr"m3 ON" ++ m3Id.is(rFileId) | ||||
|  | ||||
|     val fileCond = | ||||
|       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]] = { | ||||
|     val IC         = RItem.Columns.all.map(_.prefix("i")) | ||||
|     val aItem      = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aId        = RAttachment.Columns.id.prefix("a") | ||||
|     val aFileId    = RAttachment.Columns.fileId.prefix("a") | ||||
|     val iId        = RItem.Columns.id.prefix("i") | ||||
|     val iColl      = RItem.Columns.cid.prefix("i") | ||||
|     val sId        = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFileId    = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val rId        = RAttachmentArchive.Columns.id.prefix("r") | ||||
|     val rFileId    = RAttachmentArchive.Columns.fileId.prefix("r") | ||||
|     val m1Id       = RFileMeta.Columns.id.prefix("m1") | ||||
|     val m2Id       = RFileMeta.Columns.id.prefix("m2") | ||||
|     val m3Id       = RFileMeta.Columns.id.prefix("m3") | ||||
|     val m1Checksum = RFileMeta.Columns.checksum.prefix("m1") | ||||
|     val m2Checksum = RFileMeta.Columns.checksum.prefix("m2") | ||||
|     val m3Checksum = RFileMeta.Columns.checksum.prefix("m3") | ||||
|  | ||||
|     val IC            = RItem.Columns.all.map(_.prefix("i")) | ||||
|     val aItem         = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aId           = RAttachment.Columns.id.prefix("a") | ||||
|     val aFileId       = RAttachment.Columns.fileId.prefix("a") | ||||
|     val iId           = RItem.Columns.id.prefix("i") | ||||
|     val iColl         = RItem.Columns.cid.prefix("i") | ||||
|     val sId           = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFileId       = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val rId           = RAttachmentArchive.Columns.id.prefix("r") | ||||
|     val rFileId       = RAttachmentArchive.Columns.fileId.prefix("r") | ||||
|     val m1            = RFileMeta.as("m1") | ||||
|     val m2            = RFileMeta.as("m2") | ||||
|     val m3            = RFileMeta.as("m3") | ||||
|     val m1Id          = m1.id.column | ||||
|     val m2Id          = m2.id.column | ||||
|     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 = | ||||
|       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" ++ RFileMeta.table ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ | ||||
|         fr"INNER JOIN" ++ RFileMeta.table ++ fr"m2 ON" ++ m2Id.is(sFileId) ++ | ||||
|         fr"INNER JOIN" ++ filemetaTable ++ fr"m1 ON" ++ m1Id.is(aFileId) ++ | ||||
|         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" ++ RFileMeta.table ++ fr"m3 ON" ++ m3Id.is(rFileId) | ||||
|         fr"LEFT OUTER JOIN" ++ filemetaTable ++ fr"m3 ON" ++ m3Id.is(rFileId) | ||||
|  | ||||
|     selectSimple( | ||||
|       IC, | ||||
|   | ||||
| @@ -3,8 +3,8 @@ package docspell.store.queries | ||||
| import cats.data.OptionT | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.records.RCollective.{Columns => CC} | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
| import docspell.store.records.{RCollective, RRememberMe, RUser} | ||||
|  | ||||
| import doobie._ | ||||
| @@ -22,20 +22,14 @@ object QLogin { | ||||
|   ) | ||||
|  | ||||
|   def findUser(acc: AccountId): ConnectionIO[Option[Data]] = { | ||||
|     val user   = RUser.as("u") | ||||
|     val ucid   = user.cid.column | ||||
|     val login  = user.login.column | ||||
|     val pass   = user.password.column | ||||
|     val ustate = user.state.column | ||||
|     val cstate = CC.state.prefix("c") | ||||
|     val ccid   = CC.id.prefix("c") | ||||
|  | ||||
|     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)) | ||||
|     ) | ||||
|  | ||||
|     val user = RUser.as("u") | ||||
|     val coll = RCollective.as("c") | ||||
|     val sql = | ||||
|       Select( | ||||
|         select(user.cid, user.login, user.password, coll.state, user.state), | ||||
|         from(user).innerJoin(coll, user.cid === coll.id), | ||||
|         user.login === acc.user && user.cid === acc.collective | ||||
|       ).build | ||||
|     logger.trace(s"SQL : $sql") | ||||
|     sql.query[Data].option | ||||
|   } | ||||
|   | ||||
| @@ -5,8 +5,9 @@ import cats.implicits._ | ||||
| import fs2.Stream | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb.TableDef | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import bitpeace.FileMeta | ||||
| import doobie._ | ||||
| @@ -22,10 +23,27 @@ case class 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" | ||||
|  | ||||
|   object Columns { | ||||
|     import docspell.store.impl._ | ||||
|  | ||||
|     val id       = Column("attachid") | ||||
|     val itemId   = Column("itemid") | ||||
|     val fileId   = Column("filemetaid") | ||||
| @@ -34,32 +52,37 @@ object RAttachment { | ||||
|     val name     = Column("name") | ||||
|     val all      = List(id, itemId, fileId, position, created, name) | ||||
|   } | ||||
|   import Columns._ | ||||
|  | ||||
|   def insert(v: RAttachment): ConnectionIO[Int] = | ||||
|     insertRow( | ||||
|       table, | ||||
|       all, | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       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] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)), | ||||
|       position.decrement(1) | ||||
|     ).update.run | ||||
|     DML.update( | ||||
|       T, | ||||
|       where( | ||||
|         T.itemId === iId && T.position >= lowerBound && T.position <= upperBound | ||||
|       ), | ||||
|       DML.set(T.position.decrement(1)) | ||||
|     ) | ||||
|  | ||||
|   def incPositions(iId: Ident, lowerBound: Int, upperBound: Int): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       and(itemId.is(iId), position.isGte(lowerBound), position.isLte(upperBound)), | ||||
|       position.increment(1) | ||||
|     ).update.run | ||||
|     DML.update( | ||||
|       T, | ||||
|       where( | ||||
|         T.itemId === iId && T.position >= lowerBound && T.position <= upperBound | ||||
|       ), | ||||
|       DML.set(T.position.increment(1)) | ||||
|     ) | ||||
|  | ||||
|   def nextPosition(id: Ident): ConnectionIO[Int] = | ||||
|     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) | ||||
|  | ||||
|   def updateFileIdAndName( | ||||
| @@ -67,41 +90,49 @@ object RAttachment { | ||||
|       fId: Ident, | ||||
|       fname: Option[String] | ||||
|   ): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       id.is(attachId), | ||||
|       commas(fileId.setTo(fId), name.setTo(fname)) | ||||
|     ).update.run | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === attachId, | ||||
|       DML.set(T.fileId.setTo(fId), T.name.setTo(fname)) | ||||
|     ) | ||||
|  | ||||
|   def updateFileId( | ||||
|       attachId: Ident, | ||||
|       fId: Ident | ||||
|   ): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       id.is(attachId), | ||||
|       fileId.setTo(fId) | ||||
|     ).update.run | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === attachId, | ||||
|       DML.set(T.fileId.setTo(fId)) | ||||
|     ) | ||||
|  | ||||
|   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]] = | ||||
|     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]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     val cols      = RFileMeta.Columns.all.map(_.prefix("m")) | ||||
|     val aId       = id.prefix("a") | ||||
|     val aFileMeta = fileId.prefix("a") | ||||
|     val mId       = RFileMeta.Columns.id.prefix("m") | ||||
|  | ||||
|     val from = | ||||
|       table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ aFileMeta.is(mId) | ||||
|     val cond = aId.is(attachId) | ||||
|  | ||||
|     selectSimple(cols, from, cond).query[FileMeta].option | ||||
| //    val cols      = RFileMeta.Columns.all.map(_.prefix("m")) | ||||
| //    val aId       = id.prefix("a") | ||||
| //    val aFileMeta = fileId.prefix("a") | ||||
| //    val mId       = RFileMeta.Columns.id.prefix("m") | ||||
| // | ||||
| //    val from = | ||||
| //      table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ aFileMeta.is(mId) | ||||
| //    val cond = aId.is(attachId) | ||||
| // | ||||
| //    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( | ||||
| @@ -109,7 +140,7 @@ object RAttachment { | ||||
|       collective: Ident, | ||||
|       aname: Option[String] | ||||
|   ): 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 { | ||||
|       exists <- existsByIdAndCollective(attachId, collective) | ||||
|       n      <- if (exists) update else 0.pure[ConnectionIO] | ||||
| @@ -119,44 +150,59 @@ object RAttachment { | ||||
|   def findByIdAndCollective( | ||||
|       attachId: Ident, | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Option[RAttachment]] = | ||||
|     selectSimple( | ||||
|       all.map(_.prefix("a")), | ||||
|       table ++ fr"a," ++ RItem.table ++ fr"i", | ||||
|       and( | ||||
|         fr"a.itemid = i.itemid", | ||||
|         id.prefix("a").is(attachId), | ||||
|         RItem.Columns.cid.prefix("i").is(collective) | ||||
|       ) | ||||
|     ).query[RAttachment].option | ||||
|   ): ConnectionIO[Option[RAttachment]] = { | ||||
|     val a = RAttachment.as("a") | ||||
|     val i = RItem.as("i") | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a).innerJoin(i, a.itemId === i.id), | ||||
|       a.id === attachId && i.cid === collective | ||||
|     ).build.query[RAttachment].option | ||||
|   } | ||||
|  | ||||
|   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( | ||||
|       attachId: Ident, | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Boolean] = { | ||||
|     val aId   = id.prefix("a") | ||||
|     val aItem = itemId.prefix("a") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
|     val from = | ||||
|       table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) | ||||
|     val cond = and(iColl.is(collective), aId.is(attachId)) | ||||
|     selectCount(id, from, cond).query[Int].unique.map(_ > 0) | ||||
| //    val aId   = id.prefix("a") | ||||
| //    val aItem = itemId.prefix("a") | ||||
| //    val iId   = RItem.Columns.id.prefix("i") | ||||
| //    val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val from = | ||||
| //      table ++ fr"a INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) | ||||
| //    val cond = and(iColl.is(collective), aId.is(attachId)) | ||||
| //    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( | ||||
|       id: Ident, | ||||
|       coll: Ident | ||||
|   ): ConnectionIO[Vector[RAttachment]] = { | ||||
|     val q = selectSimple(all.map(_.prefix("a")), table ++ fr"a", Fragment.empty) ++ | ||||
|       fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ RItem.Columns.id | ||||
|         .prefix("i") | ||||
|         .is(itemId.prefix("a")) ++ | ||||
|       fr"WHERE" ++ and(itemId.prefix("a").is(id), RItem.Columns.cid.prefix("i").is(coll)) | ||||
|     q.query[RAttachment].to[Vector] | ||||
| //    val q = selectSimple(all.map(_.prefix("a")), table ++ fr"a", Fragment.empty) ++ | ||||
| //      fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ RItem.Columns.id | ||||
| //        .prefix("i") | ||||
| //        .is(itemId.prefix("a")) ++ | ||||
| //      fr"WHERE" ++ and(itemId.prefix("a").is(id), RItem.Columns.cid.prefix("i").is(coll)) | ||||
| //    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( | ||||
| @@ -165,28 +211,42 @@ object RAttachment { | ||||
|       fileIds: NonEmptyList[Ident] | ||||
|   ): ConnectionIO[Vector[RAttachment]] = { | ||||
|  | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
|     val aItem = Columns.itemId.prefix("a") | ||||
|     val aId   = Columns.id.prefix("a") | ||||
|     val aFile = Columns.fileId.prefix("a") | ||||
|     val sId   = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFile = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val rId   = RAttachmentArchive.Columns.id.prefix("r") | ||||
|     val rFile = RAttachmentArchive.Columns.fileId.prefix("r") | ||||
| //    val iId   = RItem.Columns.id.prefix("i") | ||||
| //    val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val aItem = Columns.itemId.prefix("a") | ||||
| //    val aId   = Columns.id.prefix("a") | ||||
| //    val aFile = Columns.fileId.prefix("a") | ||||
| //    val sId   = RAttachmentSource.Columns.id.prefix("s") | ||||
| //    val sFile = RAttachmentSource.Columns.fileId.prefix("s") | ||||
| //    val rId   = RAttachmentArchive.Columns.id.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" ++ | ||||
|       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] | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a) | ||||
|         .innerJoin(i, i.id === a.itemId) | ||||
|         .leftJoin(s, s.id === a.id) | ||||
|         .leftJoin(r, r.id === a.id), | ||||
|       i.id === id && i.cid === coll && | ||||
|         (a.fileId.in(fileIds) || s.fileId.in(fileIds) || r.fileId.in(fileIds)) | ||||
|     ).build.query[RAttachment].to[Vector] | ||||
|   } | ||||
|  | ||||
|   def findByItemAndCollectiveWithMeta( | ||||
| @@ -195,27 +255,45 @@ object RAttachment { | ||||
|   ): ConnectionIO[Vector[(RAttachment, FileMeta)]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     val cols      = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) | ||||
|     val afileMeta = fileId.prefix("a") | ||||
|     val aItem     = itemId.prefix("a") | ||||
|     val mId       = RFileMeta.Columns.id.prefix("m") | ||||
|     val iId       = RItem.Columns.id.prefix("i") | ||||
|     val iColl     = RItem.Columns.cid.prefix("i") | ||||
|  | ||||
|     val from = | ||||
|       table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ | ||||
|         fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) | ||||
|     val cond = Seq(aItem.is(id), iColl.is(coll)) | ||||
|  | ||||
|     selectSimple(cols, from, and(cond)).query[(RAttachment, FileMeta)].to[Vector] | ||||
| //    val cols      = all.map(_.prefix("a")) ++ RFileMeta.Columns.all.map(_.prefix("m")) | ||||
| //    val afileMeta = fileId.prefix("a") | ||||
| //    val aItem     = itemId.prefix("a") | ||||
| //    val mId       = RFileMeta.Columns.id.prefix("m") | ||||
| //    val iId       = RItem.Columns.id.prefix("i") | ||||
| //    val iColl     = RItem.Columns.cid.prefix("i") | ||||
| // | ||||
| //    val from = | ||||
| //      table ++ fr"a INNER JOIN" ++ RFileMeta.table ++ fr"m ON" ++ afileMeta.is(mId) ++ | ||||
| //        fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ aItem.is(iId) | ||||
| //    val cond = Seq(aItem.is(id), iColl.is(coll)) | ||||
| // | ||||
| //    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)]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     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" | ||||
|     q.query[(RAttachment, FileMeta)].to[Vector] | ||||
| //    val q = | ||||
| //      fr"SELECT a.*,m.* FROM" ++ table ++ fr"a, filzemeta m | ||||
| //      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. | ||||
| @@ -225,110 +303,159 @@ object RAttachment { | ||||
|       n0 <- RAttachmentMeta.delete(attachId) | ||||
|       n1 <- RAttachmentSource.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 | ||||
|  | ||||
|   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( | ||||
|       coll: Option[Ident], | ||||
|       chunkSize: Int | ||||
|   ): Stream[ConnectionIO, RAttachment] = { | ||||
|     val aItem = Columns.itemId.prefix("a") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
|  | ||||
|     val cols = all.map(_.prefix("a")) | ||||
| //    val aItem = Columns.itemId.prefix("a") | ||||
| //    val iId   = RItem.Columns.id.prefix("i") | ||||
| //    val iColl = RItem.Columns.cid.prefix("i") | ||||
| // | ||||
| //    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 { | ||||
|       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) | ||||
|         Select( | ||||
|           select(a.all), | ||||
|           from(a) | ||||
|             .innerJoin(i, i.id === a.itemId), | ||||
|           i.cid === cid | ||||
|         ).build.query[RAttachment].streamWithChunkSize(chunkSize) | ||||
|       case None => | ||||
|         selectSimple(cols, table, Fragment.empty) | ||||
|         Select(select(a.all), from(a)).build | ||||
|           .query[RAttachment] | ||||
|           .streamWithChunkSize(chunkSize) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   def findAllWithoutPageCount(chunkSize: Int): Stream[ConnectionIO, RAttachment] = { | ||||
|     val aId      = Columns.id.prefix("a") | ||||
|     val aCreated = Columns.created.prefix("a") | ||||
|     val mId      = RAttachmentMeta.Columns.id.prefix("m") | ||||
|     val mPages   = RAttachmentMeta.Columns.pages.prefix("m") | ||||
|  | ||||
|     val cols = all.map(_.prefix("a")) | ||||
|     val join = table ++ fr"a LEFT OUTER JOIN" ++ | ||||
|       RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) | ||||
|     val cond = mPages.isNull | ||||
|  | ||||
|     (selectSimple(cols, join, cond) ++ orderBy(aCreated.desc)) | ||||
|       .query[RAttachment] | ||||
|       .streamWithChunkSize(chunkSize) | ||||
| //    val aId      = Columns.id.prefix("a") | ||||
| //    val aCreated = Columns.created.prefix("a") | ||||
| //    val mId      = RAttachmentMeta.Columns.id.prefix("m") | ||||
| //    val mPages   = RAttachmentMeta.Columns.pages.prefix("m") | ||||
| // | ||||
| //    val cols = all.map(_.prefix("a")) | ||||
| //    val join = table ++ fr"a LEFT OUTER JOIN" ++ | ||||
| //      RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId) | ||||
| //    val cond = mPages.isNull | ||||
| // | ||||
| //    (selectSimple(cols, join, cond) ++ orderBy(aCreated.desc)) | ||||
| //      .query[RAttachment] | ||||
| //      .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( | ||||
|       coll: Option[Ident], | ||||
|       chunkSize: Int | ||||
|   ): Stream[ConnectionIO, RAttachment] = { | ||||
|     val aId      = Columns.id.prefix("a") | ||||
|     val aItem    = Columns.itemId.prefix("a") | ||||
|     val aCreated = Columns.created.prefix("a") | ||||
|     val pId      = RAttachmentPreview.Columns.id.prefix("p") | ||||
|     val iId      = RItem.Columns.id.prefix("i") | ||||
|     val iColl    = RItem.Columns.cid.prefix("i") | ||||
| //    val aId      = Columns.id.prefix("a") | ||||
| //    val aItem    = Columns.itemId.prefix("a") | ||||
| //    val aCreated = Columns.created.prefix("a") | ||||
| //    val pId      = RAttachmentPreview.Columns.id.prefix("p") | ||||
| //    val iId      = RItem.Columns.id.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 = | ||||
|       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 baseJoin = from(a).leftJoin(p, p.id === a.id) | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       coll.map(_ => baseJoin.innerJoin(i, i.id === a.itemId)).getOrElse(baseJoin), | ||||
|       p.id.isNull &&? coll.map(cid => i.cid === cid) | ||||
|     ).orderBy(a.created.asc).build.query[RAttachment].streamWithChunkSize(chunkSize) | ||||
|   } | ||||
|  | ||||
|   def findNonConvertedPdf( | ||||
|       coll: Option[Ident], | ||||
|       chunkSize: Int | ||||
|   ): Stream[ConnectionIO, RAttachment] = { | ||||
|     val aId     = Columns.id.prefix("a") | ||||
|     val aItem   = Columns.itemId.prefix("a") | ||||
|     val aFile   = Columns.fileId.prefix("a") | ||||
|     val sId     = RAttachmentSource.Columns.id.prefix("s") | ||||
|     val sFile   = RAttachmentSource.Columns.fileId.prefix("s") | ||||
|     val iId     = RItem.Columns.id.prefix("i") | ||||
|     val iColl   = RItem.Columns.cid.prefix("i") | ||||
|     val mId     = RFileMeta.Columns.id.prefix("m") | ||||
|     val mType   = RFileMeta.Columns.mimetype.prefix("m") | ||||
| //    val aId     = Columns.id.prefix("a") | ||||
| //    val aItem   = Columns.itemId.prefix("a") | ||||
| //    val aFile   = Columns.fileId.prefix("a") | ||||
| //    val sId     = RAttachmentSource.Columns.id.prefix("s") | ||||
| //    val sFile   = RAttachmentSource.Columns.fileId.prefix("s") | ||||
| //    val iId     = RItem.Columns.id.prefix("i") | ||||
| //    val iColl   = RItem.Columns.cid.prefix("i") | ||||
| //    val mId     = RFileMeta.Columns.id.prefix("m") | ||||
| //    val mType   = RFileMeta.Columns.mimetype.prefix("m") | ||||
|     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" ++ | ||||
|       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) | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a) | ||||
|         .innerJoin(s, s.id === a.id) | ||||
|         .innerJoin(i, i.id === a.itemId) | ||||
|         .innerJoin(m, m.id === a.fileId), | ||||
|       a.fileId === s.fileId && | ||||
|         m.mimetype.likes(pdfType) &&? | ||||
|         coll.map(cid => i.cid === cid) | ||||
|     ).build.query[RAttachment].streamWithChunkSize(chunkSize) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,9 @@ package docspell.store.records | ||||
| import cats.data.NonEmptyList | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb.TableDef | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import bitpeace.FileMeta | ||||
| import doobie._ | ||||
| @@ -22,10 +23,25 @@ case class 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" | ||||
|  | ||||
|   object Columns { | ||||
|     import docspell.store.impl._ | ||||
|  | ||||
|     val id        = Column("id") | ||||
|     val fileId    = Column("file_id") | ||||
|     val name      = Column("filename") | ||||
| @@ -35,64 +51,83 @@ object RAttachmentArchive { | ||||
|     val all = List(id, fileId, name, messageId, created) | ||||
|   } | ||||
|  | ||||
|   import Columns._ | ||||
|  | ||||
|   def of(ra: RAttachment, mId: Option[String]): RAttachmentArchive = | ||||
|     RAttachmentArchive(ra.id, ra.fileId, ra.name, mId, ra.created) | ||||
|  | ||||
|   def insert(v: RAttachmentArchive): ConnectionIO[Int] = | ||||
|     insertRow( | ||||
|       table, | ||||
|       all, | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       fr"${v.id},${v.fileId},${v.name},${v.messageId},${v.created}" | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   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] = | ||||
|     deleteFrom(table, id.is(attachId)).update.run | ||||
|     DML.delete(T, T.id === attachId) | ||||
|  | ||||
|   def deleteAll(fId: Ident): ConnectionIO[Int] = | ||||
|     deleteFrom(table, fileId.is(fId)).update.run | ||||
|     DML.delete(T, T.fileId === fId) | ||||
|  | ||||
|   def findByIdAndCollective( | ||||
|       attachId: Ident, | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Option[RAttachmentArchive]] = { | ||||
|     val bId   = RAttachment.Columns.id.prefix("b") | ||||
|     val aId   = Columns.id.prefix("a") | ||||
|     val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val bId   = RAttachment.Columns.id.prefix("b") | ||||
| //    val aId   = Columns.id.prefix("a") | ||||
| //    val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
| //    val iId   = RItem.Columns.id.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" ++ | ||||
|       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 | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a) | ||||
|         .innerJoin(b, b.id === a.id) | ||||
|         .innerJoin(i, i.id === b.itemId), | ||||
|       a.id === attachId && b.id === attachId && i.cid === collective | ||||
|     ).build.query[RAttachmentArchive].option | ||||
|   } | ||||
|  | ||||
|   def findByMessageIdAndCollective( | ||||
|       messageIds: NonEmptyList[String], | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Vector[RAttachmentArchive]] = { | ||||
|     val bId    = RAttachment.Columns.id.prefix("b") | ||||
|     val bItem  = RAttachment.Columns.itemId.prefix("b") | ||||
|     val aMsgId = Columns.messageId.prefix("a") | ||||
|     val aId    = Columns.id.prefix("a") | ||||
|     val iId    = RItem.Columns.id.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(aMsgId.isIn(messageIds), iColl.is(collective)) | ||||
|  | ||||
|     selectSimple(all.map(_.prefix("a")), from, where).query[RAttachmentArchive].to[Vector] | ||||
| //    val bId    = RAttachment.Columns.id.prefix("b") | ||||
| //    val bItem  = RAttachment.Columns.itemId.prefix("b") | ||||
| //    val aMsgId = Columns.messageId.prefix("a") | ||||
| //    val aId    = Columns.id.prefix("a") | ||||
| //    val iId    = RItem.Columns.id.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(aMsgId.isIn(messageIds), iColl.is(collective)) | ||||
| // | ||||
| //    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( | ||||
| @@ -100,31 +135,49 @@ object RAttachmentArchive { | ||||
|   ): ConnectionIO[Vector[(RAttachmentArchive, FileMeta)]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     val aId       = Columns.id.prefix("a") | ||||
|     val afileMeta = fileId.prefix("a") | ||||
|     val bPos      = RAttachment.Columns.position.prefix("b") | ||||
|     val bId       = RAttachment.Columns.id.prefix("b") | ||||
|     val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
|     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[(RAttachmentArchive, FileMeta)] | ||||
|       .to[Vector] | ||||
| //    val aId       = Columns.id.prefix("a") | ||||
| //    val afileMeta = fileId.prefix("a") | ||||
| //    val bPos      = RAttachment.Columns.position.prefix("b") | ||||
| //    val bId       = RAttachment.Columns.id.prefix("b") | ||||
| //    val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
| //    val mId       = RFileMeta.as("m").id.column | ||||
| // | ||||
| //    val cols = all.map(_.prefix("a")) ++ RFileMeta.as("m").all.map(_.column).toList | ||||
| //    val from = table ++ fr"a INNER JOIN" ++ | ||||
| //      Fragment.const(RFileMeta.T.tableName) ++ 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[(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 | ||||
|     * the number of all associated attachments. Returns 0 if there is | ||||
|     * no archive for the given attachment. | ||||
|     */ | ||||
|   def countEntries(attachId: Ident): ConnectionIO[Int] = { | ||||
|     val qFileId = selectSimple(Seq(fileId), table, id.is(attachId)) | ||||
|     val q       = selectCount(id, table, fileId.isSubquery(qFileId)) | ||||
|     q.query[Int].unique | ||||
|   } | ||||
|   def countEntries(attachId: Ident): ConnectionIO[Int] = | ||||
| //    val qFileId = selectSimple(Seq(fileId), table, id.is(attachId)) | ||||
| //    val q       = selectCount(id, table, fileId.isSubquery(qFileId)) | ||||
| //    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 | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
| import cats.implicits._ | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import doobie._ | ||||
| import doobie.implicits._ | ||||
| @@ -29,9 +30,25 @@ object RAttachmentMeta { | ||||
|   def empty(attachId: Ident) = | ||||
|     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 { | ||||
|     import docspell.store.impl._ | ||||
|  | ||||
|     val id        = Column("attachid") | ||||
|     val content   = Column("content") | ||||
|     val nerlabels = Column("nerlabels") | ||||
| @@ -39,23 +56,22 @@ object RAttachmentMeta { | ||||
|     val pages     = Column("page_count") | ||||
|     val all       = List(id, content, nerlabels, proposals, pages) | ||||
|   } | ||||
|   import Columns._ | ||||
|  | ||||
|   def insert(v: RAttachmentMeta): ConnectionIO[Int] = | ||||
|     insertRow( | ||||
|       table, | ||||
|       all, | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       fr"${v.id},${v.content},${v.nerlabels},${v.proposals},${v.pages}" | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   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]] = | ||||
|     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]] = | ||||
|     selectSimple(Seq(pages), table, id.is(attachId)) | ||||
|     Select(T.pages.s, from(T), T.id === attachId).build | ||||
|       .query[Option[Int]] | ||||
|       .option | ||||
|       .map(_.flatten) | ||||
| @@ -67,37 +83,37 @@ object RAttachmentMeta { | ||||
|     } yield n1 | ||||
|  | ||||
|   def update(v: RAttachmentMeta): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       id.is(v.id), | ||||
|       commas( | ||||
|         content.setTo(v.content), | ||||
|         nerlabels.setTo(v.nerlabels), | ||||
|         proposals.setTo(v.proposals) | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === v.id, | ||||
|       DML.set( | ||||
|         T.content.setTo(v.content), | ||||
|         T.nerlabels.setTo(v.nerlabels), | ||||
|         T.proposals.setTo(v.proposals) | ||||
|       ) | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   def updateLabels(mid: Ident, labels: List[NerLabel]): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       id.is(mid), | ||||
|       commas( | ||||
|         nerlabels.setTo(labels) | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === mid, | ||||
|       DML.set( | ||||
|         T.nerlabels.setTo(labels) | ||||
|       ) | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   def updateProposals(mid: Ident, plist: MetaProposalList): ConnectionIO[Int] = | ||||
|     updateRow( | ||||
|       table, | ||||
|       id.is(mid), | ||||
|       commas( | ||||
|         proposals.setTo(plist) | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === mid, | ||||
|       DML.set( | ||||
|         T.proposals.setTo(plist) | ||||
|       ) | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   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] = | ||||
|     deleteFrom(table, id.is(attachId)).update.run | ||||
|     DML.delete(T, T.id === attachId) | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import bitpeace.FileMeta | ||||
| import doobie._ | ||||
| @@ -19,10 +21,24 @@ case class 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" | ||||
|  | ||||
|   object Columns { | ||||
|     import docspell.store.impl._ | ||||
|     val id      = Column("id") | ||||
|     val fileId  = Column("file_id") | ||||
|     val name    = Column("filename") | ||||
| @@ -31,67 +47,98 @@ object RAttachmentPreview { | ||||
|     val all = List(id, fileId, name, created) | ||||
|   } | ||||
|  | ||||
|   import Columns._ | ||||
|  | ||||
|   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]] = | ||||
|     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] = | ||||
|     deleteFrom(table, id.is(attachId)).update.run | ||||
|     DML.delete(T, T.id === attachId) | ||||
|  | ||||
|   def findByIdAndCollective( | ||||
|       attachId: Ident, | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Option[RAttachmentPreview]] = { | ||||
|     val bId   = RAttachment.Columns.id.prefix("b") | ||||
|     val aId   = Columns.id.prefix("a") | ||||
|     val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val bId   = RAttachment.Columns.id.prefix("b") | ||||
| //    val aId   = Columns.id.prefix("a") | ||||
| //    val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
| //    val iId   = RItem.Columns.id.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" ++ | ||||
|       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 | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a) | ||||
|         .innerJoin(b, a.id === b.id) | ||||
|         .innerJoin(i, i.id === b.itemId), | ||||
|       a.id === attachId && b.id === attachId && i.cid === collective | ||||
|     ).build.query[RAttachmentPreview].option | ||||
|   } | ||||
|  | ||||
|   def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentPreview]] = { | ||||
|     val sId   = Columns.id.prefix("s") | ||||
|     val aId   = RAttachment.Columns.id.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[RAttachmentPreview] | ||||
|       .to[Vector] | ||||
| //    val sId   = Columns.id.prefix("s") | ||||
| //    val aId   = RAttachment.Columns.id.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[RAttachmentPreview] | ||||
| //      .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( | ||||
|       itemId: Ident, | ||||
|       coll: Ident | ||||
|   ): ConnectionIO[Option[RAttachmentPreview]] = { | ||||
|     val sId   = Columns.id.prefix("s") | ||||
|     val aId   = RAttachment.Columns.id.prefix("a") | ||||
|     val aItem = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aPos  = RAttachment.Columns.position.prefix("a") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val sId   = Columns.id.prefix("s") | ||||
| //    val aId   = RAttachment.Columns.id.prefix("a") | ||||
| //    val aItem = RAttachment.Columns.itemId.prefix("a") | ||||
| //    val aPos  = RAttachment.Columns.position.prefix("a") | ||||
| //    val iId   = RItem.Columns.id.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 = | ||||
|       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)) | ||||
|     ) | ||||
|     Select( | ||||
|       select(s.all).append(a.position.s), | ||||
|       from(s) | ||||
|         .innerJoin(a, s.id === a.id) | ||||
|         .innerJoin(i, i.id === a.itemId), | ||||
|       a.itemId === itemId && i.cid === coll | ||||
|     ).build | ||||
|       .query[(RAttachmentPreview, Int)] | ||||
|       .to[Vector] | ||||
|       .map(_.sortBy(_._2).headOption.map(_._1)) | ||||
| @@ -102,22 +149,33 @@ object RAttachmentPreview { | ||||
|   ): ConnectionIO[Vector[(RAttachmentPreview, FileMeta)]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     val aId       = Columns.id.prefix("a") | ||||
|     val afileMeta = fileId.prefix("a") | ||||
|     val bPos      = RAttachment.Columns.position.prefix("b") | ||||
|     val bId       = RAttachment.Columns.id.prefix("b") | ||||
|     val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
|     val mId       = RFileMeta.Columns.id.prefix("m") | ||||
| //    val aId       = Columns.id.prefix("a") | ||||
| //    val afileMeta = fileId.prefix("a") | ||||
| //    val bPos      = RAttachment.Columns.position.prefix("b") | ||||
| //    val bId       = RAttachment.Columns.id.prefix("b") | ||||
| //    val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
| //    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")) | ||||
|     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] | ||||
|     Select( | ||||
|       select(a.all, m.all), | ||||
|       from(a) | ||||
|         .innerJoin(m, a.fileId === m.id) | ||||
|         .innerJoin(b, b.id === a.id), | ||||
|       b.itemId === id | ||||
|     ).orderBy(b.position.asc).build.query[(RAttachmentPreview, FileMeta)].to[Vector] | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import bitpeace.FileMeta | ||||
| import doobie._ | ||||
| @@ -19,10 +21,24 @@ case class 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" | ||||
|  | ||||
|   object Columns { | ||||
|     import docspell.store.impl._ | ||||
|     val id      = Column("id") | ||||
|     val fileId  = Column("file_id") | ||||
|     val name    = Column("filename") | ||||
| @@ -31,67 +47,90 @@ object RAttachmentSource { | ||||
|     val all = List(id, fileId, name, created) | ||||
|   } | ||||
|  | ||||
|   import Columns._ | ||||
|  | ||||
|   def of(ra: RAttachment): RAttachmentSource = | ||||
|     RAttachmentSource(ra.id, ra.fileId, ra.name, ra.created) | ||||
|  | ||||
|   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]] = | ||||
|     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] = | ||||
|     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] | ||||
|       .unique | ||||
|       .map(_ > 0) | ||||
|  | ||||
|   def isConverted(attachId: Ident): ConnectionIO[Boolean] = { | ||||
|     val sId   = Columns.id.prefix("s") | ||||
|     val sFile = Columns.fileId.prefix("s") | ||||
|     val aId   = RAttachment.Columns.id.prefix("a") | ||||
|     val aFile = RAttachment.Columns.fileId.prefix("a") | ||||
|     val s = RAttachmentSource.as("s") | ||||
|     val a = RAttachment.as("a") | ||||
|     Select( | ||||
|       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" ++ | ||||
|       RAttachment.table ++ fr"a ON" ++ aId.is(sId) | ||||
|  | ||||
|     selectCount(aId, from, and(aId.is(attachId), aFile.isNot(sFile))) | ||||
|       .query[Int] | ||||
|       .unique | ||||
|       .map(_ > 0) | ||||
| //    val sId   = Columns.id.prefix("s") | ||||
| //    val sFile = Columns.fileId.prefix("s") | ||||
| //    val aId   = RAttachment.Columns.id.prefix("a") | ||||
| //    val aFile = RAttachment.Columns.fileId.prefix("a") | ||||
| // | ||||
| //    val from = table ++ fr"s INNER JOIN" ++ | ||||
| //      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] = | ||||
|     deleteFrom(table, id.is(attachId)).update.run | ||||
|     DML.delete(T, T.id === attachId) | ||||
|  | ||||
|   def findByIdAndCollective( | ||||
|       attachId: Ident, | ||||
|       collective: Ident | ||||
|   ): ConnectionIO[Option[RAttachmentSource]] = { | ||||
|     val bId   = RAttachment.Columns.id.prefix("b") | ||||
|     val aId   = Columns.id.prefix("a") | ||||
|     val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
| //    val bId   = RAttachment.Columns.id.prefix("b") | ||||
| //    val aId   = Columns.id.prefix("a") | ||||
| //    val bItem = RAttachment.Columns.itemId.prefix("b") | ||||
| //    val iId   = RItem.Columns.id.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" ++ | ||||
|       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 | ||||
|     Select( | ||||
|       select(a.all), | ||||
|       from(a) | ||||
|         .innerJoin(b, a.id === b.id) | ||||
|         .innerJoin(i, i.id === b.itemId), | ||||
|       a.id === attachId && b.id === attachId && i.cid === collective | ||||
|     ).build.query[RAttachmentSource].option | ||||
|   } | ||||
|  | ||||
|   def findByItem(itemId: Ident): ConnectionIO[Vector[RAttachmentSource]] = { | ||||
|     val sId   = Columns.id.prefix("s") | ||||
|     val aId   = RAttachment.Columns.id.prefix("a") | ||||
|     val aItem = RAttachment.Columns.itemId.prefix("a") | ||||
| //    val sId   = Columns.id.prefix("s") | ||||
| //    val aId   = RAttachment.Columns.id.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) | ||||
|     selectSimple(all.map(_.prefix("s")), from, aItem.is(itemId)) | ||||
|     val s = RAttachmentSource.as("s") | ||||
|     val a = RAttachment.as("a") | ||||
|     Select(select(s.all), from(s).innerJoin(a, a.id === s.id), a.itemId === itemId).build | ||||
|       .query[RAttachmentSource] | ||||
|       .to[Vector] | ||||
|   } | ||||
| @@ -101,22 +140,33 @@ object RAttachmentSource { | ||||
|   ): ConnectionIO[Vector[(RAttachmentSource, FileMeta)]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     val aId       = Columns.id.prefix("a") | ||||
|     val afileMeta = fileId.prefix("a") | ||||
|     val bPos      = RAttachment.Columns.position.prefix("b") | ||||
|     val bId       = RAttachment.Columns.id.prefix("b") | ||||
|     val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
|     val mId       = RFileMeta.Columns.id.prefix("m") | ||||
| //    val aId       = Columns.id.prefix("a") | ||||
| //    val afileMeta = fileId.prefix("a") | ||||
| //    val bPos      = RAttachment.Columns.position.prefix("b") | ||||
| //    val bId       = RAttachment.Columns.id.prefix("b") | ||||
| //    val bItem     = RAttachment.Columns.itemId.prefix("b") | ||||
| //    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")) | ||||
|     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] | ||||
|     Select( | ||||
|       select(a.all, m.all), | ||||
|       from(a) | ||||
|         .innerJoin(m, a.fileId === m.id) | ||||
|         .innerJoin(b, b.id === a.id), | ||||
|       b.itemId === id | ||||
|     ).orderBy(b.position.asc).build.query[(RAttachmentSource, FileMeta)].to[Vector] | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
| import cats.implicits._ | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import com.github.eikek.calev._ | ||||
| import doobie._ | ||||
| @@ -21,71 +22,69 @@ case class RClassifierSetting( | ||||
| ) {} | ||||
|  | ||||
| object RClassifierSetting { | ||||
|   final case class Table(alias: Option[String]) extends TableDef { | ||||
|     val tableName = "classifier_setting" | ||||
|  | ||||
|   val table = fr"classifier_setting" | ||||
|  | ||||
|   object Columns { | ||||
|     val cid       = Column("cid") | ||||
|     val enabled   = Column("enabled") | ||||
|     val schedule  = Column("schedule") | ||||
|     val category  = Column("category") | ||||
|     val itemCount = Column("item_count") | ||||
|     val fileId    = Column("file_id") | ||||
|     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 | ||||
|     val cid       = Column[Ident]("cid", this) | ||||
|     val enabled   = Column[Boolean]("enabled", this) | ||||
|     val schedule  = Column[CalEvent]("schedule", this) | ||||
|     val category  = Column[String]("category", this) | ||||
|     val itemCount = Column[Int]("item_count", this) | ||||
|     val fileId    = Column[Ident]("file_id", this) | ||||
|     val created   = Column[Timestamp]("created", this) | ||||
|     val all = NonEmptyList | ||||
|       .of[Column[_]](cid, enabled, schedule, category, itemCount, fileId, created) | ||||
|   } | ||||
|  | ||||
|   def updateAll(v: RClassifierSetting): ConnectionIO[Int] = { | ||||
|     val sql = updateRow( | ||||
|       table, | ||||
|       cid.is(v.cid), | ||||
|       commas( | ||||
|         enabled.setTo(v.enabled), | ||||
|         schedule.setTo(v.schedule), | ||||
|         category.setTo(v.category), | ||||
|         itemCount.setTo(v.itemCount), | ||||
|         fileId.setTo(v.fileId) | ||||
|   val T = Table(None) | ||||
|   def as(alias: String): Table = | ||||
|     Table(Some(alias)) | ||||
|  | ||||
|   def insert(v: RClassifierSetting): ConnectionIO[Int] = | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       fr"${v.cid},${v.enabled},${v.schedule},${v.category},${v.itemCount},${v.fileId},${v.created}" | ||||
|     ) | ||||
|  | ||||
|   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] = | ||||
|     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] = | ||||
|     for { | ||||
|       n1 <- updateRow( | ||||
|         table, | ||||
|         cid.is(v.cid), | ||||
|         commas( | ||||
|           enabled.setTo(v.enabled), | ||||
|           schedule.setTo(v.schedule), | ||||
|           itemCount.setTo(v.itemCount), | ||||
|           category.setTo(v.category) | ||||
|       n1 <- DML.update( | ||||
|         T, | ||||
|         T.cid === v.cid, | ||||
|         DML.set( | ||||
|           T.enabled.setTo(v.enabled), | ||||
|           T.schedule.setTo(v.schedule), | ||||
|           T.itemCount.setTo(v.itemCount), | ||||
|           T.category.setTo(v.category) | ||||
|         ) | ||||
|       ).update.run | ||||
|       ) | ||||
|       n2 <- if (n1 <= 0) insert(v) else 0.pure[ConnectionIO] | ||||
|     } yield n1 + n2 | ||||
|  | ||||
|   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 | ||||
|   } | ||||
|  | ||||
|   def delete(coll: Ident): ConnectionIO[Int] = | ||||
|     deleteFrom(table, cid.is(coll)).update.run | ||||
|     DML.delete(T, T.cid === coll) | ||||
|  | ||||
|   case class Classifier( | ||||
|       enabled: Boolean, | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
| import fs2.Stream | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Column | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import doobie._ | ||||
| import doobie.implicits._ | ||||
| @@ -18,58 +19,54 @@ case class 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 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) | ||||
|     val all = NonEmptyList.of[Column[_]](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] = { | ||||
|     val sql = insertRow( | ||||
|       table, | ||||
|       Columns.all, | ||||
|   def insert(value: RCollective): ConnectionIO[Int] = | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       fr"${value.id},${value.state},${value.language},${value.integrationEnabled},${value.created}" | ||||
|     ) | ||||
|     sql.update.run | ||||
|   } | ||||
|  | ||||
|   def update(value: RCollective): ConnectionIO[Int] = { | ||||
|     val sql = updateRow( | ||||
|       table, | ||||
|       id.is(value.id), | ||||
|       commas( | ||||
|         state.setTo(value.state) | ||||
|   def update(value: RCollective): ConnectionIO[Int] = | ||||
|     DML.update( | ||||
|       T, | ||||
|       T.id === value.id, | ||||
|       DML.set( | ||||
|         T.state.setTo(value.state) | ||||
|       ) | ||||
|     ) | ||||
|     sql.update.run | ||||
|   } | ||||
|  | ||||
|   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] = | ||||
|     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] = | ||||
|     for { | ||||
|       n1 <- updateRow( | ||||
|         table, | ||||
|         id.is(cid), | ||||
|         commas( | ||||
|           language.setTo(settings.language), | ||||
|           integration.setTo(settings.integrationEnabled) | ||||
|       n1 <- DML.update( | ||||
|         T, | ||||
|         T.id === cid, | ||||
|         DML.set( | ||||
|           T.language.setTo(settings.language), | ||||
|           T.integration.setTo(settings.integrationEnabled) | ||||
|         ) | ||||
|       ).update.run | ||||
|       ) | ||||
|       cls <- | ||||
|         Timestamp | ||||
|           .current[ConnectionIO] | ||||
| @@ -83,66 +80,64 @@ object RCollective { | ||||
|     } yield n1 + n2 | ||||
|  | ||||
|   def getSettings(coll: Ident): ConnectionIO[Option[Settings]] = { | ||||
|     val cId   = id.prefix("c") | ||||
|     val CS    = RClassifierSetting.Columns | ||||
|     val csCid = CS.cid.prefix("cs") | ||||
|     val c  = RCollective.as("c") | ||||
|     val cs = RClassifierSetting.as("cs") | ||||
|  | ||||
|     val cols = Seq( | ||||
|       language.prefix("c"), | ||||
|       integration.prefix("c"), | ||||
|       CS.enabled.prefix("cs"), | ||||
|       CS.schedule.prefix("cs"), | ||||
|       CS.itemCount.prefix("cs"), | ||||
|       CS.category.prefix("cs") | ||||
|     ) | ||||
|     val from = table ++ fr"c LEFT JOIN" ++ | ||||
|       RClassifierSetting.table ++ fr"cs ON" ++ csCid.is(cId) | ||||
|  | ||||
|     selectSimple(cols, from, cId.is(coll)) | ||||
|       .query[Settings] | ||||
|       .option | ||||
|     Select( | ||||
|       select( | ||||
|         c.language.s, | ||||
|         c.integration.s, | ||||
|         cs.enabled.s, | ||||
|         cs.schedule.s, | ||||
|         cs.itemCount.s, | ||||
|         cs.category.s | ||||
|       ), | ||||
|       from(c).leftJoin(cs, cs.cid === c.id), | ||||
|       c.id === coll | ||||
|     ).build.query[Settings].option | ||||
|   } | ||||
|  | ||||
|   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 | ||||
|   } | ||||
|  | ||||
|   def findByItem(itemId: Ident): ConnectionIO[Option[RCollective]] = { | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val cId   = id.prefix("c") | ||||
|     val from  = RItem.table ++ fr"i INNER JOIN" ++ table ++ fr"c ON" ++ iColl.is(cId) | ||||
|     selectSimple(all.map(_.prefix("c")), from, iId.is(itemId)).query[RCollective].option | ||||
|     val i = RItem.as("i") | ||||
|     val c = RCollective.as("c") | ||||
|     Select( | ||||
|       select(c.all), | ||||
|       from(i).innerJoin(c, i.cid === c.id), | ||||
|       i.id === itemId | ||||
|     ).build.query[RCollective].option | ||||
|   } | ||||
|  | ||||
|   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) | ||||
|   } | ||||
|  | ||||
|   def findAll(order: Columns.type => Column): ConnectionIO[Vector[RCollective]] = { | ||||
|     val sql = selectSimple(all, table, Fragment.empty) ++ orderBy(order(Columns).f) | ||||
|     sql.query[RCollective].to[Vector] | ||||
|   def findAll(order: Table => Column[_]): ConnectionIO[Vector[RCollective]] = { | ||||
|     val sql = Select(select(T.all), from(T)).orderBy(order(T)) | ||||
|     sql.build.query[RCollective].to[Vector] | ||||
|   } | ||||
|  | ||||
|   def streamAll(order: Columns.type => Column): Stream[ConnectionIO, RCollective] = { | ||||
|     val sql = selectSimple(all, table, Fragment.empty) ++ orderBy(order(Columns).f) | ||||
|     sql.query[RCollective].stream | ||||
|   def streamAll(order: Table => Column[_]): Stream[ConnectionIO, RCollective] = { | ||||
|     val sql = Select(select(T.all), from(T)).orderBy(order(T)) | ||||
|     sql.build.query[RCollective].stream | ||||
|   } | ||||
|  | ||||
|   def findByAttachment(attachId: Ident): ConnectionIO[Option[RCollective]] = { | ||||
|     val iColl = RItem.Columns.cid.prefix("i") | ||||
|     val iId   = RItem.Columns.id.prefix("i") | ||||
|     val aItem = RAttachment.Columns.itemId.prefix("a") | ||||
|     val aId   = RAttachment.Columns.id.prefix("a") | ||||
|     val cId   = Columns.id.prefix("c") | ||||
|  | ||||
|     val from = table ++ fr"c INNER JOIN" ++ | ||||
|       RItem.table ++ fr"i ON" ++ cId.is(iColl) ++ fr"INNER JOIN" ++ | ||||
|       RAttachment.table ++ fr"a ON" ++ aItem.is(iId) | ||||
|  | ||||
|     selectSimple(all.map(_.prefix("c")), from, aId.is(attachId)).query[RCollective].option | ||||
|     val i = RItem.as("i") | ||||
|     val a = RAttachment.as("a") | ||||
|     val c = RCollective.as("c") | ||||
|     Select( | ||||
|       select(c.all), | ||||
|       from(c) | ||||
|         .innerJoin(i, c.id === i.cid) | ||||
|         .innerJoin(a, a.itemId === i.id), | ||||
|       a.id === attachId | ||||
|     ).build.query[RCollective].option | ||||
|   } | ||||
|  | ||||
|   case class Settings( | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| package docspell.store.records | ||||
|  | ||||
| import java.time.Instant | ||||
|  | ||||
| import cats.data.NonEmptyList | ||||
| import cats.implicits._ | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
| import docspell.store.syntax.MimeTypes._ | ||||
|  | ||||
| import bitpeace.FileMeta | ||||
| @@ -14,26 +16,30 @@ import doobie._ | ||||
| import doobie.implicits._ | ||||
|  | ||||
| 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 id        = Column("id") | ||||
|     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 all = NonEmptyList | ||||
|       .of[Column[_]](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]] = { | ||||
|     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]] = { | ||||
| @@ -41,7 +47,7 @@ object RFileMeta { | ||||
|  | ||||
|     NonEmptyList.fromList(ids) match { | ||||
|       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 => | ||||
|         Vector.empty[FileMeta].pure[ConnectionIO] | ||||
|     } | ||||
| @@ -50,7 +56,7 @@ object RFileMeta { | ||||
|   def findMime(fid: Ident): ConnectionIO[Option[MimeType]] = { | ||||
|     import bitpeace.sql._ | ||||
|  | ||||
|     selectSimple(Seq(Columns.mimetype), table, Columns.id.is(fid)) | ||||
|     run(select(T.mimetype), from(T), T.id === fid) | ||||
|       .query[Mimetype] | ||||
|       .option | ||||
|       .map(_.map(_.toLocal)) | ||||
|   | ||||
| @@ -5,9 +5,8 @@ import cats.effect.Sync | ||||
| import cats.implicits._ | ||||
|  | ||||
| import docspell.common._ | ||||
| import docspell.store.impl.Implicits._ | ||||
| import docspell.store.impl._ | ||||
| import docspell.store.qb.{Select, TableDef} | ||||
| import docspell.store.qb.DSL._ | ||||
| import docspell.store.qb._ | ||||
|  | ||||
| import doobie._ | ||||
| import doobie.implicits._ | ||||
| @@ -110,8 +109,9 @@ object RItem { | ||||
|     Table(Some(alias)) | ||||
|  | ||||
|   val table = fr"item" | ||||
|  | ||||
|   object Columns { | ||||
|     import docspell.store.impl._ | ||||
|  | ||||
|     val id            = Column("itemid") | ||||
|     val cid           = Column("cid") | ||||
|     val name          = Column("name") | ||||
| @@ -149,19 +149,21 @@ object RItem { | ||||
|       folder | ||||
|     ) | ||||
|   } | ||||
|   import Columns._ | ||||
|  | ||||
|   private val currentTime = | ||||
|     Timestamp.current[ConnectionIO] | ||||
|  | ||||
|   def insert(v: RItem): ConnectionIO[Int] = | ||||
|     insertRow( | ||||
|       table, | ||||
|       all, | ||||
|     DML.insert( | ||||
|       T, | ||||
|       T.all, | ||||
|       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.created},${v.updated},${v.notes},${v.folderId}" | ||||
|     ).update.run | ||||
|     ) | ||||
|  | ||||
|   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( | ||||
|       itemId: Ident, | ||||
| @@ -170,11 +172,11 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.is(itemId), state.isIn(existing)), | ||||
|         commas(state.setTo(itemState), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id === itemId && T.state.in(existing), | ||||
|         DML.set(T.state.setTo(itemState), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateStateForCollective( | ||||
| @@ -184,11 +186,11 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(state.setTo(itemState), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.state.setTo(itemState), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateDirection( | ||||
| @@ -198,11 +200,11 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(incoming.setTo(dir), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.incoming.setTo(dir), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateCorrOrg( | ||||
| @@ -212,21 +214,21 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(corrOrg.setTo(org), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.corrOrg.setTo(org), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def removeCorrOrg(coll: Ident, currentOrg: Ident): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(cid.is(coll), corrOrg.is(Some(currentOrg))), | ||||
|         commas(corrOrg.setTo(None: Option[Ident]), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.cid === coll && T.corrOrg === currentOrg, | ||||
|         DML.set(T.corrOrg.setTo(None: Option[Ident]), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateCorrPerson( | ||||
| @@ -236,21 +238,21 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(corrPerson.setTo(person), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.corrPerson.setTo(person), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def removeCorrPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(cid.is(coll), corrPerson.is(Some(currentPerson))), | ||||
|         commas(corrPerson.setTo(None: Option[Ident]), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.cid === coll && T.corrPerson === currentPerson, | ||||
|         DML.set(T.corrPerson.setTo(None: Option[Ident]), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateConcPerson( | ||||
| @@ -260,21 +262,21 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(concPerson.setTo(person), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.concPerson.setTo(person), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def removeConcPerson(coll: Ident, currentPerson: Ident): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(cid.is(coll), concPerson.is(Some(currentPerson))), | ||||
|         commas(concPerson.setTo(None: Option[Ident]), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.cid === coll && T.concPerson === currentPerson, | ||||
|         DML.set(T.concPerson.setTo(None: Option[Ident]), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateConcEquip( | ||||
| @@ -284,21 +286,21 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(concEquipment.setTo(equip), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.concEquipment.setTo(equip), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def removeConcEquip(coll: Ident, currentEquip: Ident): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(cid.is(coll), concEquipment.is(Some(currentEquip))), | ||||
|         commas(concEquipment.setTo(None: Option[Ident]), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.cid === coll && T.concEquipment === currentEquip, | ||||
|         DML.set(T.concEquipment.setTo(None: Option[Ident]), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateFolder( | ||||
| @@ -308,31 +310,31 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(cid.is(coll), id.is(itemId)), | ||||
|         commas(folder.setTo(folderId), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.cid === coll && T.id === itemId, | ||||
|         DML.set(T.folder.setTo(folderId), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateNotes(itemId: Ident, coll: Ident, text: Option[String]): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.is(itemId), cid.is(coll)), | ||||
|         commas(notes.setTo(text), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id === itemId && T.cid === coll, | ||||
|         DML.set(T.notes.setTo(text), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateName(itemId: Ident, coll: Ident, itemName: String): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.is(itemId), cid.is(coll)), | ||||
|         commas(name.setTo(itemName), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id === itemId && T.cid === coll, | ||||
|         DML.set(T.name.setTo(itemName), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateDate( | ||||
| @@ -342,11 +344,11 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(itemDate.setTo(date), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.itemDate.setTo(date), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   def updateDueDate( | ||||
| @@ -356,50 +358,50 @@ object RItem { | ||||
|   ): ConnectionIO[Int] = | ||||
|     for { | ||||
|       t <- currentTime | ||||
|       n <- updateRow( | ||||
|         table, | ||||
|         and(id.isIn(itemIds), cid.is(coll)), | ||||
|         commas(dueDate.setTo(date), updated.setTo(t)) | ||||
|       ).update.run | ||||
|       n <- DML.update( | ||||
|         T, | ||||
|         T.id.in(itemIds) && T.cid === coll, | ||||
|         DML.set(T.dueDate.setTo(date), T.updated.setTo(t)) | ||||
|       ) | ||||
|     } yield n | ||||
|  | ||||
|   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] = | ||||
|     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] = | ||||
|     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( | ||||
|       itemIds: NonEmptyList[Ident], | ||||
|       coll: Ident | ||||
|   ): 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] | ||||
|       .unique | ||||
|       .map(_ == itemIds.size) | ||||
|  | ||||
|   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]] = | ||||
|     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]] = | ||||
|     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] = { | ||||
|     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 = { | ||||
|     import docspell.store.qb.DSL._ | ||||
|  | ||||
|   def filterItemsFragment(items: NonEmptyList[Ident], coll: Ident): Select = | ||||
|     Select(select(T.id), from(T), T.cid === coll && T.id.in(items)) | ||||
|   } | ||||
|  | ||||
|   def filterItems(items: NonEmptyList[Ident], coll: Ident): ConnectionIO[Vector[Ident]] = | ||||
|     filterItemsFragment(items, coll).build.query[Ident].to[Vector] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user