diff --git a/modules/query/shared/src/main/scala/docspell/query/internal/ExprUtil.scala b/modules/query/shared/src/main/scala/docspell/query/internal/ExprUtil.scala index a1fa2045..3a82dd1f 100644 --- a/modules/query/shared/src/main/scala/docspell/query/internal/ExprUtil.scala +++ b/modules/query/shared/src/main/scala/docspell/query/internal/ExprUtil.scala @@ -1,5 +1,7 @@ package docspell.query.internal +import cats.data.{NonEmptyList => Nel} + import docspell.query.ItemQuery.Expr._ import docspell.query.ItemQuery._ @@ -11,12 +13,14 @@ object ExprUtil { def reduce(expr: Expr): Expr = expr match { case AndExpr(inner) => - if (inner.tail.isEmpty) reduce(inner.head) - else AndExpr(inner.map(reduce)) + val nodes = spliceAnd(inner) + if (nodes.tail.isEmpty) reduce(nodes.head) + else AndExpr(nodes.map(reduce)) case OrExpr(inner) => - if (inner.tail.isEmpty) reduce(inner.head) - else OrExpr(inner.map(reduce)) + val nodes = spliceOr(inner) + if (nodes.tail.isEmpty) reduce(nodes.head) + else OrExpr(nodes.map(reduce)) case NotExpr(inner) => inner match { @@ -62,4 +66,19 @@ object ExprUtil { case CustomFieldIdMatch(_, _, _) => expr } + + private def spliceAnd(nodes: Nel[Expr]): Nel[Expr] = + nodes.flatMap { + case Expr.AndExpr(inner) => + spliceAnd(inner) + case node => + Nel.of(node) + } + private def spliceOr(nodes: Nel[Expr]): Nel[Expr] = + nodes.flatMap { + case Expr.OrExpr(inner) => + spliceOr(inner) + case node => + Nel.of(node) + } } diff --git a/modules/query/shared/src/test/scala/docspell/query/FulltextExtractTest.scala b/modules/query/shared/src/test/scala/docspell/query/FulltextExtractTest.scala index 0c3b555a..8b1fe312 100644 --- a/modules/query/shared/src/test/scala/docspell/query/FulltextExtractTest.scala +++ b/modules/query/shared/src/test/scala/docspell/query/FulltextExtractTest.scala @@ -52,6 +52,6 @@ class FulltextExtractTest extends FunSuite { test("wrong fulltext search position") { assertFts("name:test (| date:2021-02 content:yes)", Result.UnsupportedPosition) - assertFts("name:test (& date:2021-02 content:yes)", Result.UnsupportedPosition) //TODO + assertFtsSuccess("name:test (& date:2021-02 content:yes)", "yes".some) } } diff --git a/modules/query/shared/src/test/scala/docspell/query/internal/ItemQueryParserTest.scala b/modules/query/shared/src/test/scala/docspell/query/internal/ItemQueryParserTest.scala index 61dfdf86..52e35fc4 100644 --- a/modules/query/shared/src/test/scala/docspell/query/internal/ItemQueryParserTest.scala +++ b/modules/query/shared/src/test/scala/docspell/query/internal/ItemQueryParserTest.scala @@ -1,5 +1,7 @@ package docspell.query.internal +import cats.implicits._ + import munit._ import docspell.query.ItemQueryParser import docspell.query.ItemQuery @@ -40,4 +42,20 @@ class ItemQueryParserTest extends FunSuite { val q = ItemQueryParser.parseUnsafe("") assertEquals(ItemQuery.all, q) } + + test("splice inner and nodes") { + val raw = "(& name:hello (& date:2021-02 name:world) (& name:hello) )" + val q = ItemQueryParser.parseUnsafe(raw) + val expect = + ItemQueryParser.parseUnsafe("name:hello date:2021-02 name:world name:hello") + assertEquals(expect.copy(raw = raw.some), q) + } + + test("splice inner or nodes") { + val raw = "(| name:hello (| date:2021-02 name:world) (| name:hello) )" + val q = ItemQueryParser.parseUnsafe(raw) + val expect = + ItemQueryParser.parseUnsafe("(| name:hello date:2021-02 name:world name:hello )") + assertEquals(expect.copy(raw = raw.some), q) + } }