diff --git a/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala b/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala index 84d36d1f..24fed950 100644 --- a/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala +++ b/modules/store/src/main/scala/docspell/store/qb/generator/ItemQueryGenerator.scala @@ -171,12 +171,16 @@ object ItemQueryGenerator { tables.item.id.in(select.withSelect(Nel.of(RItem.as("i").id.s))) case Expr.AttachId(id) => - tables.item.id.in( - Select( - select(RAttachment.T.itemId), - from(RAttachment.T), + val idWildcard = QueryWildcard(id) + val query = + if (id == idWildcard) { RAttachment.T.id.cast[String] === id - ).distinct + } else { + RAttachment.T.id.cast[String].like(idWildcard) + } + + tables.item.id.in( + Select(select(RAttachment.T.itemId), from(RAttachment.T), query).distinct ) case Expr.Fulltext(_) => diff --git a/modules/store/src/test/scala/docspell/store/generator/ItemQueryGeneratorTest.scala b/modules/store/src/test/scala/docspell/store/generator/ItemQueryGeneratorTest.scala index db49f5cd..503cbc5a 100644 --- a/modules/store/src/test/scala/docspell/store/generator/ItemQueryGeneratorTest.scala +++ b/modules/store/src/test/scala/docspell/store/generator/ItemQueryGeneratorTest.scala @@ -11,6 +11,7 @@ import java.time.LocalDate import docspell.common._ import docspell.query.ItemQueryParser import docspell.store.qb.DSL._ +import docspell.store.qb.Select import docspell.store.qb.generator.{ItemQueryGenerator, Tables} import docspell.store.queries.AttachCountTable import docspell.store.records._ @@ -56,4 +57,31 @@ class ItemQueryGeneratorTest extends FunSuite { assertEquals(cond, expect) } + test("attach.id with wildcard") { + val q = ItemQueryParser.parseUnsafe("attach.id=abcde*") + val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q) + val expect = tables.item.id.in( + Select( + select(RAttachment.T.itemId), + from(RAttachment.T), + RAttachment.T.id.cast[String].like("abcde%") + ).distinct + ) + + assertEquals(cond, expect) + } + + test("attach.id with equals") { + val q = ItemQueryParser.parseUnsafe("attach.id=abcde") + val cond = ItemQueryGenerator(now, tables, Ident.unsafe("coll"))(q) + val expect = tables.item.id.in( + Select( + select(RAttachment.T.itemId), + from(RAttachment.T), + RAttachment.T.id.cast[String] === "abcde" + ).distinct + ) + + assertEquals(cond, expect) + } } diff --git a/website/site/content/docs/query/_index.md b/website/site/content/docs/query/_index.md index db17043a..03b1f89d 100644 --- a/website/site/content/docs/query/_index.md +++ b/website/site/content/docs/query/_index.md @@ -459,13 +459,16 @@ When negating, it finds all items that are not in a folder: The `attach.id` field is a special field to find items by providing the id of an attachment. This can be helpful in certain situations -when you only have the id of an attachment. It always uses equality, -so all other operators are not supported. +when you only have the id or part of that of an attachment. It uses +equality if no wildcard is present. A wildcard `*` can be used at +beginning or end if only a part of the id is known. ``` attach.id=5YjdnuTAdKJ-V6ofWTYsqKV-mAwB5aXTNWE-FAbeRU58qLb +attach.id=5YjdnuTAdKJ* ``` + # Shortcuts Shortcuts are only a short form of a longer query and are provided for