M api/graph/generated.go => api/graph/generated.go +9 -1
@@ 19679,7 19679,7 @@ func (ec *executionContext) unmarshalInputGetLinkInput(ctx context.Context, obj
asMap[k] = v
}
- fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "search", "filter"}
+ fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag", "search", "filter"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ 19726,6 19726,14 @@ func (ec *executionContext) unmarshalInputGetLinkInput(ctx context.Context, obj
if err != nil {
return it, err
}
+ case "excludeTag":
+ var err error
+
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("excludeTag"))
+ it.ExcludeTag, err = ec.unmarshalOString2áš–string(ctx, v)
+ if err != nil {
+ return it, err
+ }
case "search":
var err error
M api/graph/model/models_gen.go => api/graph/model/models_gen.go +8 -7
@@ 147,13 147,14 @@ type GetAdminOrganizationsInput struct {
}
type GetLinkInput struct {
- OrgSlug *string `json:"orgSlug,omitempty"`
- Limit *int `json:"limit,omitempty"`
- After *Cursor `json:"after,omitempty"`
- Before *Cursor `json:"before,omitempty"`
- Tag *string `json:"tag,omitempty"`
- Search *string `json:"search,omitempty"`
- Filter *string `json:"filter,omitempty"`
+ OrgSlug *string `json:"orgSlug,omitempty"`
+ Limit *int `json:"limit,omitempty"`
+ After *Cursor `json:"after,omitempty"`
+ Before *Cursor `json:"before,omitempty"`
+ Tag *string `json:"tag,omitempty"`
+ ExcludeTag *string `json:"excludeTag,omitempty"`
+ Search *string `json:"search,omitempty"`
+ Filter *string `json:"filter,omitempty"`
}
type GetLinkShortInput struct {
M api/graph/schema.graphqls => api/graph/schema.graphqls +1 -0
@@ 443,6 443,7 @@ input GetLinkInput {
after: Cursor,
before: Cursor,
tag: String,
+ excludeTag: String,
search: String
filter: String
}
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +4 -8
@@ 4744,19 4744,15 @@ func (r *queryResolver) GetOrgLinks(ctx context.Context, input *model.GetLinkInp
}
if input.Tag != nil && *input.Tag != "" {
- subQ := sq.Select("tl.org_link_id").
- From("tag_links tl").
- LeftJoin("tags t ON t.id = tl.tag_id").
- Where("t.slug = ?", *input.Tag)
-
- subQText, _, err := subQ.ToSql()
+ // Filter based on tags
+ tagList := strings.Split(*input.Tag, ",")
+ subQText, subQVal, err := links.GetTagSubQuery(tagList, []string{})
if err != nil {
return nil, err
}
-
linkOpts.Filter = sq.And{
linkOpts.Filter,
- sq.Expr("ol.id IN ("+subQText+")", *input.Tag),
+ sq.Expr("ol.id IN ("+subQText+")", subQVal...),
}
}
M helpers.go => helpers.go +53 -0
@@ 708,3 708,56 @@ func CreateNoteURL(domain string) (string, string) {
return noteURL.String(), noteHash
}
+
+func GetTagSubQuery(tags, excludeTags []string) (string, []interface{}, error) {
+ subQOpts := &database.FilterOptions{
+ Filter: sq.And{},
+ }
+
+ if len(tags) > 0 {
+ subQOpts.Filter = sq.And{
+ subQOpts.Filter,
+ sq.Eq{"t.slug": tags},
+ }
+ }
+
+ if len(excludeTags) > 0 {
+ // NOTE: Since we are using a subquery
+ // that get the org_link_id (tag_links) based on
+ // a left join we want to exclude any org_link_id
+ // that have a relation to an excluded target tag
+ // no matter if that org_link_id has any relation
+ // to one of the previous tags (included)
+ excludeSubQ := sq.Select("tl.org_link_id").
+ From("tag_links tl").
+ LeftJoin("tags t ON t.id = tl.tag_id").
+ Where(sq.Eq{"t.slug": "alpine"})
+ excludeSubQText, excludeSubQVal, err := excludeSubQ.ToSql()
+ if err != nil {
+ return "", nil, err
+ }
+
+ subQOpts.Filter = sq.And{
+ subQOpts.Filter,
+ sq.Expr("tl.org_link_id NOT IN ("+excludeSubQText+")", excludeSubQVal...),
+ }
+ }
+
+ subQ := sq.Select("tl.org_link_id").
+ From("tag_links tl").
+ LeftJoin("tags t ON t.id = tl.tag_id").
+ Where(subQOpts.Filter)
+
+ if len(tags) > 0 {
+ // NOTE: Since we are using a subquery
+ // that get the org_link_id (tag_links) based on
+ // a left join to tags we want to make sure to
+ // include ONLY the org_link_id that have ALL
+ // target tags
+ subQ = subQ.
+ GroupBy("tl.org_link_id").
+ Having(sq.Eq{"COUNT(DISTINCT tag_id)": len(tags)})
+ }
+
+ return subQ.ToSql()
+}