Amend search results with attachment info

This uses again another query per item to retrieve some information
about each attachment already in the search results.
This commit is contained in:
Eike Kettner 2020-11-09 14:24:28 +01:00
parent a77f34b7ba
commit 8c08bf233d
3 changed files with 71 additions and 10 deletions

View File

@ -1291,10 +1291,11 @@ paths:
summary: Search for items. summary: Search for items.
description: | description: |
Search for items given a search form. The results are grouped Search for items given a search form. The results are grouped
by month and are sorted by item date (newest first). Tags are by month and are sorted by item date (newest first). Tags and
*not* resolved. The results will always contain an empty list attachments are *not* resolved. The results will always
for item tags. Use `/searchWithTags` to also retrieve all tags contain an empty list for item tags and attachments. Use
of an item. `/searchWithTags` to also retrieve all tags and a list of
attachments of an item.
The `fulltext` field can be used to restrict the results by The `fulltext` field can be used to restrict the results by
using full-text search in the documents contents. using full-text search in the documents contents.
@ -1318,9 +1319,10 @@ paths:
summary: Search for items. summary: Search for items.
description: | description: |
Search for items given a search form. The results are grouped Search for items given a search form. The results are grouped
by month by default. For each item, its tags are also by month by default. For each item, its tags and attachments
returned. This uses more queries and is therefore slower, but are also returned. This uses more queries and is therefore
returns all tags to an item. slower, but returns all tags to an item as well as their
attachments with some minor details.
The `fulltext` field can be used to restrict the results by The `fulltext` field can be used to restrict the results by
using full-text search in the documents contents. using full-text search in the documents contents.
@ -4703,6 +4705,10 @@ components:
fileCount: fileCount:
type: integer type: integer
format: int32 format: int32
attachments:
type: array
items:
$ref: "#/components/schemas/AttachmentLight"
tags: tags:
type: array type: array
items: items:
@ -4720,6 +4726,24 @@ components:
type: array type: array
items: items:
$ref: "#/components/schemas/HighlightEntry" $ref: "#/components/schemas/HighlightEntry"
AttachmentLight:
description: |
Some little details about an attachment.
required:
- id
- position
properties:
id:
type: string
format: ident
position:
type: integer
format: int32
name:
type: string
pageCount:
type: integer
format: int32
HighlightEntry: HighlightEntry:
description: | description: |
Highlighting information for a single field (maybe attachment Highlighting information for a single field (maybe attachment

View File

@ -22,6 +22,7 @@ import bitpeace.FileMeta
import org.http4s.headers.`Content-Type` import org.http4s.headers.`Content-Type`
import org.http4s.multipart.Multipart import org.http4s.multipart.Multipart
import org.log4s.Logger import org.log4s.Logger
import docspell.store.queries.QItem
trait Conversions { trait Conversions {
@ -204,6 +205,7 @@ trait Conversions {
i.folder.map(mkIdName), i.folder.map(mkIdName),
i.fileCount, i.fileCount,
Nil, Nil,
Nil,
i.notes, i.notes,
Nil Nil
) )
@ -215,7 +217,11 @@ trait Conversions {
} }
def mkItemLightWithTags(i: OItemSearch.ListItemWithTags): ItemLight = def mkItemLightWithTags(i: OItemSearch.ListItemWithTags): ItemLight =
mkItemLight(i.item).copy(tags = i.tags.map(mkTag)) mkItemLight(i.item)
.copy(tags = i.tags.map(mkTag), attachments = i.attachments.map(mkAttachmentLight))
private def mkAttachmentLight(qa: QItem.AttachmentLight): AttachmentLight =
AttachmentLight(qa.id, qa.position, qa.name, qa.pageCount)
def mkItemLightWithTags(i: OFulltext.FtsItemWithTags): ItemLight = { def mkItemLightWithTags(i: OFulltext.FtsItemWithTags): ItemLight = {
val il = mkItemLightWithTags(i.item) val il = mkItemLightWithTags(i.item)

View File

@ -443,7 +443,17 @@ object QItem {
from.query[ListItem].stream from.query[ListItem].stream
} }
case class ListItemWithTags(item: ListItem, tags: List[RTag]) case class AttachmentLight(
id: Ident,
position: Int,
name: Option[String],
pageCount: Option[Int]
)
case class ListItemWithTags(
item: ListItem,
tags: List[RTag],
attachments: List[AttachmentLight]
)
/** Same as `findItems` but resolves the tags for each item. Note that /** Same as `findItems` but resolves the tags for each item. Note that
* this is implemented by running an additional query per item. * this is implemented by running an additional query per item.
@ -476,8 +486,29 @@ object QItem {
item <- search item <- search
tagItems <- Stream.eval(RTagItem.findByItem(item.id)) tagItems <- Stream.eval(RTagItem.findByItem(item.id))
tags <- Stream.eval(tagItems.traverse(ti => findTag(resolvedTags, ti))) tags <- Stream.eval(tagItems.traverse(ti => findTag(resolvedTags, ti)))
attachs <- Stream.eval(findAttachmentLight(item.id))
ftags = tags.flatten.filter(t => t.collective == collective) ftags = tags.flatten.filter(t => t.collective == collective)
} yield ListItemWithTags(item, ftags.toList.sortBy(_.name)) } yield ListItemWithTags(
item,
ftags.toList.sortBy(_.name),
attachs.sortBy(_.position)
)
}
private def findAttachmentLight(item: Ident): ConnectionIO[List[AttachmentLight]] = {
val aId = RAttachment.Columns.id.prefix("a")
val aItem = RAttachment.Columns.itemId.prefix("a")
val aPos = RAttachment.Columns.position.prefix("a")
val aName = RAttachment.Columns.name.prefix("a")
val mId = RAttachmentMeta.Columns.id.prefix("m")
val mPages = RAttachmentMeta.Columns.pages.prefix("m")
val cols = Seq(aId, aPos, aName, mPages)
val join = RAttachment.table ++
fr"a LEFT OUTER JOIN" ++ RAttachmentMeta.table ++ fr"m ON" ++ aId.is(mId)
val cond = aItem.is(item)
selectSimple(cols, join, cond).query[AttachmentLight].to[List]
} }
def delete[F[_]: Sync](store: Store[F])(itemId: Ident, collective: Ident): F[Int] = def delete[F[_]: Sync](store: Store[F])(itemId: Ident, collective: Ident): F[Int] =