From a355767fdbdcd8300a1d32448e11dadb1f3883bb Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sun, 13 Dec 2020 21:15:53 +0100 Subject: [PATCH] Convert all query libs besides QItem --- .../docspell/backend/ops/OItemSearch.scala | 4 +- .../main/scala/docspell/store/qb/Select.scala | 5 ++ .../scala/docspell/store/queries/Batch.scala | 22 +++++ .../docspell/store/queries/QAttachment.scala | 90 +++++++------------ .../docspell/store/queries/QCollective.scala | 42 ++++----- .../docspell/store/queries/QCustomField.scala | 35 +------- .../scala/docspell/store/queries/QItem.scala | 21 ----- .../scala/docspell/store/queries/QMails.scala | 72 ++++++--------- .../store/queries/QOrganization.scala | 44 ++++----- 9 files changed, 122 insertions(+), 213 deletions(-) create mode 100644 modules/store/src/main/scala/docspell/store/queries/Batch.scala diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala b/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala index c546a184..25efda8d 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala @@ -59,8 +59,8 @@ object OItemSearch { type Query = QItem.Query val Query = QItem.Query - type Batch = QItem.Batch - val Batch = QItem.Batch + type Batch = docspell.store.queries.Batch + val Batch = docspell.store.queries.Batch type ListItem = QItem.ListItem val ListItem = QItem.ListItem diff --git a/modules/store/src/main/scala/docspell/store/qb/Select.scala b/modules/store/src/main/scala/docspell/store/qb/Select.scala index 406135f0..e219ee03 100644 --- a/modules/store/src/main/scala/docspell/store/qb/Select.scala +++ b/modules/store/src/main/scala/docspell/store/qb/Select.scala @@ -64,6 +64,11 @@ object Select { def distinct: SimpleSelect = copy(distinctFlag = true) + + def where(c: Option[Condition]): SimpleSelect = + copy(where = c) + def where(c: Condition): SimpleSelect = + copy(where = Some(c)) } case class Union(q: Select, qs: Vector[Select]) extends Select diff --git a/modules/store/src/main/scala/docspell/store/queries/Batch.scala b/modules/store/src/main/scala/docspell/store/queries/Batch.scala new file mode 100644 index 00000000..d88ec957 --- /dev/null +++ b/modules/store/src/main/scala/docspell/store/queries/Batch.scala @@ -0,0 +1,22 @@ +package docspell.store.queries + +case class Batch(offset: Int, limit: Int) { + def restrictLimitTo(n: Int): Batch = + Batch(offset, math.min(n, limit)) + + def next: Batch = + Batch(offset + limit, limit) + + def first: Batch = + Batch(0, limit) +} + +object Batch { + val all: Batch = Batch(0, Int.MaxValue) + + def page(n: Int, size: Int): Batch = + Batch(n * size, size) + + def limit(c: Int): Batch = + Batch(0, c) +} diff --git a/modules/store/src/main/scala/docspell/store/queries/QAttachment.scala b/modules/store/src/main/scala/docspell/store/queries/QAttachment.scala index f1aae89a..6ac9327a 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QAttachment.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QAttachment.scala @@ -8,15 +8,20 @@ import fs2.Stream import docspell.common._ import docspell.common.syntax.all._ import docspell.store.Store -import docspell.store.impl.Implicits._ +import docspell.store.qb.DSL._ +import docspell.store.qb._ import docspell.store.records._ import doobie._ -import doobie.implicits._ object QAttachment { private[this] val logger = org.log4s.getLogger + private val a = RAttachment.as("a") + private val item = RItem.as("i") + private val am = RAttachmentMeta.as("am") + private val c = RCollective.as("c") + def deletePreview[F[_]: Sync](store: Store[F])(attachId: Ident): F[Int] = { val findPreview = for { @@ -113,20 +118,13 @@ object QAttachment { } yield ns.sum def getMetaProposals(itemId: Ident, coll: Ident): ConnectionIO[MetaProposalList] = { - val AC = RAttachment.Columns - val MC = RAttachmentMeta.Columns - val IC = RItem.Columns - - val q = fr"SELECT" ++ MC.proposals - .prefix("m") - .f ++ fr"FROM" ++ RAttachmentMeta.table ++ fr"m" ++ - fr"INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ AC.id - .prefix("a") - .is(MC.id.prefix("m")) ++ - fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ AC.itemId - .prefix("a") - .is(IC.id.prefix("i")) ++ - fr"WHERE" ++ and(AC.itemId.prefix("a").is(itemId), IC.cid.prefix("i").is(coll)) + val q = Select( + am.proposals.s, + from(am) + .innerJoin(a, a.id === am.id) + .innerJoin(item, a.itemId === item.id), + a.itemId === itemId && item.cid === coll + ).build for { ml <- q.query[MetaProposalList].to[Vector] @@ -137,24 +135,13 @@ object QAttachment { attachId: Ident, collective: Ident ): ConnectionIO[Option[RAttachmentMeta]] = { - val AC = RAttachment.Columns - val MC = RAttachmentMeta.Columns - val IC = RItem.Columns - - val q = - fr"SELECT" ++ commas( - MC.all.map(_.prefix("m").f) - ) ++ fr"FROM" ++ RItem.table ++ fr"i" ++ - fr"INNER JOIN" ++ RAttachment.table ++ fr"a ON" ++ IC.id - .prefix("i") - .is(AC.itemId.prefix("a")) ++ - fr"INNER JOIN" ++ RAttachmentMeta.table ++ fr"m ON" ++ AC.id - .prefix("a") - .is(MC.id.prefix("m")) ++ - fr"WHERE" ++ and( - AC.id.prefix("a").is(attachId), - IC.cid.prefix("i").is(collective) - ) + val q = Select( + select(am.all), + from(item) + .innerJoin(a, a.itemId === item.id) + .innerJoin(am, am.id === a.id), + a.id === attachId && item.cid === collective + ).build q.query[RAttachmentMeta].option } @@ -171,31 +158,16 @@ object QAttachment { def allAttachmentMetaAndName( coll: Option[Ident], chunkSize: Int - ): Stream[ConnectionIO, ContentAndName] = { - val aId = RAttachment.Columns.id.prefix("a") - val aItem = RAttachment.Columns.itemId.prefix("a") - val aName = RAttachment.Columns.name.prefix("a") - val mId = RAttachmentMeta.Columns.id.prefix("m") - val mContent = RAttachmentMeta.Columns.content.prefix("m") - val iId = RItem.Columns.id.prefix("i") - val iColl = RItem.Columns.cid.prefix("i") - val iFolder = RItem.Columns.folder.prefix("i") - 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" ++ Fragment.const(RCollective.T.tableName) ++ fr"c ON" ++ cId.is( - iColl - ) - - val where = coll.map(cid => iColl.is(cid)).getOrElse(Fragment.empty) - - selectSimple(cols, from, where) + ): Stream[ConnectionIO, ContentAndName] = + Select( + select(a.id, a.itemId, item.cid, item.folder, c.language, a.name, am.content), + from(a) + .innerJoin(am, am.id === a.id) + .innerJoin(item, item.id === a.itemId) + .innerJoin(c, c.id === item.cid) + ).where(coll.map(cid => item.cid === cid)) + .build .query[ContentAndName] .streamWithChunkSize(chunkSize) - } + } diff --git a/modules/store/src/main/scala/docspell/store/queries/QCollective.scala b/modules/store/src/main/scala/docspell/store/queries/QCollective.scala index 36361780..b9e8f74a 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QCollective.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QCollective.scala @@ -5,14 +5,20 @@ import fs2.Stream import docspell.common.ContactKind import docspell.common.{Direction, Ident} -import docspell.store.impl.Implicits._ -import docspell.store.qb.{GroupBy, Select} +import docspell.store.qb.DSL._ +import docspell.store.qb._ import docspell.store.records._ import doobie._ import doobie.implicits._ object QCollective { + private val ti = RTagItem.as("ti") + private val t = RTag.as("t") + private val ro = ROrganization.as("o") + private val rp = RPerson.as("p") + private val rc = RContact.as("c") + private val i = RItem.as("i") case class Names(org: Vector[String], pers: Vector[String], equip: Vector[String]) object Names { @@ -37,17 +43,16 @@ object QCollective { ) def getInsights(coll: Ident): ConnectionIO[InsightData] = { - val IC = RItem.Columns - val q0 = selectCount( - IC.id, - RItem.table, - and(IC.cid.is(coll), IC.incoming.is(Direction.incoming)) - ).query[Int].unique - val q1 = selectCount( - IC.id, - RItem.table, - and(IC.cid.is(coll), IC.incoming.is(Direction.outgoing)) - ).query[Int].unique + val q0 = Select( + count(i.id).s, + from(i), + i.cid === coll && i.incoming === Direction.incoming + ).build.query[Int].unique + val q1 = Select( + count(i.id).s, + from(i), + i.cid === coll && i.incoming === Direction.outgoing + ).build.query[Int].unique val fileSize = sql""" select sum(length) from ( @@ -78,10 +83,6 @@ object QCollective { } def tagCloud(coll: Ident): ConnectionIO[List[TagCount]] = { - import docspell.store.qb.DSL._ - - val ti = RTagItem.as("ti") - val t = RTag.as("t") val sql = Select( select(t.all).append(count(ti.itemId).s), @@ -97,13 +98,6 @@ object QCollective { query: Option[String], kind: Option[ContactKind] ): Stream[ConnectionIO, RContact] = { - import docspell.store.qb.DSL._ - import docspell.store.qb._ - - val ro = ROrganization.as("o") - val rp = RPerson.as("p") - val rc = RContact.as("c") - val orgCond = Select(select(ro.oid), from(ro), ro.cid === coll) val persCond = Select(select(rp.pid), from(rp), rp.cid === coll) val valueFilter = query.map(s => rc.value.like(s"%${s.toLowerCase}%")) diff --git a/modules/store/src/main/scala/docspell/store/queries/QCustomField.scala b/modules/store/src/main/scala/docspell/store/queries/QCustomField.scala index 0990a12b..b2923295 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QCustomField.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QCustomField.scala @@ -29,45 +29,14 @@ object QCustomField { nameQuery: Option[String], fieldId: Option[Ident] ): Select = { -// val fId = RCustomField.Columns.id.prefix("f") -// val fColl = RCustomField.Columns.cid.prefix("f") -// val fName = RCustomField.Columns.name.prefix("f") -// val fLabel = RCustomField.Columns.label.prefix("f") -// val vField = RCustomFieldValue.Columns.field.prefix("v") -// -// val join = RCustomField.table ++ fr"f LEFT OUTER JOIN" ++ -// RCustomFieldValue.table ++ fr"v ON" ++ fId.is(vField) -// -// val cols = RCustomField.Columns.all.map(_.prefix("f")) :+ Column("COUNT(v.id)") -// -// val nameCond = nameQuery.map(QueryWildcard.apply) match { -// case Some(q) => -// or(fName.lowerLike(q), and(fLabel.isNotNull, fLabel.lowerLike(q))) -// case None => -// Fragment.empty -// } -// val fieldCond = fieldId match { -// case Some(id) => -// fId.is(id) -// case None => -// Fragment.empty -// } -// val cond = and(fColl.is(coll), nameCond, fieldCond) -// -// val group = NonEmptyList.fromList(RCustomField.Columns.all) match { -// case Some(nel) => groupBy(nel.map(_.prefix("f"))) -// case None => Fragment.empty -// } -// -// selectSimple(cols, join, cond) ++ group - val nameFilter = nameQuery.map { q => f.name.likes(q) || (f.label.isNotNull && f.label.like(q)) } Select( f.all.map(_.s).append(count(v.id).as("num")), - from(f).leftJoin(v, f.id === v.field), + from(f) + .leftJoin(v, f.id === v.field), f.cid === coll &&? nameFilter &&? fieldId.map(fid => f.id === fid), GroupBy(f.all) ) diff --git a/modules/store/src/main/scala/docspell/store/queries/QItem.scala b/modules/store/src/main/scala/docspell/store/queries/QItem.scala index 81bb78b0..5c9ca443 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala @@ -259,27 +259,6 @@ object QItem { ) } - case class Batch(offset: Int, limit: Int) { - def restrictLimitTo(n: Int): Batch = - Batch(offset, math.min(n, limit)) - - def next: Batch = - Batch(offset + limit, limit) - - def first: Batch = - Batch(0, limit) - } - - object Batch { - val all: Batch = Batch(0, Int.MaxValue) - - def page(n: Int, size: Int): Batch = - Batch(n * size, size) - - def limit(c: Int): Batch = - Batch(0, c) - } - private def findCustomFieldValuesForColl( coll: Ident, values: Seq[CustomValue] diff --git a/modules/store/src/main/scala/docspell/store/queries/QMails.scala b/modules/store/src/main/scala/docspell/store/queries/QMails.scala index 30d476af..b58740c7 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QMails.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QMails.scala @@ -3,8 +3,8 @@ package docspell.store.queries import cats.data.OptionT import docspell.common._ -import docspell.store.impl.Column -import docspell.store.impl.Implicits._ +import docspell.store.qb.DSL._ +import docspell.store.qb._ import docspell.store.records._ import doobie._ @@ -12,6 +12,11 @@ import doobie.implicits._ object QMails { + private val item = RItem.as("i") + private val smail = RSentMail.as("sm") + private val mailitem = RSentMailItem.as("mi") + private val user = RUser.as("u") + def delete(coll: Ident, mailId: Ident): ConnectionIO[Int] = (for { m <- OptionT(findMail(coll, mailId)) @@ -19,53 +24,28 @@ object QMails { n <- OptionT.liftF(RSentMail.delete(m._1.id)) } yield k + n).getOrElse(0) - def findMail(coll: Ident, mailId: Ident): ConnectionIO[Option[(RSentMail, Ident)]] = { - val iColl = RItem.Columns.cid.prefix("i") - val smail = RSentMail.as("m") - val mId = smail.id.column + def findMail(coll: Ident, mailId: Ident): ConnectionIO[Option[(RSentMail, Ident)]] = + partialFind + .where(smail.id === mailId && item.cid === coll) + .build + .query[(RSentMail, Ident)] + .option - val (cols, from) = partialFind - - val cond = Seq(mId.is(mailId), iColl.is(coll)) - - selectSimple(cols, from, and(cond)).query[(RSentMail, Ident)].option - } - - def findMails(coll: Ident, itemId: Ident): ConnectionIO[Vector[(RSentMail, Ident)]] = { - val smailitem = RSentMailItem.as("t") - val smail = RSentMail.as("m") - val iColl = RItem.Columns.cid.prefix("i") - val tItem = smailitem.itemId.column - val mCreated = smail.created.column - - val (cols, from) = partialFind - - val cond = Seq(tItem.is(itemId), iColl.is(coll)) - - (selectSimple(cols, from, and(cond)) ++ orderBy(mCreated.f) ++ fr"DESC") + def findMails(coll: Ident, itemId: Ident): ConnectionIO[Vector[(RSentMail, Ident)]] = + partialFind + .where(mailitem.itemId === itemId && item.cid === coll) + .orderBy(smail.created.desc) + .build .query[(RSentMail, Ident)] .to[Vector] - } - private def partialFind: (Seq[Column], Fragment) = { - val user = RUser.as("u") - val smailitem = RSentMailItem.as("t") - val smail = RSentMail.as("m") - val iId = RItem.Columns.id.prefix("i") - val tItem = smailitem.itemId.column - val tMail = smailitem.sentMailId.column - val mId = smail.id.column - val mUser = smail.uid.column - - val cols = smail.all.map(_.column) :+ user.login.column - val from = Fragment.const(smail.tableName) ++ fr"m INNER JOIN" ++ - Fragment.const(smailitem.tableName) ++ fr"t ON" ++ tMail.is(mId) ++ - fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++ - fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ user.uid.column.is( - mUser - ) - - (cols.toList, from) - } + private def partialFind: Select.SimpleSelect = + Select( + select(smail.all).append(user.login.s), + from(smail) + .innerJoin(mailitem, mailitem.sentMailId === smail.id) + .innerJoin(item, mailitem.itemId === item.id) + .innerJoin(user, user.uid === smail.uid) + ) } diff --git a/modules/store/src/main/scala/docspell/store/queries/QOrganization.scala b/modules/store/src/main/scala/docspell/store/queries/QOrganization.scala index 585d2fd0..d83e2a25 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QOrganization.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QOrganization.scala @@ -13,15 +13,15 @@ import doobie._ import doobie.implicits._ object QOrganization { + private val p = RPerson.as("p") + private val c = RContact.as("c") + private val org = ROrganization.as("o") def findOrgAndContact( coll: Ident, query: Option[String], order: ROrganization.Table => Column[_] ): Stream[ConnectionIO, (ROrganization, Vector[RContact])] = { - val org = ROrganization.as("o") - val c = RContact.as("c") - val valFilter = query.map { q => val v = s"%$q%" c.value.like(v) || org.name.like(v) || org.notes.like(v) @@ -46,9 +46,6 @@ object QOrganization { coll: Ident, orgId: Ident ): ConnectionIO[Option[(ROrganization, Vector[RContact])]] = { - val org = ROrganization.as("o") - val c = RContact.as("c") - val sql = run( select(org.all, c.all), from(org).leftJoin(c, c.orgId === org.oid), @@ -72,19 +69,16 @@ object QOrganization { query: Option[String], order: RPerson.Table => Column[_] ): Stream[ConnectionIO, (RPerson, Option[ROrganization], Vector[RContact])] = { - val pers = RPerson.as("p") - val org = ROrganization.as("o") - val c = RContact.as("c") val valFilter = query .map(s => s"%$s%") - .map(v => c.value.like(v) || pers.name.like(v) || pers.notes.like(v)) + .map(v => c.value.like(v) || p.name.like(v) || p.notes.like(v)) val sql = Select( - select(pers.all, org.all, c.all), - from(pers) - .leftJoin(org, org.oid === pers.oid) - .leftJoin(c, c.personId === pers.pid), - pers.cid === coll &&? valFilter - ).orderBy(order(pers)) + select(p.all, org.all, c.all), + from(p) + .leftJoin(org, org.oid === p.oid) + .leftJoin(c, c.personId === p.pid), + p.cid === coll &&? valFilter + ).orderBy(order(p)) sql.build .query[(RPerson, Option[ROrganization], Option[RContact])] @@ -101,16 +95,13 @@ object QOrganization { coll: Ident, persId: Ident ): ConnectionIO[Option[(RPerson, Option[ROrganization], Vector[RContact])]] = { - val pers = RPerson.as("p") - val org = ROrganization.as("o") - val c = RContact.as("c") val sql = run( - select(pers.all, org.all, c.all), - from(pers) - .leftJoin(org, pers.oid === org.oid) - .leftJoin(c, c.personId === pers.pid), - pers.cid === coll && pers.pid === persId + select(p.all, org.all, c.all), + from(p) + .leftJoin(org, p.oid === org.oid) + .leftJoin(c, c.personId === p.pid), + p.cid === coll && p.pid === persId ) sql @@ -131,9 +122,7 @@ object QOrganization { value: String, ck: Option[ContactKind], concerning: Option[Boolean] - ): Stream[ConnectionIO, RPerson] = { - val p = RPerson.as("p") - val c = RContact.as("c") + ): Stream[ConnectionIO, RPerson] = runDistinct( select(p.all), from(p).innerJoin(c, c.personId === p.pid), @@ -141,7 +130,6 @@ object QOrganization { concerning.map(c => p.concerning === c) &&? ck.map(k => c.kind === k) ).query[RPerson].stream - } def addOrg[F[_]]( org: ROrganization,