From 4ca6dfccae8a788d6ebeef7194ffaeb1eb6cdc16 Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Tue, 15 Dec 2020 22:14:03 +0100
Subject: [PATCH] Get basic search summary

---
 .../docspell/backend/ops/OCollective.scala    |  4 ++--
 .../docspell/backend/ops/OItemSearch.scala    | 11 +++++++++
 .../docspell/store/queries/QCollective.scala  |  2 --
 .../scala/docspell/store/queries/QItem.scala  | 24 +++++++++++++++++++
 .../store/queries/SearchSummary.scala         |  3 +++
 .../docspell/store/queries/TagCount.scala     |  5 ++++
 6 files changed, 45 insertions(+), 4 deletions(-)
 create mode 100644 modules/store/src/main/scala/docspell/store/queries/SearchSummary.scala
 create mode 100644 modules/store/src/main/scala/docspell/store/queries/TagCount.scala

diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala b/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala
index a4f06986..f68be8b7 100644
--- a/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala
+++ b/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala
@@ -66,8 +66,8 @@ trait OCollective[F[_]] {
 
 object OCollective {
 
-  type TagCount = QCollective.TagCount
-  val TagCount = QCollective.TagCount
+  type TagCount = docspell.store.queries.TagCount
+  val TagCount = docspell.store.queries.TagCount
 
   type InsightData = QCollective.InsightData
   val insightData = QCollective.InsightData
diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala b/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala
index ccfe6230..fbfa69d2 100644
--- a/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala
+++ b/modules/backend/src/main/scala/docspell/backend/ops/OItemSearch.scala
@@ -24,6 +24,8 @@ trait OItemSearch[F[_]] {
       maxNoteLen: Int
   )(q: Query, batch: Batch): F[Vector[ListItemWithTags]]
 
+  def findItemsSummary(q: Query): F[SearchSummary]
+
   def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]]
 
   def findAttachmentSource(
@@ -53,6 +55,9 @@ trait OItemSearch[F[_]] {
 
 object OItemSearch {
 
+  type SearchSummary = queries.SearchSummary
+  val SearchSummary = queries.SearchSummary
+
   type CustomValue = queries.CustomValue
   val CustomValue = queries.CustomValue
 
@@ -139,6 +144,12 @@ object OItemSearch {
           .toVector
       }
 
+      def findItemsSummary(q: Query): F[SearchSummary] =
+        for {
+          tags  <- store.transact(QItem.searchTagSummary(q))
+          count <- store.transact(QItem.searchCountSummary(q))
+        } yield SearchSummary(count, tags)
+
       def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
         store
           .transact(RAttachment.findByIdAndCollective(id, collective))
diff --git a/modules/store/src/main/scala/docspell/store/queries/QCollective.scala b/modules/store/src/main/scala/docspell/store/queries/QCollective.scala
index b9e8f74a..696d2294 100644
--- a/modules/store/src/main/scala/docspell/store/queries/QCollective.scala
+++ b/modules/store/src/main/scala/docspell/store/queries/QCollective.scala
@@ -33,8 +33,6 @@ object QCollective {
     } yield Names(orgs.map(_.name), pers.map(_.name), equp.map(_.name)))
       .getOrElse(Names.empty)
 
-  case class TagCount(tag: RTag, count: Int)
-
   case class InsightData(
       incoming: Int,
       outgoing: Int,
diff --git a/modules/store/src/main/scala/docspell/store/queries/QItem.scala b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
index 456fc69f..fab67704 100644
--- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala
+++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
@@ -234,6 +234,30 @@ object QItem {
     sql.query[ListItem].stream
   }
 
+  def searchTagSummary(q: Query): ConnectionIO[List[TagCount]] = {
+    val tagFrom =
+      from(ti)
+        .innerJoin(tag, tag.tid === ti.tagId)
+        .innerJoin(i, i.id === ti.itemId)
+
+    findItemsBase(q, 0)
+      .withSelect(select(tag.all).append(count(i.id).as("num")))
+      .changeFrom(_.prepend(tagFrom))
+      .changeWhere(c => c && queryCondition(q))
+      .groupBy(tag.tid)
+      .build
+      .query[TagCount]
+      .to[List]
+  }
+
+  def searchCountSummary(q: Query): ConnectionIO[Int] =
+    findItemsBase(q, 0)
+      .withSelect(Nel.of(count(i.id).as("num")))
+      .changeWhere(c => c && queryCondition(q))
+      .build
+      .query[Int]
+      .unique
+
   def findSelectedItems(
       q: Query,
       maxNoteLen: Int,
diff --git a/modules/store/src/main/scala/docspell/store/queries/SearchSummary.scala b/modules/store/src/main/scala/docspell/store/queries/SearchSummary.scala
new file mode 100644
index 00000000..606ec6ff
--- /dev/null
+++ b/modules/store/src/main/scala/docspell/store/queries/SearchSummary.scala
@@ -0,0 +1,3 @@
+package docspell.store.queries
+
+case class SearchSummary(count: Int, tags: List[TagCount])
diff --git a/modules/store/src/main/scala/docspell/store/queries/TagCount.scala b/modules/store/src/main/scala/docspell/store/queries/TagCount.scala
new file mode 100644
index 00000000..e392f889
--- /dev/null
+++ b/modules/store/src/main/scala/docspell/store/queries/TagCount.scala
@@ -0,0 +1,5 @@
+package docspell.store.queries
+
+import docspell.store.records.RTag
+
+case class TagCount(tag: RTag, count: Int)