mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Correctly count tag categories
If multiple tags of the same category are applied to the same item, just summing tag counts will produce the wrong results as now items are counted multiple times.
This commit is contained in:
@ -71,6 +71,9 @@ object OCollective {
|
||||
type TagCount = docspell.store.queries.TagCount
|
||||
val TagCount = docspell.store.queries.TagCount
|
||||
|
||||
type CategoryCount = docspell.store.queries.CategoryCount
|
||||
val CategoryCount = docspell.store.queries.CategoryCount
|
||||
|
||||
type InsightData = QCollective.InsightData
|
||||
val insightData = QCollective.InsightData
|
||||
|
||||
|
@ -4242,6 +4242,7 @@ components:
|
||||
required:
|
||||
- count
|
||||
- tagCloud
|
||||
- tagCategoryCloud
|
||||
- fieldStats
|
||||
- folderStats
|
||||
properties:
|
||||
@ -4250,6 +4251,8 @@ components:
|
||||
format: int32
|
||||
tagCloud:
|
||||
$ref: "#/components/schemas/TagCloud"
|
||||
tagCategoryCloud:
|
||||
$ref: "#/components/schemas/NameCloud"
|
||||
fieldStats:
|
||||
type: array
|
||||
items:
|
||||
@ -4354,7 +4357,7 @@ components:
|
||||
$ref: "#/components/schemas/TagCount"
|
||||
TagCount:
|
||||
description: |
|
||||
Generic structure for counting something.
|
||||
Structure for counting tags.
|
||||
required:
|
||||
- tag
|
||||
- count
|
||||
@ -4364,6 +4367,30 @@ components:
|
||||
count:
|
||||
type: integer
|
||||
format: int32
|
||||
|
||||
NameCloud:
|
||||
description: |
|
||||
A set of counters.
|
||||
required:
|
||||
- items
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/NameCount"
|
||||
NameCount:
|
||||
description: |
|
||||
Generic structure for counting something.
|
||||
required:
|
||||
- name
|
||||
- count
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
count:
|
||||
type: integer
|
||||
format: int32
|
||||
|
||||
AttachmentMeta:
|
||||
description: |
|
||||
Extracted meta data of an attachment.
|
||||
|
@ -31,6 +31,7 @@ trait Conversions {
|
||||
SearchStats(
|
||||
sum.count,
|
||||
mkTagCloud(sum.tags),
|
||||
mkTagCategoryCloud(sum.cats),
|
||||
sum.fields.map(mkFieldStats),
|
||||
sum.folders.map(mkFolderStats)
|
||||
)
|
||||
@ -63,6 +64,9 @@ trait Conversions {
|
||||
def mkTagCloud(tags: List[OCollective.TagCount]) =
|
||||
TagCloud(tags.map(tc => TagCount(mkTag(tc.tag), tc.count)))
|
||||
|
||||
def mkTagCategoryCloud(tags: List[OCollective.CategoryCount]) =
|
||||
NameCloud(tags.map(tc => NameCount(tc.category, tc.count)))
|
||||
|
||||
// attachment meta
|
||||
def mkAttachmentMeta(rm: RAttachmentMeta): AttachmentMeta =
|
||||
AttachmentMeta(
|
||||
|
@ -9,11 +9,11 @@ object DBFunction {
|
||||
val countAll: DBFunction = CountAll
|
||||
|
||||
def countAs[A](column: Column[A]): DBFunction =
|
||||
Count(column)
|
||||
Count(column, false)
|
||||
|
||||
case object CountAll extends DBFunction
|
||||
|
||||
case class Count(column: Column[_]) extends DBFunction
|
||||
case class Count(column: Column[_], distinct: Boolean) extends DBFunction
|
||||
|
||||
case class Max(expr: SelectExpr) extends DBFunction
|
||||
|
||||
|
@ -63,7 +63,10 @@ trait DSL extends DoobieMeta {
|
||||
FromExpr.From(sel, alias)
|
||||
|
||||
def count(c: Column[_]): DBFunction =
|
||||
DBFunction.Count(c)
|
||||
DBFunction.Count(c, false)
|
||||
|
||||
def countDistinct(c: Column[_]): DBFunction =
|
||||
DBFunction.Count(c, true)
|
||||
|
||||
def countAll: DBFunction =
|
||||
DBFunction.CountAll
|
||||
|
@ -13,8 +13,9 @@ object DBFunctionBuilder extends CommonBuilder {
|
||||
case DBFunction.CountAll =>
|
||||
sql"COUNT(*)"
|
||||
|
||||
case DBFunction.Count(col) =>
|
||||
sql"COUNT(" ++ column(col) ++ fr")"
|
||||
case DBFunction.Count(col, distinct) =>
|
||||
if (distinct) sql"COUNT(DISTINCT " ++ column(col) ++ fr")"
|
||||
else sql"COUNT(" ++ column(col) ++ fr")"
|
||||
|
||||
case DBFunction.Max(expr) =>
|
||||
sql"MAX(" ++ SelectExprBuilder.build(expr) ++ fr")"
|
||||
|
@ -0,0 +1,3 @@
|
||||
package docspell.store.queries
|
||||
|
||||
final case class CategoryCount(category: String, count: Int)
|
@ -190,9 +190,38 @@ object QItem {
|
||||
for {
|
||||
count <- searchCountSummary(today)(q)
|
||||
tags <- searchTagSummary(today)(q)
|
||||
cats <- searchTagCategorySummary(today)(q)
|
||||
fields <- searchFieldSummary(today)(q)
|
||||
folders <- searchFolderSummary(today)(q)
|
||||
} yield SearchSummary(count, tags, fields, folders)
|
||||
} yield SearchSummary(count, tags, cats, fields, folders)
|
||||
|
||||
def searchTagCategorySummary(
|
||||
today: LocalDate
|
||||
)(q: Query): ConnectionIO[List[CategoryCount]] = {
|
||||
val tagFrom =
|
||||
from(ti)
|
||||
.innerJoin(tag, tag.tid === ti.tagId)
|
||||
.innerJoin(i, i.id === ti.itemId)
|
||||
|
||||
val tagCloud =
|
||||
findItemsBase(q.fix, today, 0).unwrap
|
||||
.withSelect(select(tag.category).append(countDistinct(i.id).as("num")))
|
||||
.changeFrom(_.prepend(tagFrom))
|
||||
.changeWhere(c => c && queryCondition(today, q.fix.account.collective, q.cond))
|
||||
.groupBy(tag.category)
|
||||
.build
|
||||
.query[CategoryCount]
|
||||
.to[List]
|
||||
|
||||
// the previous query starts from tags, so items with tag-count=0
|
||||
// are not included they are fetched separately
|
||||
for {
|
||||
existing <- tagCloud
|
||||
allCats <- RTag.listCategories(q.fix.account.collective)
|
||||
other = allCats.diff(existing.map(_.category))
|
||||
} yield existing ++ other.map(CategoryCount(_, 0))
|
||||
|
||||
}
|
||||
|
||||
def searchTagSummary(today: LocalDate)(q: Query): ConnectionIO[List[TagCount]] = {
|
||||
val tagFrom =
|
||||
|
@ -3,6 +3,7 @@ package docspell.store.queries
|
||||
case class SearchSummary(
|
||||
count: Int,
|
||||
tags: List[TagCount],
|
||||
cats: List[CategoryCount],
|
||||
fields: List[FieldStats],
|
||||
folders: List[FolderCount]
|
||||
)
|
||||
|
@ -2,4 +2,4 @@ package docspell.store.queries
|
||||
|
||||
import docspell.store.records.RTag
|
||||
|
||||
case class TagCount(tag: RTag, count: Int)
|
||||
final case class TagCount(tag: RTag, count: Int)
|
||||
|
Reference in New Issue
Block a user