mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-06 15:15:58 +00:00
Use no-op fts-client if disabled + push this flag to the webui
This commit is contained in:
parent
330fdcdd5b
commit
cfe5aa8894
@ -60,7 +60,8 @@ object OFulltext {
|
|||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
def findItems(q: Query, ftsQ: String, batch: Batch): F[Vector[ListItem]] =
|
def findItems(q: Query, ftsQ: String, batch: Batch): F[Vector[ListItem]] =
|
||||||
findItemsFts(q, ftsQ, batch, itemSearch.findItems)
|
findItemsFts(q, ftsQ, batch.first, itemSearch.findItems)
|
||||||
|
.drop(batch.offset.toLong)
|
||||||
.take(batch.limit.toLong)
|
.take(batch.limit.toLong)
|
||||||
.compile
|
.compile
|
||||||
.toVector
|
.toVector
|
||||||
@ -70,28 +71,33 @@ object OFulltext {
|
|||||||
ftsQ: String,
|
ftsQ: String,
|
||||||
batch: Batch
|
batch: Batch
|
||||||
): F[Vector[ListItemWithTags]] =
|
): F[Vector[ListItemWithTags]] =
|
||||||
findItemsFts(q, ftsQ, batch, itemSearch.findItemsWithTags)
|
findItemsFts(q, ftsQ, batch.first, itemSearch.findItemsWithTags)
|
||||||
|
.drop(batch.offset.toLong)
|
||||||
.take(batch.limit.toLong)
|
.take(batch.limit.toLong)
|
||||||
.compile
|
.compile
|
||||||
.toVector
|
.toVector
|
||||||
|
|
||||||
private def findItemsFts[A](
|
private def findItemsFts[A: ItemId](
|
||||||
q: Query,
|
q: Query,
|
||||||
ftsQ: String,
|
ftsQ: String,
|
||||||
batch: Batch,
|
batch: Batch,
|
||||||
search: (Query, Batch) => F[Vector[A]]
|
search: (Query, Batch) => F[Vector[A]]
|
||||||
): Stream[F, A] = {
|
): Stream[F, A] = {
|
||||||
val fq = FtsQuery(ftsQ, q.collective, Nil, batch.limit, batch.offset)
|
|
||||||
|
val sqlResult = search(q, batch)
|
||||||
|
val fq = FtsQuery(ftsQ, q.collective, Set.empty, batch.limit, batch.offset)
|
||||||
|
|
||||||
val qres =
|
val qres =
|
||||||
for {
|
for {
|
||||||
items <-
|
items <- sqlResult
|
||||||
|
ids = items.map(a => ItemId[A].itemId(a))
|
||||||
|
ftsQ = fq.copy(items = ids.toSet)
|
||||||
|
ftsR <-
|
||||||
fts
|
fts
|
||||||
.search(fq)
|
.search(ftsQ)
|
||||||
.map(_.results.map(_.itemId))
|
.map(_.results.map(_.itemId))
|
||||||
.map(_.toSet)
|
.map(_.toSet)
|
||||||
sq = q.copy(itemIds = Some(items))
|
res = items.filter(a => ftsR.contains(ItemId[A].itemId(a)))
|
||||||
res <- search(sq, batch)
|
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
Stream.eval(qres).flatMap { v =>
|
Stream.eval(qres).flatMap { v =>
|
||||||
@ -100,6 +106,23 @@ object OFulltext {
|
|||||||
else results ++ findItemsFts(q, ftsQ, batch.next, search)
|
else results ++ findItemsFts(q, ftsQ, batch.next, search)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
trait ItemId[A] {
|
||||||
|
def itemId(a: A): Ident
|
||||||
|
}
|
||||||
|
object ItemId {
|
||||||
|
def apply[A](implicit ev: ItemId[A]): ItemId[A] = ev
|
||||||
|
|
||||||
|
def from[A](f: A => Ident): ItemId[A] =
|
||||||
|
new ItemId[A] {
|
||||||
|
def itemId(a: A) = f(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val listItemId: ItemId[ListItem] =
|
||||||
|
ItemId.from(_.id)
|
||||||
|
|
||||||
|
implicit val listItemWithTagsId: ItemId[ListItemWithTags] =
|
||||||
|
ItemId.from(_.item.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package docspell.ftsclient
|
package docspell.ftsclient
|
||||||
|
|
||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
|
import cats.implicits._
|
||||||
|
import cats.effect._
|
||||||
|
import org.log4s.getLogger
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
|
|
||||||
/** The fts client is the interface for docspell to a fulltext search
|
/** The fts client is the interface for docspell to a fulltext search
|
||||||
@ -90,3 +93,29 @@ trait FtsClient[F[_]] {
|
|||||||
def clear(logger: Logger[F], collective: Ident): F[Unit]
|
def clear(logger: Logger[F], collective: Ident): F[Unit]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object FtsClient {
|
||||||
|
|
||||||
|
def none[F[_]: Sync] =
|
||||||
|
new FtsClient[F] {
|
||||||
|
private[this] val logger = Logger.log4s[F](getLogger)
|
||||||
|
|
||||||
|
def initialize: F[Unit] =
|
||||||
|
logger.info("Full-text search is disabled!")
|
||||||
|
|
||||||
|
def search(q: FtsQuery): F[FtsResult] =
|
||||||
|
logger.warn("Full-text search is disabled!") *> FtsResult.empty.pure[F]
|
||||||
|
|
||||||
|
def updateIndex(logger: Logger[F], data: Stream[F, TextData]): F[Unit] =
|
||||||
|
logger.warn("Full-text search is disabled!")
|
||||||
|
|
||||||
|
def indexData(logger: Logger[F], data: Stream[F, TextData]): F[Unit] =
|
||||||
|
logger.warn("Full-text search is disabled!")
|
||||||
|
|
||||||
|
def clearAll(logger: Logger[F]): F[Unit] =
|
||||||
|
logger.warn("Full-text search is disabled!")
|
||||||
|
|
||||||
|
def clear(logger: Logger[F], collective: Ident): F[Unit] =
|
||||||
|
logger.warn("Full-text search is disabled!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ import docspell.common._
|
|||||||
final case class FtsQuery(
|
final case class FtsQuery(
|
||||||
q: String,
|
q: String,
|
||||||
collective: Ident,
|
collective: Ident,
|
||||||
items: List[Ident],
|
items: Set[Ident],
|
||||||
limit: Int,
|
limit: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
) {
|
) {
|
||||||
|
@ -14,6 +14,9 @@ final case class FtsResult(
|
|||||||
|
|
||||||
object FtsResult {
|
object FtsResult {
|
||||||
|
|
||||||
|
val empty =
|
||||||
|
FtsResult(Duration.millis(0), 0, 0.0, Map.empty, Nil)
|
||||||
|
|
||||||
sealed trait MatchData
|
sealed trait MatchData
|
||||||
case class AttachmentData(attachId: Ident) extends MatchData
|
case class AttachmentData(attachId: Ident) extends MatchData
|
||||||
case object ItemData extends MatchData
|
case object ItemData extends MatchData
|
||||||
|
@ -39,7 +39,7 @@ object QueryData {
|
|||||||
val items = fq.items.map(_.id).mkString(" ")
|
val items = fq.items.map(_.id).mkString(" ")
|
||||||
val collQ = s"""${Field.collectiveId.name}:"${fq.collective.id}""""
|
val collQ = s"""${Field.collectiveId.name}:"${fq.collective.id}""""
|
||||||
val filterQ = fq.items match {
|
val filterQ = fq.items match {
|
||||||
case Nil =>
|
case s if s.isEmpty =>
|
||||||
collQ
|
collQ
|
||||||
case _ =>
|
case _ =>
|
||||||
(collQ :: List(s"""${Field.itemId.name}:($items)""")).mkString(" AND ")
|
(collQ :: List(s"""${Field.itemId.name}:($items)""")).mkString(" AND ")
|
||||||
|
@ -3,6 +3,10 @@ package docspell.joex
|
|||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import emil.javamail._
|
import emil.javamail._
|
||||||
|
import fs2.concurrent.SignallingRef
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import org.http4s.client.Client
|
||||||
|
import org.http4s.client.blaze.BlazeClientBuilder
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.backend.ops._
|
import docspell.backend.ops._
|
||||||
import docspell.joex.hk._
|
import docspell.joex.hk._
|
||||||
@ -15,10 +19,8 @@ import docspell.joexapi.client.JoexClient
|
|||||||
import docspell.store.Store
|
import docspell.store.Store
|
||||||
import docspell.store.queue._
|
import docspell.store.queue._
|
||||||
import docspell.store.records.RJobLog
|
import docspell.store.records.RJobLog
|
||||||
|
import docspell.ftsclient.FtsClient
|
||||||
import docspell.ftssolr.SolrFtsClient
|
import docspell.ftssolr.SolrFtsClient
|
||||||
import fs2.concurrent.SignallingRef
|
|
||||||
import scala.concurrent.ExecutionContext
|
|
||||||
import org.http4s.client.blaze.BlazeClientBuilder
|
|
||||||
|
|
||||||
final class JoexAppImpl[F[_]: ConcurrentEffect: ContextShift: Timer](
|
final class JoexAppImpl[F[_]: ConcurrentEffect: ContextShift: Timer](
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
@ -78,7 +80,7 @@ object JoexAppImpl {
|
|||||||
nodeOps <- ONode(store)
|
nodeOps <- ONode(store)
|
||||||
joex <- OJoex(client, store)
|
joex <- OJoex(client, store)
|
||||||
upload <- OUpload(store, queue, cfg.files, joex)
|
upload <- OUpload(store, queue, cfg.files, joex)
|
||||||
fts <- SolrFtsClient(cfg.fullTextSearch.solr, httpClient)
|
fts <- createFtsClient(cfg)(httpClient)
|
||||||
javaEmil =
|
javaEmil =
|
||||||
JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug))
|
JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug))
|
||||||
sch <- SchedulerBuilder(cfg.scheduler, blocker, store)
|
sch <- SchedulerBuilder(cfg.scheduler, blocker, store)
|
||||||
@ -137,4 +139,10 @@ object JoexAppImpl {
|
|||||||
app = new JoexAppImpl(cfg, nodeOps, store, queue, pstore, termSignal, sch, psch)
|
app = new JoexAppImpl(cfg, nodeOps, store, queue, pstore, termSignal, sch, psch)
|
||||||
appR <- Resource.make(app.init.map(_ => app))(_.shutdown)
|
appR <- Resource.make(app.init.map(_ => app))(_.shutdown)
|
||||||
} yield appR
|
} yield appR
|
||||||
|
|
||||||
|
private def createFtsClient[F[_]: ConcurrentEffect: ContextShift](
|
||||||
|
cfg: Config
|
||||||
|
)(client: Client[F]): Resource[F, FtsClient[F]] =
|
||||||
|
if (cfg.fullTextSearch.enabled) SolrFtsClient(cfg.fullTextSearch.solr, client)
|
||||||
|
else Resource.pure[F, FtsClient[F]](FtsClient.none[F])
|
||||||
}
|
}
|
||||||
|
@ -39,5 +39,6 @@ object RestAppImpl {
|
|||||||
private def createFtsClient[F[_]: ConcurrentEffect: ContextShift](
|
private def createFtsClient[F[_]: ConcurrentEffect: ContextShift](
|
||||||
cfg: Config
|
cfg: Config
|
||||||
)(client: Client[F]): Resource[F, FtsClient[F]] =
|
)(client: Client[F]): Resource[F, FtsClient[F]] =
|
||||||
SolrFtsClient(cfg.fullTextSearch.solr, client)
|
if (cfg.fullTextSearch.enabled) SolrFtsClient(cfg.fullTextSearch.solr, client)
|
||||||
|
else Resource.pure[F, FtsClient[F]](FtsClient.none[F])
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ case class Flags(
|
|||||||
baseUrl: LenientUri,
|
baseUrl: LenientUri,
|
||||||
signupMode: SignupConfig.Mode,
|
signupMode: SignupConfig.Mode,
|
||||||
docspellAssetPath: String,
|
docspellAssetPath: String,
|
||||||
integrationEnabled: Boolean
|
integrationEnabled: Boolean,
|
||||||
|
fullTextSearchEnabled: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
object Flags {
|
object Flags {
|
||||||
@ -23,7 +24,8 @@ object Flags {
|
|||||||
cfg.baseUrl,
|
cfg.baseUrl,
|
||||||
cfg.backend.signup.mode,
|
cfg.backend.signup.mode,
|
||||||
s"/app/assets/docspell-webapp/${BuildInfo.version}",
|
s"/app/assets/docspell-webapp/${BuildInfo.version}",
|
||||||
cfg.integrationEndpoint.enabled
|
cfg.integrationEndpoint.enabled,
|
||||||
|
cfg.fullTextSearch.enabled
|
||||||
)
|
)
|
||||||
|
|
||||||
implicit val jsonEncoder: Encoder[Flags] =
|
implicit val jsonEncoder: Encoder[Flags] =
|
||||||
|
@ -199,6 +199,9 @@ object QItem {
|
|||||||
|
|
||||||
def next: Batch =
|
def next: Batch =
|
||||||
Batch(offset + limit, limit)
|
Batch(offset + limit, limit)
|
||||||
|
|
||||||
|
def first: Batch =
|
||||||
|
Batch(0, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Batch {
|
object Batch {
|
||||||
|
@ -15,6 +15,7 @@ type alias Config =
|
|||||||
, signupMode : String
|
, signupMode : String
|
||||||
, docspellAssetPath : String
|
, docspellAssetPath : String
|
||||||
, integrationEnabled : Bool
|
, integrationEnabled : Bool
|
||||||
|
, fullTextSearchEnabled : Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user