mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 15:15:58 +00:00
Use an enum instead of a boolean to differentiate search
It's not very likely to have more modes of search besides normal and trashed, but got surprised in that way quite often and it's nicer this way anyways.
This commit is contained in:
parent
a7b74bd5ae
commit
edb344314f
@ -238,6 +238,10 @@ val openapiScalaSettings = Seq(
|
|||||||
field.copy(typeDef =
|
field.copy(typeDef =
|
||||||
TypeDef("EquipmentUse", Imports("docspell.common.EquipmentUse"))
|
TypeDef("EquipmentUse", Imports("docspell.common.EquipmentUse"))
|
||||||
)
|
)
|
||||||
|
case "searchmode" =>
|
||||||
|
field =>
|
||||||
|
field
|
||||||
|
.copy(typeDef = TypeDef("SearchMode", Imports("docspell.common.SearchMode")))
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,11 +87,11 @@ object OSimpleSearch {
|
|||||||
useFTS: Boolean,
|
useFTS: Boolean,
|
||||||
resolveDetails: Boolean,
|
resolveDetails: Boolean,
|
||||||
maxNoteLen: Int,
|
maxNoteLen: Int,
|
||||||
deleted: Boolean
|
searchMode: SearchMode
|
||||||
)
|
)
|
||||||
final case class StatsSettings(
|
final case class StatsSettings(
|
||||||
useFTS: Boolean,
|
useFTS: Boolean,
|
||||||
deleted: Boolean
|
searchMode: SearchMode
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed trait Items {
|
sealed trait Items {
|
||||||
@ -223,8 +223,10 @@ object OSimpleSearch {
|
|||||||
// 2. sql+fulltext if fulltextQuery.isDefined && q.nonEmpty && useFTS
|
// 2. sql+fulltext if fulltextQuery.isDefined && q.nonEmpty && useFTS
|
||||||
// 3. sql-only else (if fulltextQuery.isEmpty || !useFTS)
|
// 3. sql-only else (if fulltextQuery.isEmpty || !useFTS)
|
||||||
val validItemQuery =
|
val validItemQuery =
|
||||||
if (settings.deleted) q.withFix(_.andQuery(ItemQuery.Expr.Trashed))
|
settings.searchMode match {
|
||||||
else q.withFix(_.andQuery(ItemQuery.Expr.ValidItemStates))
|
case SearchMode.Trashed => q.withFix(_.andQuery(ItemQuery.Expr.Trashed))
|
||||||
|
case SearchMode.Normal => q.withFix(_.andQuery(ItemQuery.Expr.ValidItemStates))
|
||||||
|
}
|
||||||
fulltextQuery match {
|
fulltextQuery match {
|
||||||
case Some(ftq) if settings.useFTS =>
|
case Some(ftq) if settings.useFTS =>
|
||||||
if (q.isEmpty) {
|
if (q.isEmpty) {
|
||||||
@ -280,8 +282,10 @@ object OSimpleSearch {
|
|||||||
settings: StatsSettings
|
settings: StatsSettings
|
||||||
)(q: Query, fulltextQuery: Option[String]): F[SearchSummary] = {
|
)(q: Query, fulltextQuery: Option[String]): F[SearchSummary] = {
|
||||||
val validItemQuery =
|
val validItemQuery =
|
||||||
if (settings.deleted) q.withFix(_.andQuery(ItemQuery.Expr.Trashed))
|
settings.searchMode match {
|
||||||
else q.withFix(_.andQuery(ItemQuery.Expr.ValidItemStates))
|
case SearchMode.Trashed => q.withFix(_.andQuery(ItemQuery.Expr.Trashed))
|
||||||
|
case SearchMode.Normal => q.withFix(_.andQuery(ItemQuery.Expr.ValidItemStates))
|
||||||
|
}
|
||||||
fulltextQuery match {
|
fulltextQuery match {
|
||||||
case Some(ftq) if settings.useFTS =>
|
case Some(ftq) if settings.useFTS =>
|
||||||
if (q.isEmpty)
|
if (q.isEmpty)
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Docspell Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docspell.common
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
|
||||||
|
import io.circe.Decoder
|
||||||
|
import io.circe.Encoder
|
||||||
|
|
||||||
|
sealed trait SearchMode { self: Product =>
|
||||||
|
|
||||||
|
final def name: String =
|
||||||
|
productPrefix.toLowerCase
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SearchMode {
|
||||||
|
|
||||||
|
final case object Normal extends SearchMode
|
||||||
|
final case object Trashed extends SearchMode
|
||||||
|
|
||||||
|
def fromString(str: String): Either[String, SearchMode] =
|
||||||
|
str.toLowerCase match {
|
||||||
|
case "normal" => Right(Normal)
|
||||||
|
case "trashed" => Right(Trashed)
|
||||||
|
case _ => Left(s"Invalid search mode: $str")
|
||||||
|
}
|
||||||
|
|
||||||
|
val all: NonEmptyList[SearchMode] =
|
||||||
|
NonEmptyList.of(Normal, Trashed)
|
||||||
|
|
||||||
|
def unsafe(str: String): SearchMode =
|
||||||
|
fromString(str).fold(sys.error, identity)
|
||||||
|
|
||||||
|
implicit val jsonDecoder: Decoder[SearchMode] =
|
||||||
|
Decoder.decodeString.emap(fromString)
|
||||||
|
implicit val jsonEncoder: Encoder[SearchMode] =
|
||||||
|
Encoder.encodeString.contramap(_.name)
|
||||||
|
}
|
@ -1478,7 +1478,7 @@ paths:
|
|||||||
- $ref: "#/components/parameters/limit"
|
- $ref: "#/components/parameters/limit"
|
||||||
- $ref: "#/components/parameters/offset"
|
- $ref: "#/components/parameters/offset"
|
||||||
- $ref: "#/components/parameters/withDetails"
|
- $ref: "#/components/parameters/withDetails"
|
||||||
- $ref: "#/components/parameters/deleted"
|
- $ref: "#/components/parameters/searchMode"
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Ok
|
description: Ok
|
||||||
@ -4114,12 +4114,16 @@ components:
|
|||||||
withDetails:
|
withDetails:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
deleted:
|
searchMode:
|
||||||
type: boolean
|
type: string
|
||||||
default: false
|
format: searchmode
|
||||||
|
enum:
|
||||||
|
- normal
|
||||||
|
- trashed
|
||||||
|
default: normal
|
||||||
description: |
|
description: |
|
||||||
If this is true, the search performed only for
|
Specify whether the search query should apply to
|
||||||
soft-deleted items.
|
soft-deleted items or not.
|
||||||
query:
|
query:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
@ -5846,12 +5850,13 @@ components:
|
|||||||
description: Whether to return details to each item.
|
description: Whether to return details to each item.
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
deleted:
|
searchMode:
|
||||||
name: deleted
|
name: searchMode
|
||||||
in: query
|
in: query
|
||||||
description: Whether to search in soft-deleted items only.
|
description: Whether to search in soft-deleted items only.
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: string
|
||||||
|
format: searchmode
|
||||||
name:
|
name:
|
||||||
name: name
|
name: name
|
||||||
in: path
|
in: path
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package docspell.restserver.http4s
|
package docspell.restserver.http4s
|
||||||
|
|
||||||
import docspell.common.ContactKind
|
import docspell.common.ContactKind
|
||||||
|
import docspell.common.SearchMode
|
||||||
|
|
||||||
import org.http4s.ParseFailure
|
import org.http4s.ParseFailure
|
||||||
import org.http4s.QueryParamDecoder
|
import org.http4s.QueryParamDecoder
|
||||||
@ -23,6 +24,11 @@ object QueryParam {
|
|||||||
implicit val queryStringDecoder: QueryParamDecoder[QueryString] =
|
implicit val queryStringDecoder: QueryParamDecoder[QueryString] =
|
||||||
QueryParamDecoder[String].map(s => QueryString(s.trim.toLowerCase))
|
QueryParamDecoder[String].map(s => QueryString(s.trim.toLowerCase))
|
||||||
|
|
||||||
|
implicit val searchModeDecoder: QueryParamDecoder[SearchMode] =
|
||||||
|
QueryParamDecoder[String].emap(str =>
|
||||||
|
SearchMode.fromString(str).left.map(s => ParseFailure(str, s))
|
||||||
|
)
|
||||||
|
|
||||||
object FullOpt extends OptionalQueryParamDecoderMatcher[Boolean]("full")
|
object FullOpt extends OptionalQueryParamDecoderMatcher[Boolean]("full")
|
||||||
|
|
||||||
object OwningOpt extends OptionalQueryParamDecoderMatcher[Boolean]("owning")
|
object OwningOpt extends OptionalQueryParamDecoderMatcher[Boolean]("owning")
|
||||||
@ -35,7 +41,7 @@ object QueryParam {
|
|||||||
object Limit extends OptionalQueryParamDecoderMatcher[Int]("limit")
|
object Limit extends OptionalQueryParamDecoderMatcher[Int]("limit")
|
||||||
object Offset extends OptionalQueryParamDecoderMatcher[Int]("offset")
|
object Offset extends OptionalQueryParamDecoderMatcher[Int]("offset")
|
||||||
object WithDetails extends OptionalQueryParamDecoderMatcher[Boolean]("withDetails")
|
object WithDetails extends OptionalQueryParamDecoderMatcher[Boolean]("withDetails")
|
||||||
object Deleted extends OptionalQueryParamDecoderMatcher[Boolean]("deleted")
|
object SearchKind extends OptionalQueryParamDecoderMatcher[SearchMode]("searchMode")
|
||||||
|
|
||||||
object WithFallback extends OptionalQueryParamDecoderMatcher[Boolean]("withFallback")
|
object WithFallback extends OptionalQueryParamDecoderMatcher[Boolean]("withFallback")
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ object ItemRoutes {
|
|||||||
HttpRoutes.of {
|
HttpRoutes.of {
|
||||||
case GET -> Root / "search" :? QP.Query(q) :? QP.Limit(limit) :? QP.Offset(
|
case GET -> Root / "search" :? QP.Query(q) :? QP.Limit(limit) :? QP.Offset(
|
||||||
offset
|
offset
|
||||||
) :? QP.WithDetails(detailFlag) :? QP.Deleted(deletedFlag) =>
|
) :? QP.WithDetails(detailFlag) :? QP.SearchKind(searchMode) =>
|
||||||
val batch = Batch(offset.getOrElse(0), limit.getOrElse(cfg.maxItemPageSize))
|
val batch = Batch(offset.getOrElse(0), limit.getOrElse(cfg.maxItemPageSize))
|
||||||
.restrictLimitTo(cfg.maxItemPageSize)
|
.restrictLimitTo(cfg.maxItemPageSize)
|
||||||
val itemQuery = ItemQueryString(q)
|
val itemQuery = ItemQueryString(q)
|
||||||
@ -58,17 +58,17 @@ object ItemRoutes {
|
|||||||
cfg.fullTextSearch.enabled,
|
cfg.fullTextSearch.enabled,
|
||||||
detailFlag.getOrElse(false),
|
detailFlag.getOrElse(false),
|
||||||
cfg.maxNoteLength,
|
cfg.maxNoteLength,
|
||||||
deletedFlag.getOrElse(false)
|
searchMode.getOrElse(SearchMode.Normal)
|
||||||
)
|
)
|
||||||
val fixQuery = Query.Fix(user.account, None, None)
|
val fixQuery = Query.Fix(user.account, None, None)
|
||||||
searchItems(backend, dsl)(settings, fixQuery, itemQuery)
|
searchItems(backend, dsl)(settings, fixQuery, itemQuery)
|
||||||
|
|
||||||
case GET -> Root / "searchStats" :? QP.Query(q) :? QP.Deleted(deletedFlag) =>
|
case GET -> Root / "searchStats" :? QP.Query(q) :? QP.SearchKind(searchMode) =>
|
||||||
val itemQuery = ItemQueryString(q)
|
val itemQuery = ItemQueryString(q)
|
||||||
val fixQuery = Query.Fix(user.account, None, None)
|
val fixQuery = Query.Fix(user.account, None, None)
|
||||||
val settings = OSimpleSearch.StatsSettings(
|
val settings = OSimpleSearch.StatsSettings(
|
||||||
useFTS = cfg.fullTextSearch.enabled,
|
useFTS = cfg.fullTextSearch.enabled,
|
||||||
deleted = deletedFlag.getOrElse(false)
|
searchMode = searchMode.getOrElse(SearchMode.Normal)
|
||||||
)
|
)
|
||||||
searchItemStats(backend, dsl)(settings, fixQuery, itemQuery)
|
searchItemStats(backend, dsl)(settings, fixQuery, itemQuery)
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ object ItemRoutes {
|
|||||||
cfg.fullTextSearch.enabled,
|
cfg.fullTextSearch.enabled,
|
||||||
userQuery.withDetails.getOrElse(false),
|
userQuery.withDetails.getOrElse(false),
|
||||||
cfg.maxNoteLength,
|
cfg.maxNoteLength,
|
||||||
deleted = userQuery.deleted.getOrElse(false)
|
searchMode = userQuery.searchMode.getOrElse(SearchMode.Normal)
|
||||||
)
|
)
|
||||||
fixQuery = Query.Fix(user.account, None, None)
|
fixQuery = Query.Fix(user.account, None, None)
|
||||||
resp <- searchItems(backend, dsl)(settings, fixQuery, itemQuery)
|
resp <- searchItems(backend, dsl)(settings, fixQuery, itemQuery)
|
||||||
@ -100,7 +100,7 @@ object ItemRoutes {
|
|||||||
fixQuery = Query.Fix(user.account, None, None)
|
fixQuery = Query.Fix(user.account, None, None)
|
||||||
settings = OSimpleSearch.StatsSettings(
|
settings = OSimpleSearch.StatsSettings(
|
||||||
useFTS = cfg.fullTextSearch.enabled,
|
useFTS = cfg.fullTextSearch.enabled,
|
||||||
deleted = userQuery.deleted.getOrElse(false)
|
searchMode = userQuery.searchMode.getOrElse(SearchMode.Normal)
|
||||||
)
|
)
|
||||||
resp <- searchItemStats(backend, dsl)(settings, fixQuery, itemQuery)
|
resp <- searchItemStats(backend, dsl)(settings, fixQuery, itemQuery)
|
||||||
} yield resp
|
} yield resp
|
||||||
|
@ -79,7 +79,7 @@ request mq =
|
|||||||
, limit = Nothing
|
, limit = Nothing
|
||||||
, withDetails = Just True
|
, withDetails = Just True
|
||||||
, query = renderMaybe mq
|
, query = renderMaybe mq
|
||||||
, deleted = Just False
|
, searchMode = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user