feat(filters): basic text filter works now

This commit is contained in:
kolaente 2023-11-09 22:31:06 +01:00
parent 307ffe11c4
commit de320aac72
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 87 additions and 93 deletions

View File

@ -55,6 +55,7 @@ type taskFilter struct {
value interface{} // Needs to be an interface to be able to hold the field's native value
comparator taskFilterComparator
isNumeric bool
join taskFilterConcatinator
}
func parseTimeFromUserInput(timeString string) (value time.Time, err error) {
@ -103,11 +104,11 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
c.FilterComparator = append(c.FilterComparator, c.FilterComparatorArr...)
}
if c.FilterConcat != "" && c.FilterConcat != filterConcatAnd && c.FilterConcat != filterConcatOr {
return nil, ErrInvalidTaskFilterConcatinator{
Concatinator: taskFilterConcatinator(c.FilterConcat),
}
}
//if c.FilterConcat != "" && c.FilterConcat != filterConcatAnd && c.FilterConcat != filterConcatOr {
// return nil, ErrInvalidTaskFilterConcatinator{
// Concatinator: taskFilterConcatinator(c.FilterConcat),
// }
//}
parsedFilter, err := fexpr.Parse(c.Filter)
if err != nil {
@ -115,18 +116,21 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
}
filters = make([]*taskFilter, 0, len(parsedFilter))
for i, f := range parsedFilter {
for _, f := range parsedFilter {
filter := &taskFilter{}
filter := &taskFilter{
join: filterConcatAnd,
}
if f.Join == fexpr.JoinOr {
filter.join = filterConcatOr
}
var value string
switch v := f.Item.(type) {
case fexpr.Expr:
filter.field = v.Left.Literal
filter.comparator = v.Op
filter.value = v.Right.Literal // TODO: nesting
}
if len(c.FilterComparator) > i {
filter.comparator, err = getFilterComparatorFromString(c.FilterComparator[i])
value = v.Right.Literal // TODO: nesting
filter.comparator, err = getFilterComparatorFromOp(v.Op)
if err != nil {
return
}
@ -139,13 +143,11 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
// Cast the field value to its native type
var reflectValue *reflect.StructField
if len(c.FilterValue) > i {
reflectValue, filter.value, err = getNativeValueForTaskField(filter.field, filter.comparator, c.FilterValue[i])
if err != nil {
return nil, ErrInvalidTaskFilterValue{
Value: filter.field,
Field: c.FilterValue[i],
}
reflectValue, filter.value, err = getNativeValueForTaskField(filter.field, filter.comparator, value)
if err != nil {
return nil, ErrInvalidTaskFilterValue{
Value: filter.field,
Field: value,
}
}
if reflectValue != nil {
@ -200,6 +202,29 @@ func getFilterComparatorFromString(comparator string) (taskFilterComparator, err
}
}
func getFilterComparatorFromOp(op fexpr.SignOp) (taskFilterComparator, error) {
switch op {
case fexpr.SignEq:
return taskFilterComparatorEquals, nil
case fexpr.SignGt:
return taskFilterComparatorGreater, nil
case fexpr.SignGte:
return taskFilterComparatorGreateEquals, nil
case fexpr.SignLt:
return taskFilterComparatorLess, nil
case fexpr.SignLte:
return taskFilterComparatorLessEquals, nil
case fexpr.SignNeq:
return taskFilterComparatorNotEquals, nil
case fexpr.SignLike:
return taskFilterComparatorLike, nil
case "in":
return taskFilterComparatorIn, nil
default:
return taskFilterComparatorInvalid, ErrInvalidTaskFilterComparator{Comparator: taskFilterComparator(op)}
}
}
func getValueForField(field reflect.StructField, rawValue string) (value interface{}, err error) {
switch field.Type.Kind() {
case reflect.Int64:

View File

@ -794,10 +794,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
{
name: "ReadAll Tasks with range",
fields: fields{
FilterBy: []string{"start_date", "end_date"},
FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"},
FilterComparator: []string{"greater", "less"},
Filter: "start_date>'2018-12-11T03:46:40+00:00' || end_date<'2018-12-13T11:20:01+00:00'",
//FilterBy: []string{"start_date", "end_date"},
//FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"},
//FilterComparator: []string{"greater", "less"},
Filter: "start_date > '2018-12-11T03:46:40+00:00' || end_date < '2018-12-13T11:20:01+00:00'",
},
args: defaultArgs,
want: []*Task{

View File

@ -84,12 +84,6 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
return nil, 0, err
}
// Some filters need a special treatment since they are in a separate table
reminderFilters := []builder.Cond{}
assigneeFilters := []builder.Cond{}
labelFilters := []builder.Cond{}
projectFilters := []builder.Cond{}
var filters = make([]builder.Cond, 0, len(opts.filters))
// To still find tasks with nil values, we exclude 0s when comparing with >/< values.
for _, f := range opts.filters {
@ -104,7 +98,7 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
if err != nil {
return nil, totalCount, err
}
reminderFilters = append(reminderFilters, filter)
filters = append(filters, getFilterCondForSeparateTable("task_reminders", filter))
continue
}
@ -122,7 +116,13 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
if err != nil {
return nil, totalCount, err
}
assigneeFilters = append(assigneeFilters, filter)
assigneeFilter := builder.In("user_id",
builder.Select("id").
From("users").
Where(filter),
)
filters = append(filters, getFilterCondForSeparateTable("task_assignees", assigneeFilter))
continue
}
@ -137,7 +137,8 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
if err != nil {
return nil, totalCount, err
}
labelFilters = append(labelFilters, filter)
filters = append(filters, getFilterCondForSeparateTable("label_tasks", filter))
continue
}
@ -152,7 +153,15 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
if err != nil {
return nil, totalCount, err
}
projectFilters = append(projectFilters, filter)
cond := builder.In(
"project_id",
builder.
Select("id").
From("projects").
Where(filter),
)
filters = append(filters, cond)
continue
}
@ -199,50 +208,17 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
favoritesCond = builder.In("id", favCond)
}
if len(reminderFilters) > 0 {
filters = append(filters, getFilterCondForSeparateTable("task_reminders", opts.filterConcat, reminderFilters))
}
if len(assigneeFilters) > 0 {
assigneeFilter := []builder.Cond{
builder.In("user_id",
builder.Select("id").
From("users").
Where(builder.Or(assigneeFilters...)),
)}
filters = append(filters, getFilterCondForSeparateTable("task_assignees", opts.filterConcat, assigneeFilter))
}
if len(labelFilters) > 0 {
filters = append(filters, getFilterCondForSeparateTable("label_tasks", opts.filterConcat, labelFilters))
}
if len(projectFilters) > 0 {
var filtercond builder.Cond
if opts.filterConcat == filterConcatOr {
filtercond = builder.Or(projectFilters...)
}
if opts.filterConcat == filterConcatAnd {
filtercond = builder.And(projectFilters...)
}
cond := builder.In(
"project_id",
builder.
Select("id").
From("projects").
Where(filtercond),
)
filters = append(filters, cond)
}
var filterCond builder.Cond
if len(filters) > 0 {
if opts.filterConcat == filterConcatOr {
filterCond = builder.Or(filters...)
}
if opts.filterConcat == filterConcatAnd {
filterCond = builder.And(filters...)
for i, f := range filters {
if len(filters) > i+1 {
switch opts.filters[i].join {
case filterConcatOr:
filterCond = builder.Or(filterCond, f, filters[i+1])
case filterConcatAnd:
filterCond = builder.And(filterCond, f, filters[i+1])
}
}
}
}

View File

@ -162,16 +162,17 @@ func (t *Task) GetFrontendURL() string {
type taskFilterConcatinator string
const (
filterConcatAnd = "and"
filterConcatOr = "or"
filterConcatAnd taskFilterConcatinator = "and"
filterConcatOr taskFilterConcatinator = "or"
)
type taskSearchOptions struct {
search string
page int
perPage int
sortby []*sortParam
filters []*taskFilter
search string
page int
perPage int
sortby []*sortParam
filters []*taskFilter
// deprecated: concat should live in filters directly
filterConcat taskFilterConcatinator
filterIncludeNulls bool
filter string
@ -239,21 +240,13 @@ func getFilterCond(f *taskFilter, includeNulls bool) (cond builder.Cond, err err
return
}
func getFilterCondForSeparateTable(table string, concat taskFilterConcatinator, conds []builder.Cond) builder.Cond {
var filtercond builder.Cond
if concat == filterConcatOr {
filtercond = builder.Or(conds...)
}
if concat == filterConcatAnd {
filtercond = builder.And(conds...)
}
func getFilterCondForSeparateTable(table string, cond builder.Cond) builder.Cond {
return builder.In(
"id",
builder.
Select("task_id").
From(table).
Where(filtercond),
Where(cond),
)
}