From c5c7f7ed3bf7f55b0a7088211b3b01b9224d2717 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Tue, 8 Dec 2020 22:43:17 +0100 Subject: [PATCH] Convert equipment record --- .../scala/docspell/store/impl/Implicits.scala | 8 +- .../main/scala/docspell/store/qb/DSL.scala | 42 ++++++++- .../scala/docspell/store/queries/QItem.scala | 49 +++++----- .../docspell/store/records/REquipment.scala | 89 +++++++++++-------- 4 files changed, 127 insertions(+), 61 deletions(-) diff --git a/modules/store/src/main/scala/docspell/store/impl/Implicits.scala b/modules/store/src/main/scala/docspell/store/impl/Implicits.scala index 9e18b3e1..edafa832 100644 --- a/modules/store/src/main/scala/docspell/store/impl/Implicits.scala +++ b/modules/store/src/main/scala/docspell/store/impl/Implicits.scala @@ -1,3 +1,9 @@ package docspell.store.impl -object Implicits extends DoobieMeta with DoobieSyntax +object Implicits extends DoobieMeta with DoobieSyntax { + + implicit final class LegacySyntax(col: docspell.store.qb.Column[_]) { + def oldColumn: Column = + Column(col.name) + } +} diff --git a/modules/store/src/main/scala/docspell/store/qb/DSL.scala b/modules/store/src/main/scala/docspell/store/qb/DSL.scala index 63eb7083..4844b662 100644 --- a/modules/store/src/main/scala/docspell/store/qb/DSL.scala +++ b/modules/store/src/main/scala/docspell/store/qb/DSL.scala @@ -26,10 +26,28 @@ trait DSL extends DoobieMeta { DBFunction.Count(c, "cn") def and(c: Condition, cs: Condition*): Condition = - Condition.And(c, cs.toVector) + c match { + case Condition.And(head, tail) => + Condition.And(head, tail ++ (c +: cs.toVector)) + case _ => + Condition.And(c, cs.toVector) + } def or(c: Condition, cs: Condition*): Condition = - Condition.Or(c, cs.toVector) + c match { + case Condition.Or(head, tail) => + Condition.Or(head, tail ++ (c +: cs.toVector)) + case _ => + Condition.Or(c, cs.toVector) + } + + def not(c: Condition): Condition = + c match { + case Condition.Not(el) => + el + case _ => + Condition.Not(c) + } def where(c: Condition, cs: Condition*): Condition = and(c, cs: _*) @@ -71,7 +89,27 @@ trait DSL extends DoobieMeta { def ===(other: Column[A]): Condition = Condition.CompareCol(col, Operator.Eq, other) + } + implicit final class ConditionOps(c: Condition) { + + def &&(other: Condition): Condition = + and(c, other) + + def &&?(other: Option[Condition]): Condition = + other.map(ce => &&(ce)).getOrElse(c) + + def ||(other: Condition): Condition = + or(c, other) + + def ||?(other: Option[Condition]): Condition = + other.map(ce => ||(ce)).getOrElse(c) + + def negate: Condition = + not(c) + + def unary_! : Condition = + not(c) } } 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 494bf2b2..2207a29f 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala @@ -87,13 +87,14 @@ object QItem { } def findItem(id: Ident): ConnectionIO[Option[ItemData]] = { - val IC = RItem.Columns.all.map(_.prefix("i")) - val OC = ROrganization.Columns.all.map(_.prefix("o")) - val P0C = RPerson.Columns.all.map(_.prefix("p0")) - val P1C = RPerson.Columns.all.map(_.prefix("p1")) - val EC = REquipment.Columns.all.map(_.prefix("e")) - val ICC = List(RItem.Columns.id, RItem.Columns.name).map(_.prefix("ref")) - val FC = List(RFolder.Columns.id, RFolder.Columns.name).map(_.prefix("f")) + val equip = REquipment.as("e") + val IC = RItem.Columns.all.map(_.prefix("i")) + val OC = ROrganization.Columns.all.map(_.prefix("o")) + val P0C = RPerson.Columns.all.map(_.prefix("p0")) + val P1C = RPerson.Columns.all.map(_.prefix("p1")) + val EC = equip.all.map(_.oldColumn).map(_.prefix("e")) + val ICC = List(RItem.Columns.id, RItem.Columns.name).map(_.prefix("ref")) + val FC = List(RFolder.Columns.id, RFolder.Columns.name).map(_.prefix("f")) val cq = selectSimple( @@ -110,9 +111,11 @@ object QItem { fr"LEFT JOIN" ++ RPerson.table ++ fr"p1 ON" ++ RItem.Columns.concPerson .prefix("i") .is(RPerson.Columns.pid.prefix("p1")) ++ - fr"LEFT JOIN" ++ REquipment.table ++ fr"e ON" ++ RItem.Columns.concEquipment + fr"LEFT JOIN" ++ Fragment.const( + equip.tableName + ) ++ fr"e ON" ++ RItem.Columns.concEquipment .prefix("i") - .is(REquipment.Columns.eid.prefix("e")) ++ + .is(equip.eid.oldColumn.prefix("e")) ++ fr"LEFT JOIN" ++ RItem.table ++ fr"ref ON" ++ RItem.Columns.inReplyTo .prefix("i") .is(RItem.Columns.id.prefix("ref")) ++ @@ -305,16 +308,16 @@ object QItem { moreCols: Seq[Fragment], ctes: (String, Fragment)* ): Fragment = { + val equip = REquipment.as("e1") val IC = RItem.Columns val AC = RAttachment.Columns val PC = RPerson.Columns val OC = ROrganization.Columns - val EC = REquipment.Columns val FC = RFolder.Columns val itemCols = IC.all val personCols = List(PC.pid, PC.name) val orgCols = List(OC.oid, OC.name) - val equipCols = List(EC.eid, EC.name) + val equipCols = List(equip.eid.oldColumn, equip.name.oldColumn) val folderCols = List(FC.id, FC.name) val cvItem = RCustomFieldValue.Columns.itemId.prefix("cv") @@ -335,8 +338,8 @@ object QItem { PC.name.prefix("p0").f, PC.pid.prefix("p1").f, PC.name.prefix("p1").f, - EC.eid.prefix("e1").f, - EC.name.prefix("e1").f, + equip.eid.oldColumn.prefix("e1").f, + equip.name.oldColumn.prefix("e1").f, FC.id.prefix("f1").f, FC.name.prefix("f1").f, // sql uses 1 for first character @@ -357,7 +360,11 @@ object QItem { val withOrgs = selectSimple(orgCols, ROrganization.table, OC.cid.is(q.account.collective)) val withEquips = - selectSimple(equipCols, REquipment.table, EC.cid.is(q.account.collective)) + selectSimple( + equipCols, + Fragment.const(equip.tableName), + equip.cid.oldColumn.is(q.account.collective) + ) val withFolder = selectSimple(folderCols, RFolder.table, FC.collective.is(q.account.collective)) val withAttach = fr"SELECT COUNT(" ++ AC.id.f ++ fr") as num, " ++ AC.itemId.f ++ @@ -384,7 +391,7 @@ object QItem { fr"LEFT JOIN persons p1 ON" ++ IC.concPerson.prefix("i").is(PC.pid.prefix("p1")) ++ fr"LEFT JOIN equips e1 ON" ++ IC.concEquipment .prefix("i") - .is(EC.eid.prefix("e1")) ++ + .is(equip.eid.oldColumn.prefix("e1")) ++ fr"LEFT JOIN folders f1 ON" ++ IC.folder.prefix("i").is(FC.id.prefix("f1")) ++ (if (q.customValues.isEmpty) Fragment.empty else @@ -396,10 +403,10 @@ object QItem { maxNoteLen: Int, batch: Batch ): Stream[ConnectionIO, ListItem] = { - val IC = RItem.Columns - val PC = RPerson.Columns - val OC = ROrganization.Columns - val EC = REquipment.Columns + val equip = REquipment.as("e1") + val IC = RItem.Columns + val PC = RPerson.Columns + val OC = ROrganization.Columns // inclusive tags are AND-ed val tagSelectsIncl = q.tagsInclude @@ -432,7 +439,7 @@ object QItem { OC.name.prefix("o0").lowerLike(n), PC.name.prefix("p0").lowerLike(n), PC.name.prefix("p1").lowerLike(n), - EC.name.prefix("e1").lowerLike(n), + equip.name.oldColumn.prefix("e1").lowerLike(n), IC.name.prefix("i").lowerLike(n), IC.notes.prefix("i").lowerLike(n) ) @@ -441,7 +448,7 @@ object QItem { RPerson.Columns.pid.prefix("p0").isOrDiscard(q.corrPerson), ROrganization.Columns.oid.prefix("o0").isOrDiscard(q.corrOrg), RPerson.Columns.pid.prefix("p1").isOrDiscard(q.concPerson), - REquipment.Columns.eid.prefix("e1").isOrDiscard(q.concEquip), + equip.eid.oldColumn.prefix("e1").isOrDiscard(q.concEquip), RFolder.Columns.id.prefix("f1").isOrDiscard(q.folder), if (q.tagsInclude.isEmpty && q.tagCategoryIncl.isEmpty) Fragment.empty else diff --git a/modules/store/src/main/scala/docspell/store/records/REquipment.scala b/modules/store/src/main/scala/docspell/store/records/REquipment.scala index 3a7f6d2f..c7542e41 100644 --- a/modules/store/src/main/scala/docspell/store/records/REquipment.scala +++ b/modules/store/src/main/scala/docspell/store/records/REquipment.scala @@ -1,8 +1,8 @@ package docspell.store.records 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._ @@ -16,70 +16,85 @@ case class REquipment( ) {} object REquipment { + final case class Table(alias: Option[String]) extends TableDef { + val tableName = "equipment" - val table = fr"equipment" - - object Columns { - val eid = Column("eid") - val cid = Column("cid") - val name = Column("name") - val created = Column("created") - val updated = Column("updated") + val eid = Column[Ident]("eid", this) + val cid = Column[Ident]("cid", this) + val name = Column[String]("name", this) + val created = Column[Timestamp]("created", this) + val updated = Column[Timestamp]("updated", this) val all = List(eid, cid, name, created, updated) } - import Columns._ + + def as(alias: String): Table = + Table(Some(alias)) def insert(v: REquipment): ConnectionIO[Int] = { - val sql = - insertRow(table, all, fr"${v.eid},${v.cid},${v.name},${v.created},${v.updated}") - sql.update.run + val t = Table(None) + DML + .insert( + t, + t.all, + fr"${v.eid},${v.cid},${v.name},${v.created},${v.updated}" + ) + .update + .run } def update(v: REquipment): ConnectionIO[Int] = { - def sql(now: Timestamp) = - updateRow( - table, - and(eid.is(v.eid), cid.is(v.cid)), - commas( - cid.setTo(v.cid), - name.setTo(v.name), - updated.setTo(now) - ) - ) + val t = Table(None) for { now <- Timestamp.current[ConnectionIO] - n <- sql(now).update.run + n <- DML + .update( + t, + where(t.eid === v.eid, t.cid === v.cid), + DML.set( + t.cid.setTo(v.cid), + t.name.setTo(v.name), + t.updated.setTo(now) + ) + ) } yield n } def existsByName(coll: Ident, ename: String): ConnectionIO[Boolean] = { - val sql = selectCount(eid, table, and(cid.is(coll), name.is(ename))) + val t = Table(None) + val sql = run(select(count(t.eid)), from(t), where(t.cid === coll, t.name === ename)) sql.query[Int].unique.map(_ > 0) } def findById(id: Ident): ConnectionIO[Option[REquipment]] = { - val sql = selectSimple(all, table, eid.is(id)) + val t = Table(None) + val sql = run(select(t.all), from(t), t.eid === id) sql.query[REquipment].option } def findAll( coll: Ident, nameQ: Option[String], - order: Columns.type => Column + order: Table => Column[_] ): ConnectionIO[Vector[REquipment]] = { - val q = Seq(cid.is(coll)) ++ (nameQ match { - case Some(str) => Seq(name.lowerLike(s"%${str.toLowerCase}%")) - case None => Seq.empty - }) - val sql = selectSimple(all, table, and(q)) ++ orderBy(order(Columns).f) + val t = Table(None) + + val q = t.cid === coll &&? nameQ + .map(str => s"%${str.toLowerCase}%") + .map(v => t.name.like(v)) + + val sql = Select(select(t.all), from(t), q).orderBy(order(t)).run sql.query[REquipment].to[Vector] } - def findLike(coll: Ident, equipName: String): ConnectionIO[Vector[IdRef]] = - selectSimple(List(eid, name), table, and(cid.is(coll), name.lowerLike(equipName))) + def findLike(coll: Ident, equipName: String): ConnectionIO[Vector[IdRef]] = { + val t = Table(None) + run(select(List(t.eid, t.name)), from(t), t.cid === coll && t.name.like(equipName)) .query[IdRef] .to[Vector] + } - def delete(id: Ident, coll: Ident): ConnectionIO[Int] = - deleteFrom(table, and(eid.is(id), cid.is(coll))).update.run + def delete(id: Ident, coll: Ident): ConnectionIO[Int] = { + val t = Table(None) + DML.delete(t, t.eid === id && t.cid === coll).update.run + } }