mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-05 22:55:58 +00:00
Allow client to load items in batches
This commit is contained in:
parent
062618bf86
commit
e5b90eff34
@ -12,6 +12,7 @@ import OItem.{
|
|||||||
AttachmentArchiveData,
|
AttachmentArchiveData,
|
||||||
AttachmentData,
|
AttachmentData,
|
||||||
AttachmentSourceData,
|
AttachmentSourceData,
|
||||||
|
Batch,
|
||||||
ItemData,
|
ItemData,
|
||||||
ListItem,
|
ListItem,
|
||||||
Query
|
Query
|
||||||
@ -24,7 +25,7 @@ trait OItem[F[_]] {
|
|||||||
|
|
||||||
def findItem(id: Ident, collective: Ident): F[Option[ItemData]]
|
def findItem(id: Ident, collective: Ident): F[Option[ItemData]]
|
||||||
|
|
||||||
def findItems(q: Query, maxResults: Int): F[Vector[ListItem]]
|
def findItems(q: Query, batch: Batch): F[Vector[ListItem]]
|
||||||
|
|
||||||
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]]
|
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]]
|
||||||
|
|
||||||
@ -84,6 +85,9 @@ object OItem {
|
|||||||
type Query = QItem.Query
|
type Query = QItem.Query
|
||||||
val Query = QItem.Query
|
val Query = QItem.Query
|
||||||
|
|
||||||
|
type Batch = QItem.Batch
|
||||||
|
val Batch = QItem.Batch
|
||||||
|
|
||||||
type ListItem = QItem.ListItem
|
type ListItem = QItem.ListItem
|
||||||
val ListItem = QItem.ListItem
|
val ListItem = QItem.ListItem
|
||||||
|
|
||||||
@ -138,8 +142,11 @@ object OItem {
|
|||||||
.transact(QItem.findItem(id))
|
.transact(QItem.findItem(id))
|
||||||
.map(opt => opt.flatMap(_.filterCollective(collective)))
|
.map(opt => opt.flatMap(_.filterCollective(collective)))
|
||||||
|
|
||||||
def findItems(q: Query, maxResults: Int): F[Vector[ListItem]] =
|
def findItems(q: Query, batch: Batch): F[Vector[ListItem]] =
|
||||||
store.transact(QItem.findItems(q).take(maxResults.toLong)).compile.toVector
|
store
|
||||||
|
.transact(QItem.findItems(q, batch).take(batch.limit.toLong))
|
||||||
|
.compile
|
||||||
|
.toVector
|
||||||
|
|
||||||
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
|
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
|
||||||
store
|
store
|
||||||
|
@ -8,6 +8,7 @@ import emil.markdown._
|
|||||||
import emil.javamail.syntax._
|
import emil.javamail.syntax._
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
|
import docspell.backend.ops.OItem.Batch
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
import docspell.store.queries.QItem
|
import docspell.store.queries.QItem
|
||||||
import docspell.joex.scheduler.{Context, Task}
|
import docspell.joex.scheduler.{Context, Task}
|
||||||
@ -15,7 +16,7 @@ import cats.data.OptionT
|
|||||||
import docspell.joex.mail.EmilHeader
|
import docspell.joex.mail.EmilHeader
|
||||||
|
|
||||||
object NotifyDueItemsTask {
|
object NotifyDueItemsTask {
|
||||||
val maxItems: Long = 7
|
val maxItems: Int = 7
|
||||||
type Args = NotifyDueItemsArgs
|
type Args = NotifyDueItemsArgs
|
||||||
|
|
||||||
def apply[F[_]: Sync](cfg: MailSendConfig, emil: Emil[F]): Task[F, Args, Unit] =
|
def apply[F[_]: Sync](cfg: MailSendConfig, emil: Emil[F]): Task[F, Args, Unit] =
|
||||||
@ -78,7 +79,11 @@ object NotifyDueItemsTask {
|
|||||||
dueDateTo = Some(now + Duration.days(ctx.args.remindDays.toLong)),
|
dueDateTo = Some(now + Duration.days(ctx.args.remindDays.toLong)),
|
||||||
orderAsc = Some(_.dueDate)
|
orderAsc = Some(_.dueDate)
|
||||||
)
|
)
|
||||||
res <- ctx.store.transact(QItem.findItems(q).take(maxItems)).compile.toVector
|
res <-
|
||||||
|
ctx.store
|
||||||
|
.transact(QItem.findItems(q, Batch.limit(maxItems)).take(maxItems.toLong))
|
||||||
|
.compile
|
||||||
|
.toVector
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
def makeMail[F[_]: Sync](
|
def makeMail[F[_]: Sync](
|
||||||
|
@ -3121,6 +3121,8 @@ components:
|
|||||||
- tagsInclude
|
- tagsInclude
|
||||||
- tagsExclude
|
- tagsExclude
|
||||||
- inbox
|
- inbox
|
||||||
|
- offset
|
||||||
|
- limit
|
||||||
properties:
|
properties:
|
||||||
tagsInclude:
|
tagsInclude:
|
||||||
type: array
|
type: array
|
||||||
@ -3134,6 +3136,16 @@ components:
|
|||||||
format: ident
|
format: ident
|
||||||
inbox:
|
inbox:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
offset:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
description: |
|
||||||
|
The maximum number of results to return. Note that this
|
||||||
|
limit is a soft limit, there is some hard limit on the
|
||||||
|
server, too.
|
||||||
direction:
|
direction:
|
||||||
type: string
|
type: string
|
||||||
format: direction
|
format: direction
|
||||||
|
@ -4,6 +4,7 @@ import cats.effect._
|
|||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import docspell.backend.BackendApp
|
import docspell.backend.BackendApp
|
||||||
import docspell.backend.auth.AuthToken
|
import docspell.backend.auth.AuthToken
|
||||||
|
import docspell.backend.ops.OItem.Batch
|
||||||
import docspell.common.{Ident, ItemState}
|
import docspell.common.{Ident, ItemState}
|
||||||
import org.http4s.HttpRoutes
|
import org.http4s.HttpRoutes
|
||||||
import org.http4s.dsl.Http4sDsl
|
import org.http4s.dsl.Http4sDsl
|
||||||
@ -28,7 +29,10 @@ object ItemRoutes {
|
|||||||
_ <- logger.ftrace(s"Got search mask: $mask")
|
_ <- logger.ftrace(s"Got search mask: $mask")
|
||||||
query = Conversions.mkQuery(mask, user.account.collective)
|
query = Conversions.mkQuery(mask, user.account.collective)
|
||||||
_ <- logger.ftrace(s"Running query: $query")
|
_ <- logger.ftrace(s"Running query: $query")
|
||||||
items <- backend.item.findItems(query, 100)
|
items <- backend.item.findItems(
|
||||||
|
query,
|
||||||
|
Batch(mask.offset, mask.limit).restrictLimitTo(500)
|
||||||
|
)
|
||||||
resp <- Ok(Conversions.mkItemList(items))
|
resp <- Ok(Conversions.mkItemList(items))
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
|
@ -187,7 +187,22 @@ object QItem {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def findItems(q: Query): Stream[ConnectionIO, ListItem] = {
|
case class Batch(offset: Int, limit: Int) {
|
||||||
|
def restrictLimitTo(n: Int): Batch =
|
||||||
|
Batch(offset, math.min(n, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
def findItems(q: Query, batch: Batch): Stream[ConnectionIO, ListItem] = {
|
||||||
val IC = RItem.Columns
|
val IC = RItem.Columns
|
||||||
val AC = RAttachment.Columns
|
val AC = RAttachment.Columns
|
||||||
val PC = RPerson.Columns
|
val PC = RPerson.Columns
|
||||||
@ -202,7 +217,7 @@ object QItem {
|
|||||||
IC.id.prefix("i").f,
|
IC.id.prefix("i").f,
|
||||||
IC.name.prefix("i").f,
|
IC.name.prefix("i").f,
|
||||||
IC.state.prefix("i").f,
|
IC.state.prefix("i").f,
|
||||||
coalesce(IC.itemDate.prefix("i").f, IC.created.prefix("i").f),
|
coalesce(IC.itemDate.prefix("i").f, IC.created.prefix("i").f) ++ fr"i_date",
|
||||||
IC.dueDate.prefix("i").f,
|
IC.dueDate.prefix("i").f,
|
||||||
IC.source.prefix("i").f,
|
IC.source.prefix("i").f,
|
||||||
IC.incoming.prefix("i").f,
|
IC.incoming.prefix("i").f,
|
||||||
@ -310,11 +325,12 @@ object QItem {
|
|||||||
case Some(co) =>
|
case Some(co) =>
|
||||||
orderBy(coalesce(co(IC).prefix("i").f, IC.created.prefix("i").f) ++ fr"ASC")
|
orderBy(coalesce(co(IC).prefix("i").f, IC.created.prefix("i").f) ++ fr"ASC")
|
||||||
case None =>
|
case None =>
|
||||||
orderBy(
|
orderBy(fr"i_date DESC")
|
||||||
coalesce(IC.itemDate.prefix("i").f, IC.created.prefix("i").f) ++ fr"DESC"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
val frag = query ++ fr"WHERE" ++ cond ++ order
|
val frag =
|
||||||
|
query ++ fr"WHERE" ++ cond ++ order ++ (if (batch == Batch.all) Fragment.empty
|
||||||
|
else
|
||||||
|
fr"OFFSET ${batch.offset} LIMIT ${batch.limit}")
|
||||||
logger.trace(s"List items: $frag")
|
logger.trace(s"List items: $frag")
|
||||||
frag.query[ListItem].stream
|
frag.query[ListItem].stream
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,11 @@ update key flags msg model =
|
|||||||
doSearch : Flags -> Model -> ( Model, Cmd Msg )
|
doSearch : Flags -> Model -> ( Model, Cmd Msg )
|
||||||
doSearch flags model =
|
doSearch flags model =
|
||||||
let
|
let
|
||||||
mask =
|
smask =
|
||||||
Comp.SearchMenu.getItemSearch model.searchMenuModel
|
Comp.SearchMenu.getItemSearch model.searchMenuModel
|
||||||
|
|
||||||
|
mask =
|
||||||
|
{ smask | limit = 100 }
|
||||||
in
|
in
|
||||||
( { model | searchInProgress = True, viewMode = Listing }
|
( { model | searchInProgress = True, viewMode = Listing }
|
||||||
, Api.itemSearch flags mask ItemSearchResp
|
, Api.itemSearch flags mask ItemSearchResp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user