mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
@ -8,7 +8,7 @@ package docspell.query
|
|||||||
|
|
||||||
import cats.data.{NonEmptyList => Nel}
|
import cats.data.{NonEmptyList => Nel}
|
||||||
|
|
||||||
import docspell.query.ItemQuery.Attr.{DateAttr, IntAttr, StringAttr}
|
import docspell.query.ItemQuery.Attr.{DateAttr, StringAttr}
|
||||||
|
|
||||||
/** A query evaluates to `true` or `false` given enough details about an item.
|
/** A query evaluates to `true` or `false` given enough details about an item.
|
||||||
*
|
*
|
||||||
@ -43,7 +43,7 @@ object ItemQuery {
|
|||||||
object Attr {
|
object Attr {
|
||||||
sealed trait StringAttr extends Attr
|
sealed trait StringAttr extends Attr
|
||||||
sealed trait DateAttr extends Attr
|
sealed trait DateAttr extends Attr
|
||||||
sealed trait IntAttr extends Attr
|
// sealed trait IntAttr extends Attr
|
||||||
|
|
||||||
case object ItemName extends StringAttr
|
case object ItemName extends StringAttr
|
||||||
case object ItemSource extends StringAttr
|
case object ItemSource extends StringAttr
|
||||||
@ -52,7 +52,6 @@ object ItemQuery {
|
|||||||
case object Date extends DateAttr
|
case object Date extends DateAttr
|
||||||
case object DueDate extends DateAttr
|
case object DueDate extends DateAttr
|
||||||
case object CreatedDate extends DateAttr
|
case object CreatedDate extends DateAttr
|
||||||
case object AttachCount extends IntAttr
|
|
||||||
|
|
||||||
object Correspondent {
|
object Correspondent {
|
||||||
case object OrgId extends StringAttr
|
case object OrgId extends StringAttr
|
||||||
@ -78,7 +77,7 @@ object ItemQuery {
|
|||||||
object Property {
|
object Property {
|
||||||
final case class StringProperty(attr: StringAttr, value: String) extends Property
|
final case class StringProperty(attr: StringAttr, value: String) extends Property
|
||||||
final case class DateProperty(attr: DateAttr, value: Date) extends Property
|
final case class DateProperty(attr: DateAttr, value: Date) extends Property
|
||||||
final case class IntProperty(attr: IntAttr, value: Int) extends Property
|
// final case class IntProperty(attr: IntAttr, value: Int) extends Property
|
||||||
|
|
||||||
def apply(sa: StringAttr, value: String): Property =
|
def apply(sa: StringAttr, value: String): Property =
|
||||||
StringProperty(sa, value)
|
StringProperty(sa, value)
|
||||||
@ -86,8 +85,8 @@ object ItemQuery {
|
|||||||
def apply(da: DateAttr, value: Date): Property =
|
def apply(da: DateAttr, value: Date): Property =
|
||||||
DateProperty(da, value)
|
DateProperty(da, value)
|
||||||
|
|
||||||
def apply(na: IntAttr, value: Int): Property =
|
// def apply(na: IntAttr, value: Int): Property =
|
||||||
IntProperty(na, value)
|
// IntProperty(na, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Expr {
|
sealed trait Expr {
|
||||||
|
@ -72,14 +72,8 @@ object AttrParser {
|
|||||||
val folderName: P[Attr.StringAttr] =
|
val folderName: P[Attr.StringAttr] =
|
||||||
P.ignoreCase(C.folder).as(Attr.Folder.FolderName)
|
P.ignoreCase(C.folder).as(Attr.Folder.FolderName)
|
||||||
|
|
||||||
val attachCountAttr: P[Attr.IntAttr] =
|
|
||||||
P.ignoreCase(C.attachCount).as(Attr.AttachCount)
|
|
||||||
|
|
||||||
// combining grouped by type
|
// combining grouped by type
|
||||||
|
|
||||||
val intAttr: P[Attr.IntAttr] =
|
|
||||||
attachCountAttr
|
|
||||||
|
|
||||||
val dateAttr: P[Attr.DateAttr] =
|
val dateAttr: P[Attr.DateAttr] =
|
||||||
P.oneOf(List(date, dueDate, created))
|
P.oneOf(List(date, dueDate, created))
|
||||||
|
|
||||||
@ -104,5 +98,5 @@ object AttrParser {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val anyAttr: P[Attr] =
|
val anyAttr: P[Attr] =
|
||||||
P.oneOf(List(dateAttr, stringAttr, intAttr))
|
P.oneOf(List(dateAttr, stringAttr))
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ package docspell.query.internal
|
|||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
|
|
||||||
val attachCount = "attach.count"
|
// val attachCount = "attach.count"
|
||||||
val attachId = "attach.id"
|
val attachId = "attach.id"
|
||||||
val cat = "cat"
|
val cat = "cat"
|
||||||
val checksum = "checksum"
|
val checksum = "checksum"
|
||||||
|
@ -83,8 +83,8 @@ object ExprString {
|
|||||||
Right(s"${stringAttr(attr)}${opStr(op)}${quote(value)}")
|
Right(s"${stringAttr(attr)}${opStr(op)}${quote(value)}")
|
||||||
case Property.DateProperty(attr, value) =>
|
case Property.DateProperty(attr, value) =>
|
||||||
Right(s"${dateAttr(attr)}${opStr(op)}${dateStr(value)}")
|
Right(s"${dateAttr(attr)}${opStr(op)}${dateStr(value)}")
|
||||||
case Property.IntProperty(attr, value) =>
|
// case Property.IntProperty(attr, value) =>
|
||||||
Right(s"${attrStr(attr)}${opStr(op)}$value")
|
// Right(s"${attrStr(attr)}${opStr(op)}$value")
|
||||||
}
|
}
|
||||||
|
|
||||||
case TagCategoryMatch(op, values) =>
|
case TagCategoryMatch(op, values) =>
|
||||||
@ -171,13 +171,6 @@ object ExprString {
|
|||||||
attr match {
|
attr match {
|
||||||
case a: StringAttr => stringAttr(a)
|
case a: StringAttr => stringAttr(a)
|
||||||
case a: DateAttr => dateAttr(a)
|
case a: DateAttr => dateAttr(a)
|
||||||
case a: IntAttr => intAttr(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
private[internal] def intAttr(attr: IntAttr): String =
|
|
||||||
attr match {
|
|
||||||
case AttachCount =>
|
|
||||||
Constants.attachCount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private[internal] def dateAttr(attr: DateAttr): String =
|
private[internal] def dateAttr(attr: DateAttr): String =
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package docspell.query.internal
|
package docspell.query.internal
|
||||||
|
|
||||||
import cats.parse.Numbers
|
|
||||||
import cats.parse.{Parser => P}
|
import cats.parse.{Parser => P}
|
||||||
|
|
||||||
import docspell.query.ItemQuery._
|
import docspell.query.ItemQuery._
|
||||||
@ -26,9 +25,6 @@ object SimpleExprParser {
|
|||||||
private[this] val inOrOpDate =
|
private[this] val inOrOpDate =
|
||||||
P.eitherOr(op ~ DateParser.date, inOp *> DateParser.dateOrMore)
|
P.eitherOr(op ~ DateParser.date, inOp *> DateParser.dateOrMore)
|
||||||
|
|
||||||
private[this] val opInt =
|
|
||||||
op ~ Numbers.digits.map(_.toInt)
|
|
||||||
|
|
||||||
val stringExpr: P[Expr] =
|
val stringExpr: P[Expr] =
|
||||||
(AttrParser.stringAttr ~ inOrOpStr).map {
|
(AttrParser.stringAttr ~ inOrOpStr).map {
|
||||||
case (attr, Right((op, value))) =>
|
case (attr, Right((op, value))) =>
|
||||||
@ -45,11 +41,6 @@ object SimpleExprParser {
|
|||||||
Expr.InDateExpr(attr, values)
|
Expr.InDateExpr(attr, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
val intExpr: P[Expr] =
|
|
||||||
(AttrParser.intAttr ~ opInt).map { case (attr, (op, value)) =>
|
|
||||||
Expr.SimpleExpr(op, Property(attr, value))
|
|
||||||
}
|
|
||||||
|
|
||||||
val existsExpr: P[Expr.Exists] =
|
val existsExpr: P[Expr.Exists] =
|
||||||
(P.ignoreCase(C.exist) *> P.char(C.like) *> AttrParser.anyAttr).map(attr =>
|
(P.ignoreCase(C.exist) *> P.char(C.like) *> AttrParser.anyAttr).map(attr =>
|
||||||
Expr.Exists(attr)
|
Expr.Exists(attr)
|
||||||
@ -114,7 +105,6 @@ object SimpleExprParser {
|
|||||||
List(
|
List(
|
||||||
dateExpr,
|
dateExpr,
|
||||||
stringExpr,
|
stringExpr,
|
||||||
intExpr,
|
|
||||||
existsExpr,
|
existsExpr,
|
||||||
fulltextExpr,
|
fulltextExpr,
|
||||||
tagIdExpr,
|
tagIdExpr,
|
||||||
|
@ -93,11 +93,8 @@ object ItemQueryGen {
|
|||||||
val dateAttrGen: Gen[Attr.DateAttr] =
|
val dateAttrGen: Gen[Attr.DateAttr] =
|
||||||
Gen.oneOf(Attr.Date, Attr.DueDate, Attr.CreatedDate)
|
Gen.oneOf(Attr.Date, Attr.DueDate, Attr.CreatedDate)
|
||||||
|
|
||||||
val intAttrGen: Gen[Attr.IntAttr] =
|
|
||||||
Gen.const(Attr.AttachCount)
|
|
||||||
|
|
||||||
val attrGen: Gen[Attr] =
|
val attrGen: Gen[Attr] =
|
||||||
Gen.oneOf(stringAttrGen, dateAttrGen, intAttrGen)
|
Gen.oneOf(stringAttrGen, dateAttrGen)
|
||||||
|
|
||||||
private val valueChars =
|
private val valueChars =
|
||||||
Gen.oneOf(Gen.alphaNumChar, Gen.oneOf(" /{}*?-:@#$~+%…_[]^!ß"))
|
Gen.oneOf(Gen.alphaNumChar, Gen.oneOf(" /{}*?-:@#$~+%…_[]^!ß"))
|
||||||
@ -105,9 +102,6 @@ object ItemQueryGen {
|
|||||||
private val stringValueGen: Gen[String] =
|
private val stringValueGen: Gen[String] =
|
||||||
Gen.choose(1, 20).flatMap(n => Gen.stringOfN(n, valueChars))
|
Gen.choose(1, 20).flatMap(n => Gen.stringOfN(n, valueChars))
|
||||||
|
|
||||||
private val intValueGen: Gen[Int] =
|
|
||||||
Gen.choose(1900, 9999)
|
|
||||||
|
|
||||||
private val identGen: Gen[String] =
|
private val identGen: Gen[String] =
|
||||||
Gen
|
Gen
|
||||||
.choose(3, 12)
|
.choose(3, 12)
|
||||||
@ -167,12 +161,6 @@ object ItemQueryGen {
|
|||||||
sval <- stringValueGen
|
sval <- stringValueGen
|
||||||
} yield Property.StringProperty(attr, sval)
|
} yield Property.StringProperty(attr, sval)
|
||||||
|
|
||||||
val intPropGen: Gen[Property.IntProperty] =
|
|
||||||
for {
|
|
||||||
attr <- intAttrGen
|
|
||||||
ival <- intValueGen
|
|
||||||
} yield Property.IntProperty(attr, ival)
|
|
||||||
|
|
||||||
val datePropGen: Gen[Property.DateProperty] =
|
val datePropGen: Gen[Property.DateProperty] =
|
||||||
for {
|
for {
|
||||||
attr <- dateAttrGen
|
attr <- dateAttrGen
|
||||||
@ -180,7 +168,7 @@ object ItemQueryGen {
|
|||||||
} yield Property.DateProperty(attr, dv)
|
} yield Property.DateProperty(attr, dv)
|
||||||
|
|
||||||
val propertyGen: Gen[Property] =
|
val propertyGen: Gen[Property] =
|
||||||
Gen.oneOf(stringPropGen, datePropGen, intPropGen)
|
Gen.oneOf(stringPropGen, datePropGen)
|
||||||
|
|
||||||
val simpleExprGen: Gen[Expr.SimpleExpr] =
|
val simpleExprGen: Gen[Expr.SimpleExpr] =
|
||||||
for {
|
for {
|
||||||
|
@ -100,10 +100,6 @@ object ItemQueryGenerator {
|
|||||||
val noLikeOp = if (op == Operator.Like) Operator.Eq else op
|
val noLikeOp = if (op == Operator.Like) Operator.Eq else op
|
||||||
Condition.CompareFVal(col, makeOp(noLikeOp), dt)
|
Condition.CompareFVal(col, makeOp(noLikeOp), dt)
|
||||||
|
|
||||||
case Expr.SimpleExpr(op, Property.IntProperty(attr, value)) =>
|
|
||||||
val col = intColumn(tables)(attr)
|
|
||||||
Condition.CompareVal(col, makeOp(op), value)
|
|
||||||
|
|
||||||
case Expr.InExpr(attr, values) =>
|
case Expr.InExpr(attr, values) =>
|
||||||
val col = stringColumn(tables)(attr)
|
val col = stringColumn(tables)(attr)
|
||||||
if (values.tail.isEmpty) col === values.head
|
if (values.tail.isEmpty) col === values.head
|
||||||
@ -228,8 +224,6 @@ object ItemQueryGenerator {
|
|||||||
stringColumn(tables)(s).s
|
stringColumn(tables)(s).s
|
||||||
case t: Attr.DateAttr =>
|
case t: Attr.DateAttr =>
|
||||||
timestampColumn(tables)(t)
|
timestampColumn(tables)(t)
|
||||||
case n: Attr.IntAttr =>
|
|
||||||
intColumn(tables)(n).s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def timestampColumn(tables: Tables)(attr: Attr.DateAttr): SelectExpr =
|
private def timestampColumn(tables: Tables)(attr: Attr.DateAttr): SelectExpr =
|
||||||
@ -260,11 +254,6 @@ object ItemQueryGenerator {
|
|||||||
case Attr.Folder.FolderName => tables.folder.name
|
case Attr.Folder.FolderName => tables.folder.name
|
||||||
}
|
}
|
||||||
|
|
||||||
private def intColumn(tables: Tables)(attr: Attr.IntAttr): Column[Int] =
|
|
||||||
attr match {
|
|
||||||
case Attr.AttachCount => tables.attachCount.num
|
|
||||||
}
|
|
||||||
|
|
||||||
private def makeOp(operator: Operator): QOp =
|
private def makeOp(operator: Operator): QOp =
|
||||||
operator match {
|
operator match {
|
||||||
case Operator.Eq =>
|
case Operator.Eq =>
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package docspell.store.qb.generator
|
package docspell.store.qb.generator
|
||||||
|
|
||||||
import docspell.store.queries.AttachCountTable
|
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
|
|
||||||
final case class Tables(
|
final case class Tables(
|
||||||
@ -17,6 +16,5 @@ final case class Tables(
|
|||||||
concEquip: REquipment.Table,
|
concEquip: REquipment.Table,
|
||||||
folder: RFolder.Table,
|
folder: RFolder.Table,
|
||||||
attach: RAttachment.Table,
|
attach: RAttachment.Table,
|
||||||
meta: RAttachmentMeta.Table,
|
meta: RAttachmentMeta.Table
|
||||||
attachCount: AttachCountTable
|
|
||||||
)
|
)
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Eike K. & Contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
package docspell.store.queries
|
|
||||||
|
|
||||||
import docspell.common.Ident
|
|
||||||
import docspell.store.qb.Column
|
|
||||||
import docspell.store.qb.TableDef
|
|
||||||
|
|
||||||
final case class AttachCountTable(aliasName: String) extends TableDef {
|
|
||||||
val tableName = "attachs"
|
|
||||||
val alias: Option[String] = Some(aliasName)
|
|
||||||
|
|
||||||
val num = Column[Int]("num", this)
|
|
||||||
val itemId = Column[Ident]("item_id", this)
|
|
||||||
|
|
||||||
def as(alias: String): AttachCountTable =
|
|
||||||
copy(aliasName = alias)
|
|
||||||
}
|
|
@ -299,7 +299,7 @@ object QItem extends FtsSupport {
|
|||||||
coll: CollectiveId,
|
coll: CollectiveId,
|
||||||
q: ItemQuery.Expr
|
q: ItemQuery.Expr
|
||||||
): Condition = {
|
): Condition = {
|
||||||
val tables = Tables(i, org, pers0, pers1, equip, f, a, m, AttachCountTable("cta"))
|
val tables = Tables(i, org, pers0, pers1, equip, f, a, m)
|
||||||
ItemQueryGenerator.fromExpr(today, tables, coll)(q)
|
ItemQueryGenerator.fromExpr(today, tables, coll)(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import docspell.query.ItemQueryParser
|
|||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
import docspell.store.qb.Select
|
import docspell.store.qb.Select
|
||||||
import docspell.store.qb.generator.{ItemQueryGenerator, Tables}
|
import docspell.store.qb.generator.{ItemQueryGenerator, Tables}
|
||||||
import docspell.store.queries.AttachCountTable
|
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
|
|
||||||
import munit._
|
import munit._
|
||||||
@ -29,8 +28,7 @@ class ItemQueryGeneratorTest extends FunSuite {
|
|||||||
REquipment.as("ne"),
|
REquipment.as("ne"),
|
||||||
RFolder.as("f"),
|
RFolder.as("f"),
|
||||||
RAttachment.as("a"),
|
RAttachment.as("a"),
|
||||||
RAttachmentMeta.as("m"),
|
RAttachmentMeta.as("m")
|
||||||
AttachCountTable("cta")
|
|
||||||
)
|
)
|
||||||
val now: LocalDate = LocalDate.of(2021, 2, 25)
|
val now: LocalDate = LocalDate.of(2021, 2, 25)
|
||||||
|
|
||||||
|
2881
modules/webapp/package-lock.json
generated
2881
modules/webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@
|
|||||||
"name": "docspell-css",
|
"name": "docspell-css",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||||
"@tailwindcss/forms": "^0.5.0",
|
"@tailwindcss/forms": "^0.5.0",
|
||||||
|
@ -91,7 +91,6 @@ These fields map to at most one value:
|
|||||||
- `date` the item date
|
- `date` the item date
|
||||||
- `due` the due date of the item
|
- `due` the due date of the item
|
||||||
- `created` the date when the item was created
|
- `created` the date when the item was created
|
||||||
- `attach.count` the number of attachments of the item
|
|
||||||
- `corr.org.id` the id of the correspondent organization
|
- `corr.org.id` the id of the correspondent organization
|
||||||
- `corr.org.name` the name of the correspondent organization
|
- `corr.org.name` the name of the correspondent organization
|
||||||
- `corr.pers.name` name of correspondent person
|
- `corr.pers.name` name of correspondent person
|
||||||
@ -522,11 +521,6 @@ look into correspondent names and concerning names.
|
|||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
Find items with 2 or more attachments:
|
|
||||||
```
|
|
||||||
attach.count>2
|
|
||||||
```
|
|
||||||
|
|
||||||
Find items with at least one tag invoice or todo that are due next:
|
Find items with at least one tag invoice or todo that are due next:
|
||||||
```
|
```
|
||||||
tag:invoice,todo due>today
|
tag:invoice,todo due>today
|
||||||
|
Reference in New Issue
Block a user