From 71985244f19229af5968168c8ff2bd9c064d0f99 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Tue, 2 Mar 2021 22:14:26 +0100 Subject: [PATCH] Use a better representation for macros --- .../main/scala/docspell/query/ItemQuery.scala | 20 ++++++ .../docspell/query/internal/ExprUtil.scala | 7 ++- .../docspell/query/internal/MacroParser.scala | 62 +++++-------------- .../query/internal/MacroParserTest.scala | 16 ++--- .../qb/generator/ItemQueryGenerator.scala | 9 ++- 5 files changed, 53 insertions(+), 61 deletions(-) diff --git a/modules/query/src/main/scala/docspell/query/ItemQuery.scala b/modules/query/src/main/scala/docspell/query/ItemQuery.scala index 8160da7d..acf2307a 100644 --- a/modules/query/src/main/scala/docspell/query/ItemQuery.scala +++ b/modules/query/src/main/scala/docspell/query/ItemQuery.scala @@ -108,6 +108,26 @@ object ItemQuery { final case class Fulltext(query: String) extends Expr + // things that can be expressed with terms above + sealed trait MacroExpr extends Expr { + def body: Expr + } + case class NamesMacro(searchTerm: String) extends MacroExpr { + val body = + Expr.or( + like(Attr.ItemName, searchTerm), + like(Attr.ItemNotes, searchTerm), + like(Attr.Correspondent.OrgName, searchTerm), + like(Attr.Correspondent.PersonName, searchTerm), + like(Attr.Concerning.PersonName, searchTerm), + like(Attr.Concerning.EquipName, searchTerm) + ) + } + case class DateRangeMacro(attr: DateAttr, left: Date, right: Date) extends MacroExpr { + val body = + and(date(Operator.Gte, attr, left), date(Operator.Lte, attr, right)) + } + def or(expr0: Expr, exprs: Expr*): OrExpr = OrExpr(Nel.of(expr0, exprs: _*)) diff --git a/modules/query/src/main/scala/docspell/query/internal/ExprUtil.scala b/modules/query/src/main/scala/docspell/query/internal/ExprUtil.scala index 62489247..a1fa2045 100644 --- a/modules/query/src/main/scala/docspell/query/internal/ExprUtil.scala +++ b/modules/query/src/main/scala/docspell/query/internal/ExprUtil.scala @@ -5,8 +5,8 @@ import docspell.query.ItemQuery._ object ExprUtil { - /** Does some basic transformation, like unfolding deeply nested and - * trees containing one value etc. + /** Does some basic transformation, like unfolding nested and trees + * containing one value etc. */ def reduce(expr: Expr): Expr = expr match { @@ -30,6 +30,9 @@ object ExprUtil { expr } + case m: MacroExpr => + reduce(m.body) + case DirectionExpr(_) => expr diff --git a/modules/query/src/main/scala/docspell/query/internal/MacroParser.scala b/modules/query/src/main/scala/docspell/query/internal/MacroParser.scala index 2a2c2d41..856a2d96 100644 --- a/modules/query/src/main/scala/docspell/query/internal/MacroParser.scala +++ b/modules/query/src/main/scala/docspell/query/internal/MacroParser.scala @@ -5,61 +5,29 @@ import cats.parse.{Parser => P} import docspell.query.ItemQuery._ object MacroParser { - private[this] val macroDef: P[String] = - P.char('$') *> BasicParser.identParser <* P.char(':') + private def macroDef(name: String): P[Unit] = + P.char('$').soft.with1 *> P.string(name) <* P.char(':') - def parser[A](macros: Map[String, P[A]]): P[A] = { - val p: P[P[A]] = macroDef.map { name => - macros - .get(name) - .getOrElse(P.failWith(s"Unknown macro: $name")) + private def dateRangeMacroImpl( + name: String, + attr: Attr.DateAttr + ): P[Expr.DateRangeMacro] = + (macroDef(name) *> DateParser.dateRange).map { case (left, right) => + Expr.DateRangeMacro(attr, left, right) } - val px = (p ~ P.index ~ BasicParser.singleString).map { case ((pexpr, index), str) => - pexpr - .parseAll(str) - .left - .map(err => err.copy(failedAtOffset = err.failedAtOffset + index)) - } + val namesMacro: P[Expr.NamesMacro] = + (macroDef("names") *> BasicParser.singleString).map(Expr.NamesMacro.apply) - P.select(px)(P.Fail) - } + val dateRangeMacro: P[Expr.DateRangeMacro] = + dateRangeMacroImpl("datein", Attr.Date) - // --- definitions of available macros - - /** Expands in an OR expression that matches name fields of item and - * correspondent/concerning metadata. - */ - val names: P[Expr] = - P.string(P.anyChar.rep.void).map { input => - Expr.or( - Expr.like(Attr.ItemName, input), - Expr.like(Attr.ItemNotes, input), - Expr.like(Attr.Correspondent.OrgName, input), - Expr.like(Attr.Correspondent.PersonName, input), - Expr.like(Attr.Concerning.PersonName, input), - Expr.like(Attr.Concerning.EquipName, input) - ) - } - - def dateRange(attr: Attr.DateAttr): P[Expr] = - DateParser.dateRange.map { case (left, right) => - Expr.and( - Expr.date(Operator.Gte, attr, left), - Expr.date(Operator.Lte, attr, right) - ) - } + val dueDateRangeMacro: P[Expr.DateRangeMacro] = + dateRangeMacroImpl("duein", Attr.DueDate) // --- all macro parser - val allMacros: Map[String, P[Expr]] = - Map( - "names" -> names, - "datein" -> dateRange(Attr.Date), - "duein" -> dateRange(Attr.DueDate) - ) - val all: P[Expr] = - parser(allMacros) + P.oneOf(List(namesMacro, dateRangeMacro, dueDateRangeMacro)) } diff --git a/modules/query/src/test/scala/docspell/query/internal/MacroParserTest.scala b/modules/query/src/test/scala/docspell/query/internal/MacroParserTest.scala index 045d1120..def772bd 100644 --- a/modules/query/src/test/scala/docspell/query/internal/MacroParserTest.scala +++ b/modules/query/src/test/scala/docspell/query/internal/MacroParserTest.scala @@ -1,19 +1,15 @@ package docspell.query.internal import munit._ -import cats.parse.{Parser => P} +//import cats.parse.{Parser => P} +import docspell.query.ItemQuery.Expr class MacroParserTest extends FunSuite { - test("fail with unkown macro names") { - val p = MacroParser.parser(Map.empty) - assert(p.parseAll("$bla:blup").isLeft) // TODO check error message + test("start with $") { + val p = MacroParser.namesMacro + assertEquals(p.parseAll("$names:test"), Right(Expr.NamesMacro("test"))) + assert(p.parseAll("names:test").isLeft) } - test("select correct parser") { - val p = - MacroParser.parser[Int](Map("one" -> P.anyChar.as(1), "two" -> P.anyChar.as(2))) - assertEquals(p.parseAll("$one:y"), Right(1)) - assertEquals(p.parseAll("$two:y"), Right(2)) - } } 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 1ed9e7df..18cdf909 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 @@ -145,7 +145,9 @@ object ItemQueryGenerator { } case Expr.CustomFieldMatch(field, op, value) => - tables.item.id.in(itemsWithCustomField(_.name ==== field)(coll, makeOp(op), value)) + tables.item.id.in( + itemsWithCustomField(_.name ==== field)(coll, makeOp(op), value) + ) case Expr.CustomFieldIdMatch(field, op, value) => tables.item.id.in(itemsWithCustomField(_.id ==== field)(coll, makeOp(op), value)) @@ -153,6 +155,9 @@ object ItemQueryGenerator { case Expr.Fulltext(_) => // not supported here Condition.unit + + case _: Expr.MacroExpr => + Condition.unit } private def dateToTimestamp(today: LocalDate)(date: Date): Timestamp = @@ -233,7 +238,7 @@ object ItemQueryGenerator { } private def itemsWithCustomField( - sel: RCustomField.Table => Condition + sel: RCustomField.Table => Condition )(coll: Ident, op: QOp, value: String): Select = { val cf = RCustomField.as("cf") val cfv = RCustomFieldValue.as("cfv")