Add and change custom fields

This commit is contained in:
Eike Kettner
2020-11-16 12:39:49 +01:00
parent 248ad04dd0
commit 62313ab03a
12 changed files with 388 additions and 30 deletions

View File

@ -1,6 +1,7 @@
CREATE TABLE "custom_field" (
"id" varchar(254) not null primary key,
"name" varchar(254) not null,
"label" varchar(254),
"cid" varchar(254) not null,
"ftype" varchar(100) not null,
"created" timestamp not null,

View File

@ -1,5 +1,7 @@
package docspell.store.impl
import cats.data.NonEmptyList
import docspell.common.Timestamp
import doobie._
@ -7,6 +9,12 @@ import doobie.implicits._
trait DoobieSyntax {
def groupBy(c0: Column, cs: Column*): Fragment =
groupBy(NonEmptyList.of(c0, cs: _*))
def groupBy(cs: NonEmptyList[Column]): Fragment =
fr" GROUP BY (" ++ commas(cs.toList.map(_.f)) ++ fr")"
def coalesce(f0: Fragment, fs: Fragment*): Fragment =
sql" coalesce(" ++ commas(f0 :: fs.toList) ++ sql") "

View File

@ -0,0 +1,64 @@
package docspell.store.queries
import cats.data.NonEmptyList
import cats.implicits._
import docspell.common._
import docspell.store.impl.Column
import docspell.store.impl.Implicits._
import docspell.store.records._
import doobie._
import doobie.implicits._
object QCustomField {
case class CustomFieldData(field: RCustomField, usageCount: Int)
def findAllLike(
coll: Ident,
nameQuery: Option[String]
): ConnectionIO[Vector[CustomFieldData]] =
findFragment(coll, nameQuery, None).query[CustomFieldData].to[Vector]
def findById(field: Ident, collective: Ident): ConnectionIO[Option[CustomFieldData]] =
findFragment(collective, None, field.some).query[CustomFieldData].option
private def findFragment(
coll: Ident,
nameQuery: Option[String],
fieldId: Option[Ident]
): Fragment = {
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), 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
}
}

View File

@ -342,8 +342,8 @@ object QItem {
TagItemName.itemsWithTagOrCategory(q.tagsExclude, q.tagCategoryExcl)
val iFolder = IC.folder.prefix("i")
val name = q.name.map(_.toLowerCase).map(queryWildcard)
val allNames = q.allNames.map(_.toLowerCase).map(queryWildcard)
val name = q.name.map(_.toLowerCase).map(QueryWildcard.apply)
val allNames = q.allNames.map(_.toLowerCase).map(QueryWildcard.apply)
val cond = and(
IC.cid.prefix("i").is(q.account.collective),
IC.state.prefix("i").isOneOf(q.states),
@ -516,8 +516,9 @@ object QItem {
rn <- QAttachment.deleteItemAttachments(store)(itemId, collective)
tn <- store.transact(RTagItem.deleteItemTags(itemId))
mn <- store.transact(RSentMail.deleteByItem(itemId))
cf <- store.transact(RCustomFieldValue.deleteByItem(itemId))
n <- store.transact(RItem.deleteByIdAndCollective(itemId, collective))
} yield tn + rn + n + mn
} yield tn + rn + n + mn + cf
private def findByFileIdsQuery(
fileMetaIds: NonEmptyList[Ident],
@ -618,18 +619,6 @@ object QItem {
.to[Vector]
}
private def queryWildcard(value: String): String = {
def prefix(n: String) =
if (n.startsWith("*")) s"%${n.substring(1)}"
else n
def suffix(n: String) =
if (n.endsWith("*")) s"${n.dropRight(1)}%"
else n
prefix(suffix(value))
}
final case class NameAndNotes(
id: Ident,
collective: Ident,

View File

@ -0,0 +1,17 @@
package docspell.store.queries
object QueryWildcard {
def apply(value: String): String = {
def prefix(n: String) =
if (n.startsWith("*")) s"%${n.substring(1)}"
else n
def suffix(n: String) =
if (n.endsWith("*")) s"${n.dropRight(1)}%"
else n
prefix(suffix(value))
}
}

View File

@ -9,7 +9,8 @@ import doobie.implicits._
case class RCustomField(
id: Ident,
name: String,
name: Ident,
label: Option[String],
cid: Ident,
ftype: CustomFieldType,
created: Timestamp
@ -23,22 +24,49 @@ object RCustomField {
val id = Column("id")
val name = Column("name")
val label = Column("label")
val cid = Column("cid")
val ftype = Column("ftype")
val created = Column("created")
val all = List(id, name, cid, ftype, created)
val all = List(id, name, label, cid, ftype, created)
}
import Columns._
def insert(value: RCustomField): ConnectionIO[Int] = {
val sql = insertRow(
table,
Columns.all,
fr"${value.id},${value.name},${value.cid},${value.ftype},${value.created}"
fr"${value.id},${value.name},${value.label},${value.cid},${value.ftype},${value.created}"
)
sql.update.run
}
def exists(fname: Ident, coll: Ident): ConnectionIO[Boolean] =
???
def findById(fid: Ident, coll: Ident): ConnectionIO[Option[RCustomField]] =
selectSimple(all, table, and(id.is(fid), cid.is(coll))).query[RCustomField].option
def findByIdOrName(idOrName: Ident, coll: Ident): ConnectionIO[Option[RCustomField]] =
selectSimple(all, table, and(cid.is(coll), or(id.is(idOrName), name.is(idOrName))))
.query[RCustomField]
.option
def deleteById(fid: Ident, coll: Ident): ConnectionIO[Int] =
deleteFrom(table, and(id.is(fid), cid.is(coll))).update.run
def findAll(coll: Ident): ConnectionIO[Vector[RCustomField]] =
selectSimple(Columns.all, table, Columns.cid.is(coll)).query[RCustomField].to[Vector]
selectSimple(all, table, cid.is(coll)).query[RCustomField].to[Vector]
def update(value: RCustomField): ConnectionIO[Int] =
updateRow(
table,
and(id.is(value.id), cid.is(value.cid)),
commas(
name.setTo(value.name),
label.setTo(value.label),
ftype.setTo(value.ftype)
)
).update.run
}

View File

@ -39,4 +39,12 @@ object RCustomFieldValue {
sql.update.run
}
def countField(fieldId: Ident): ConnectionIO[Int] =
selectCount(Columns.id, table, Columns.field.is(fieldId)).query[Int].unique
def deleteByField(fieldId: Ident): ConnectionIO[Int] =
deleteFrom(table, Columns.field.is(fieldId)).update.run
def deleteByItem(item: Ident): ConnectionIO[Int] =
deleteFrom(table, Columns.itemId.is(item)).update.run
}

View File

@ -0,0 +1,21 @@
package docspell.store.queries
import minitest._
object QueryWildcardTest extends SimpleTestSuite {
test("replace prefix") {
assertEquals("%name", QueryWildcard("*name"))
assertEquals("%some more", QueryWildcard("*some more"))
}
test("replace suffix") {
assertEquals("name%", QueryWildcard("name*"))
assertEquals("some other name%", QueryWildcard("some other name*"))
}
test("replace both sides") {
assertEquals("%name%", QueryWildcard("*name*"))
assertEquals("%some other name%", QueryWildcard("*some other name*"))
}
}