feat(filters): basic text filter works now
This commit is contained in:
parent
307ffe11c4
commit
de320aac72
@ -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:
|
||||
|
@ -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{
|
||||
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user