|
|
@ -65,51 +65,6 @@ func (r *itemRepository) Fetch(ctx context.Context, filter models.ItemFilter) ([ |
|
|
|
return []entities.Item{}, nil |
|
|
|
} |
|
|
|
|
|
|
|
// For tags, use the ID filter to avoid making too much of a messy query below.
|
|
|
|
if filter.Tags != nil { |
|
|
|
query, args, err := squirrel.Select("object_id, tag_name"). |
|
|
|
From("tag"). |
|
|
|
Where(squirrel.Eq{"tag_name": filter.Tags, "object_kind": tagObjectKindItem}). |
|
|
|
ToSql() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
rows, err := r.db.QueryContext(ctx, query, args...) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
ids := make([]int, 0, 16) |
|
|
|
matches := make(map[int]int, 64) |
|
|
|
for rows.Next() { |
|
|
|
var objectID int |
|
|
|
var tagName string |
|
|
|
err := rows.Scan(&objectID, &tagName) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
if matches[objectID] == 0 { |
|
|
|
ids = append(ids, objectID) |
|
|
|
} |
|
|
|
matches[objectID] += 1 |
|
|
|
} |
|
|
|
|
|
|
|
err = rows.Close() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
if filter.IDs == nil { |
|
|
|
filter.IDs = ids |
|
|
|
} |
|
|
|
filter.IDs = genutils.RetainInPlace(filter.IDs, func(id int) bool { |
|
|
|
return matches[id] == len(filter.Tags) |
|
|
|
}) |
|
|
|
|
|
|
|
if len(filter.IDs) == 0 { |
|
|
|
return []entities.Item{}, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
sq := squirrel.Select( |
|
|
|
"i.id, i.scope_id, i.project_requirement_id, pr.project_id, i.owner_id, i.name," + |
|
|
|
" i.description, i.created_time, i.acquired_time, i.scheduled_date", |
|
|
@ -181,6 +136,10 @@ func (r *itemRepository) Fetch(ctx context.Context, filter models.ItemFilter) ([ |
|
|
|
|
|
|
|
seen := make(map[int]bool, 32) |
|
|
|
res := make([]entities.Item, 0, 32) |
|
|
|
ids := genutils.Set[int]{} |
|
|
|
projectIDs := genutils.Set[int]{} |
|
|
|
requirementIDs := genutils.Set[int]{} |
|
|
|
|
|
|
|
for rows.Next() { |
|
|
|
item := entities.Item{} |
|
|
|
|
|
|
@ -218,14 +177,16 @@ func (r *itemRepository) Fetch(ctx context.Context, filter models.ItemFilter) ([ |
|
|
|
item.ScheduledDate = scheduledDate.AsPtr() |
|
|
|
item.Tags = []string{} |
|
|
|
|
|
|
|
ids.Add(item.ID) |
|
|
|
if item.ProjectID != nil { |
|
|
|
projectIDs.Add(*item.ProjectID) |
|
|
|
requirementIDs.Add(*item.RequirementID) |
|
|
|
} |
|
|
|
|
|
|
|
res = append(res, item) |
|
|
|
} |
|
|
|
|
|
|
|
// Fill tags
|
|
|
|
ids := genutils.Map(res, func(i entities.Item) int { |
|
|
|
return i.ID |
|
|
|
}) |
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindItem, ids, func(id int, tag string) { |
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindItem, ids.Values(), func(id int, tag string) { |
|
|
|
for i := range res { |
|
|
|
if id == res[i].ID { |
|
|
|
res[i].Tags = append(res[i].Tags, tag) |
|
|
@ -236,6 +197,41 @@ func (r *itemRepository) Fetch(ctx context.Context, filter models.ItemFilter) ([ |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
if len(filter.Tags) > 0 { |
|
|
|
projectTagMap := make(map[int][]string, 64) |
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindProject, projectIDs.Values(), func(id int, tag string) { |
|
|
|
projectTagMap[id] = append(projectTagMap[id], tag) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
requirementTagMap := make(map[int][]string, 64) |
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindRequirement, requirementIDs.Values(), func(id int, tag string) { |
|
|
|
requirementTagMap[id] = append(requirementTagMap[id], tag) |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
res = genutils.RetainInPlace(res, func(item entities.Item) bool { |
|
|
|
for _, tag := range filter.Tags { |
|
|
|
if !item.HasTag(tag) { |
|
|
|
if item.RequirementID != nil { |
|
|
|
if !genutils.Contains(requirementTagMap[*item.RequirementID], tag) && |
|
|
|
!genutils.Contains(projectTagMap[*item.ProjectID], tag) { |
|
|
|
return false |
|
|
|
} |
|
|
|
} else { |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return true |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
sort.Slice(res, func(i, j int) bool { |
|
|
|
// Acquired time, descending
|
|
|
|
ati, atj := res[i].AcquiredTime, res[j].AcquiredTime |
|
|
|