mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Allow to specify ordering when retrieving meta data
The query now searches in more fields. For example, when getting a list of tags, the query is applied to the tag name *and* category. When listing persons, the query now also looks in the associated organization name. This has been used to make some headers in the meta data tables clickable to sort the list accordingly. Refs: #965, #538
This commit is contained in:
@ -12,6 +12,7 @@ case class Column[A](name: String, table: TableDef) {
|
||||
|
||||
def cast[B]: Column[B] =
|
||||
this.asInstanceOf[Column[B]]
|
||||
|
||||
}
|
||||
|
||||
object Column {}
|
||||
|
@ -303,6 +303,12 @@ trait DSL extends DoobieMeta {
|
||||
def as(otherCol: Column[_]): SelectExpr =
|
||||
SelectExpr.SelectFun(dbf, Some(otherCol.name))
|
||||
|
||||
def asc: OrderBy =
|
||||
OrderBy(SelectExpr.SelectFun(dbf, None), OrderBy.OrderType.Asc)
|
||||
|
||||
def desc: OrderBy =
|
||||
OrderBy(SelectExpr.SelectFun(dbf, None), OrderBy.OrderType.Desc)
|
||||
|
||||
def ===[A](value: A)(implicit P: Put[A]): Condition =
|
||||
Condition.CompareFVal(dbf.s, Operator.Eq, value)
|
||||
|
||||
|
@ -135,6 +135,9 @@ object Select {
|
||||
|
||||
def orderBy(ob: OrderBy, obs: OrderBy*): Ordered =
|
||||
Ordered(this, ob, obs.toVector)
|
||||
|
||||
def orderBy(ob: Nel[OrderBy]): Ordered =
|
||||
Ordered(this, ob.head, ob.tail.toVector)
|
||||
}
|
||||
|
||||
case class RawSelect(fragment: Fragment) extends Select {
|
||||
|
@ -24,9 +24,10 @@ object QCustomField {
|
||||
|
||||
def findAllLike(
|
||||
coll: Ident,
|
||||
nameQuery: Option[String]
|
||||
nameQuery: Option[String],
|
||||
order: RCustomField.Table => Nel[OrderBy]
|
||||
): ConnectionIO[Vector[CustomFieldData]] =
|
||||
findFragment(coll, nameQuery, None).build.query[CustomFieldData].to[Vector]
|
||||
findFragment(coll, nameQuery, None, order).build.query[CustomFieldData].to[Vector]
|
||||
|
||||
def findById(field: Ident, collective: Ident): ConnectionIO[Option[CustomFieldData]] =
|
||||
findFragment(collective, None, field.some).build.query[CustomFieldData].option
|
||||
@ -34,7 +35,8 @@ object QCustomField {
|
||||
private def findFragment(
|
||||
coll: Ident,
|
||||
nameQuery: Option[String],
|
||||
fieldId: Option[Ident]
|
||||
fieldId: Option[Ident],
|
||||
order: RCustomField.Table => Nel[OrderBy] = t => Nel.of(t.name.asc)
|
||||
): Select = {
|
||||
val nameFilter = nameQuery.map { q =>
|
||||
f.name.likes(q) || (f.label.isNotNull && f.label.like(q))
|
||||
@ -46,7 +48,7 @@ object QCustomField {
|
||||
.leftJoin(v, f.id === v.field),
|
||||
f.cid === coll &&? nameFilter &&? fieldId.map(fid => f.id === fid),
|
||||
GroupBy(f.all)
|
||||
)
|
||||
).orderBy(order(f))
|
||||
}
|
||||
|
||||
final case class FieldValue(
|
||||
|
@ -7,6 +7,7 @@
|
||||
package docspell.store.queries
|
||||
|
||||
import cats.data.OptionT
|
||||
import cats.data.{NonEmptyList => Nel}
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.common._
|
||||
@ -156,8 +157,11 @@ object QFolder {
|
||||
).query[IdRef].to[Vector]
|
||||
|
||||
(for {
|
||||
folder <- OptionT(findAll(account, Some(id), None, None).map(_.headOption))
|
||||
memb <- OptionT.liftF(memberQ)
|
||||
folder <- OptionT(
|
||||
findAll(account, Some(id), None, None, (ft, _) => Nel.of(ft.name.asc))
|
||||
.map(_.headOption)
|
||||
)
|
||||
memb <- OptionT.liftF(memberQ)
|
||||
} yield folder.withMembers(memb.toList)).value
|
||||
}
|
||||
|
||||
@ -165,7 +169,8 @@ object QFolder {
|
||||
account: AccountId,
|
||||
idQ: Option[Ident],
|
||||
ownerLogin: Option[Ident],
|
||||
nameQ: Option[String]
|
||||
nameQ: Option[String],
|
||||
order: (RFolder.Table, RUser.Table) => Nel[OrderBy]
|
||||
): ConnectionIO[Vector[FolderItem]] = {
|
||||
// with memberlogin as
|
||||
// (select m.folder_id,u.login
|
||||
@ -239,7 +244,7 @@ object QFolder {
|
||||
nameQ.map(q => folder.name.like(s"%${q.toLowerCase}%")) &&?
|
||||
ownerLogin.map(login => user.login === login)
|
||||
)
|
||||
).orderBy(folder.name.asc)
|
||||
).orderBy(order(folder, user))
|
||||
).build.query[FolderItem].to[Vector]
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
package docspell.store.queries
|
||||
|
||||
import cats.data.NonEmptyList
|
||||
import cats.data.{NonEmptyList => Nel}
|
||||
import cats.implicits._
|
||||
import fs2._
|
||||
|
||||
@ -27,7 +27,7 @@ object QOrganization {
|
||||
def findOrgAndContact(
|
||||
coll: Ident,
|
||||
query: Option[String],
|
||||
order: ROrganization.Table => Column[_]
|
||||
order: ROrganization.Table => Nel[OrderBy]
|
||||
): Stream[ConnectionIO, (ROrganization, Vector[RContact])] = {
|
||||
val valFilter = query.map { q =>
|
||||
val v = s"%$q%"
|
||||
@ -74,18 +74,18 @@ object QOrganization {
|
||||
def findPersonAndContact(
|
||||
coll: Ident,
|
||||
query: Option[String],
|
||||
order: RPerson.Table => Column[_]
|
||||
order: (RPerson.Table, ROrganization.Table) => Nel[OrderBy]
|
||||
): Stream[ConnectionIO, (RPerson, Option[ROrganization], Vector[RContact])] = {
|
||||
val valFilter = query
|
||||
.map(s => s"%$s%")
|
||||
.map(v => c.value.like(v) || p.name.like(v) || p.notes.like(v))
|
||||
.map(v => c.value.like(v) || p.name.like(v) || org.name.like(v) || p.notes.like(v))
|
||||
val sql = Select(
|
||||
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))
|
||||
).orderBy(order(p, org))
|
||||
|
||||
sql.build
|
||||
.query[(RPerson, Option[ROrganization], Option[RContact])]
|
||||
@ -128,7 +128,7 @@ object QOrganization {
|
||||
coll: Ident,
|
||||
value: String,
|
||||
ck: Option[ContactKind],
|
||||
use: Option[NonEmptyList[PersonUse]]
|
||||
use: Option[Nel[PersonUse]]
|
||||
): Stream[ConnectionIO, RPerson] =
|
||||
runDistinct(
|
||||
select(p.all),
|
||||
|
@ -87,7 +87,7 @@ object REquipment {
|
||||
def findAll(
|
||||
coll: Ident,
|
||||
nameQ: Option[String],
|
||||
order: Table => Column[_]
|
||||
order: Table => NonEmptyList[OrderBy]
|
||||
): ConnectionIO[Vector[REquipment]] = {
|
||||
val t = Table(None)
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
package docspell.store.records
|
||||
|
||||
import cats.Eq
|
||||
import cats.data.NonEmptyList
|
||||
import cats.data.{NonEmptyList => Nel}
|
||||
import fs2.Stream
|
||||
|
||||
import docspell.common.{IdRef, _}
|
||||
@ -52,7 +52,7 @@ object ROrganization {
|
||||
val shortName = Column[String]("short_name", this)
|
||||
val use = Column[OrgUse]("org_use", this)
|
||||
val all =
|
||||
NonEmptyList.of[Column[_]](
|
||||
Nel.of[Column[_]](
|
||||
oid,
|
||||
cid,
|
||||
name,
|
||||
@ -122,7 +122,7 @@ object ROrganization {
|
||||
def findLike(
|
||||
coll: Ident,
|
||||
orgName: String,
|
||||
use: NonEmptyList[OrgUse]
|
||||
use: Nel[OrgUse]
|
||||
): ConnectionIO[Vector[IdRef]] =
|
||||
run(
|
||||
select(T.oid, T.name),
|
||||
@ -163,7 +163,7 @@ object ROrganization {
|
||||
def findAllRef(
|
||||
coll: Ident,
|
||||
nameQ: Option[String],
|
||||
order: Table => Column[_]
|
||||
order: Table => Nel[OrderBy]
|
||||
): ConnectionIO[Vector[IdRef]] = {
|
||||
val nameFilter = nameQ.map(s =>
|
||||
T.name.like(s"%${s.toLowerCase}%") || T.shortName.like(s"%${s.toLowerCase}%")
|
||||
|
@ -7,7 +7,7 @@
|
||||
package docspell.store.records
|
||||
|
||||
import cats.Eq
|
||||
import cats.data.NonEmptyList
|
||||
import cats.data.{NonEmptyList => Nel}
|
||||
import cats.effect._
|
||||
import fs2.Stream
|
||||
|
||||
@ -52,7 +52,7 @@ object RPerson {
|
||||
val updated = Column[Timestamp]("updated", this)
|
||||
val oid = Column[Ident]("oid", this)
|
||||
val use = Column[PersonUse]("person_use", this)
|
||||
val all = NonEmptyList.of[Column[_]](
|
||||
val all = Nel.of[Column[_]](
|
||||
pid,
|
||||
cid,
|
||||
name,
|
||||
@ -122,7 +122,7 @@ object RPerson {
|
||||
def findLike(
|
||||
coll: Ident,
|
||||
personName: String,
|
||||
use: NonEmptyList[PersonUse]
|
||||
use: Nel[PersonUse]
|
||||
): ConnectionIO[Vector[IdRef]] =
|
||||
run(
|
||||
select(T.pid, T.name),
|
||||
@ -134,7 +134,7 @@ object RPerson {
|
||||
coll: Ident,
|
||||
contactKind: ContactKind,
|
||||
value: String,
|
||||
use: NonEmptyList[PersonUse]
|
||||
use: Nel[PersonUse]
|
||||
): ConnectionIO[Vector[IdRef]] = {
|
||||
val p = RPerson.as("p")
|
||||
val c = RContact.as("c")
|
||||
@ -162,7 +162,7 @@ object RPerson {
|
||||
def findAllRef(
|
||||
coll: Ident,
|
||||
nameQ: Option[String],
|
||||
order: Table => Column[_]
|
||||
order: Table => Nel[OrderBy]
|
||||
): ConnectionIO[Vector[IdRef]] = {
|
||||
|
||||
val nameFilter = nameQ.map(s => T.name.like(s"%${s.toLowerCase}%"))
|
||||
@ -176,7 +176,7 @@ object RPerson {
|
||||
DML.delete(T, T.pid === personId && T.cid === coll)
|
||||
|
||||
def findOrganization(ids: Set[Ident]): ConnectionIO[Vector[PersonRef]] =
|
||||
NonEmptyList.fromList(ids.toList) match {
|
||||
Nel.fromList(ids.toList) match {
|
||||
case Some(nel) =>
|
||||
run(select(T.pid, T.name, T.oid), from(T), T.pid.in(nel))
|
||||
.query[PersonRef]
|
||||
|
@ -75,10 +75,11 @@ object RTag {
|
||||
|
||||
def findAll(
|
||||
coll: Ident,
|
||||
nameQ: Option[String],
|
||||
order: Table => Column[_]
|
||||
query: Option[String],
|
||||
order: Table => NonEmptyList[OrderBy]
|
||||
): ConnectionIO[Vector[RTag]] = {
|
||||
val nameFilter = nameQ.map(s => T.name.like(s"%${s.toLowerCase}%"))
|
||||
val nameFilter =
|
||||
query.map(_.toLowerCase).map(s => T.name.like(s"%$s%") || T.category.like(s"%$s%"))
|
||||
val sql =
|
||||
Select(select(T.all), from(T), T.cid === coll &&? nameFilter).orderBy(order(T))
|
||||
sql.build.query[RTag].to[Vector]
|
||||
|
Reference in New Issue
Block a user