From 50e3aa2ad780f9b8d425fdeb878afb93aa6dd9bf Mon Sep 17 00:00:00 2001 From: Yader Velasquez Date: Mon, 12 Feb 2024 17:56:57 -0600 Subject: [PATCH] Add exclude support for list/shorts API Improve helper function to customize query References: https://todo.code.netlandish.com/~netlandish/links/47 --- api/graph/generated.go | 20 ++++++++++++++-- api/graph/model/models_gen.go | 22 ++++++++++-------- api/graph/schema.graphqls | 2 ++ api/graph/schema.resolvers.go | 43 +++++++++++++++++++++-------------- helpers.go | 26 +++++++++++++++------ 5 files changed, 77 insertions(+), 36 deletions(-) diff --git a/api/graph/generated.go b/api/graph/generated.go index e74659c..92a80f5 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -19763,7 +19763,7 @@ func (ec *executionContext) unmarshalInputGetLinkShortInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag"} + fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -19810,6 +19810,14 @@ func (ec *executionContext) unmarshalInputGetLinkShortInput(ctx context.Context, 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 + } } } @@ -19883,7 +19891,7 @@ func (ec *executionContext) unmarshalInputGetListingInput(ctx context.Context, o asMap[k] = v } - fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag"} + fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -19930,6 +19938,14 @@ func (ec *executionContext) unmarshalInputGetListingInput(ctx context.Context, o 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 + } } } diff --git a/api/graph/model/models_gen.go b/api/graph/model/models_gen.go index de1e377..b37da95 100644 --- a/api/graph/model/models_gen.go +++ b/api/graph/model/models_gen.go @@ -158,11 +158,12 @@ type GetLinkInput struct { } type GetLinkShortInput struct { - OrgSlug string `json:"orgSlug"` - Limit *int `json:"limit,omitempty"` - After *Cursor `json:"after,omitempty"` - Before *Cursor `json:"before,omitempty"` - Tag *string `json:"tag,omitempty"` + OrgSlug string `json:"orgSlug"` + 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"` } type GetListingDetailInput struct { @@ -174,11 +175,12 @@ type GetListingDetailInput struct { } type GetListingInput struct { - OrgSlug string `json:"orgSlug"` - Limit *int `json:"limit,omitempty"` - After *Cursor `json:"after,omitempty"` - Before *Cursor `json:"before,omitempty"` - Tag *string `json:"tag,omitempty"` + OrgSlug string `json:"orgSlug"` + 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"` } type GetPaymentInput struct { diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 479ac40..65dcb75 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -454,6 +454,7 @@ input GetLinkShortInput { after: Cursor before: Cursor tag: String + excludeTag: String } input GetListingInput { @@ -462,6 +463,7 @@ input GetListingInput { after: Cursor before: Cursor tag: String + excludeTag: String } input GetListingDetailInput { diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index d4025f3..721dc08 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -4754,7 +4754,8 @@ func (r *queryResolver) GetOrgLinks(ctx context.Context, input *model.GetLinkInp excludeTagList = strings.Split(*input.ExcludeTag, ",") } // Filter based on tags - subQText, subQVal, err := links.GetTagSubQuery(tagList, excludeTagList) + f := links.NewTagQuery("tag_links", "org_link_id") + subQText, subQVal, err := f.GetSubQuery(tagList, excludeTagList) if err != nil { return nil, err } @@ -5066,20 +5067,24 @@ func (r *queryResolver) GetLinkShorts(ctx context.Context, input *model.GetLinkS return nil, nil } - if input.Tag != nil && *input.Tag != "" { - subQ := sq.Select("tl.link_short_id"). - From("tag_link_shorts tl"). - LeftJoin("tags t ON t.id = tl.tag_id"). - Where("t.slug = ?", *input.Tag) + if (input.Tag != nil && *input.Tag != "") || (input.ExcludeTag != nil && *input.ExcludeTag != "") { + var tagList = make([]string, 0) + if input.Tag != nil && *input.Tag != "" { + tagList = strings.Split(*input.Tag, ",") + } - subQText, _, err := subQ.ToSql() + var excludeTagList = make([]string, 0) + if input.ExcludeTag != nil && *input.ExcludeTag != "" { + excludeTagList = strings.Split(*input.ExcludeTag, ",") + } + f := links.NewTagQuery("tag_link_shorts", "link_short_id") + subQText, subQVal, err := f.GetSubQuery(tagList, excludeTagList) if err != nil { return nil, err } - linkOpts.Filter = sq.And{ linkOpts.Filter, - sq.Expr("l.id IN ("+subQText+")", *input.Tag), + sq.Expr("l.id IN ("+subQText+")", subQVal...), } } @@ -5258,20 +5263,24 @@ func (r *queryResolver) GetListings(ctx context.Context, input *model.GetListing return nil, nil } - if input.Tag != nil && *input.Tag != "" { - subQ := sq.Select("tl.listing_id"). - From("tag_listings tl"). - LeftJoin("tags t ON t.id = tl.tag_id"). - Where("t.slug = ?", *input.Tag) + if (input.Tag != nil && *input.Tag != "") || (input.ExcludeTag != nil && *input.ExcludeTag != "") { + var tagList = make([]string, 0) + if input.Tag != nil && *input.Tag != "" { + tagList = strings.Split(*input.Tag, ",") + } - subQText, _, err := subQ.ToSql() + var excludeTagList = make([]string, 0) + if input.ExcludeTag != nil && *input.ExcludeTag != "" { + excludeTagList = strings.Split(*input.ExcludeTag, ",") + } + f := links.NewTagQuery("tag_listings", "listing_id") + subQText, subQVal, err := f.GetSubQuery(tagList, excludeTagList) if err != nil { return nil, err } - listingOpts.Filter = sq.And{ listingOpts.Filter, - sq.Expr("l.id IN ("+subQText+")", *input.Tag), + sq.Expr("l.id IN ("+subQText+")", subQVal...), } } diff --git a/helpers.go b/helpers.go index 5979903..24f817c 100644 --- a/helpers.go +++ b/helpers.go @@ -709,7 +709,19 @@ func CreateNoteURL(domain string) (string, string) { return noteURL.String(), noteHash } -func GetTagSubQuery(tags, excludeTags []string) (string, []interface{}, error) { +type TagQuery struct { + table string + column string +} + +func NewTagQuery(t, c string) *TagQuery { + return &TagQuery{ + table: t, + column: c, + } +} + +func (t TagQuery) GetSubQuery(tags, excludeTags []string) (string, []interface{}, error) { subQOpts := &database.FilterOptions{ Filter: sq.And{}, } @@ -728,8 +740,8 @@ func GetTagSubQuery(tags, excludeTags []string) (string, []interface{}, error) { // 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"). + excludeSubQ := sq.Select(fmt.Sprintf("tl.%s", t.column)). + From(fmt.Sprintf("%s tl", t.table)). LeftJoin("tags t ON t.id = tl.tag_id"). Where(sq.Eq{"t.slug": excludeTags}) excludeSubQText, excludeSubQVal, err := excludeSubQ.ToSql() @@ -739,12 +751,12 @@ func GetTagSubQuery(tags, excludeTags []string) (string, []interface{}, error) { subQOpts.Filter = sq.And{ subQOpts.Filter, - sq.Expr("tl.org_link_id NOT IN ("+excludeSubQText+")", excludeSubQVal...), + sq.Expr(fmt.Sprintf("tl.%s NOT IN (%s)", t.column, excludeSubQText), excludeSubQVal...), } } - subQ := sq.Select("tl.org_link_id"). - From("tag_links tl"). + subQ := sq.Select(fmt.Sprintf("tl.%s", t.column)). + From(fmt.Sprintf("%s tl", t.table)). LeftJoin("tags t ON t.id = tl.tag_id"). Where(subQOpts.Filter) @@ -755,7 +767,7 @@ func GetTagSubQuery(tags, excludeTags []string) (string, []interface{}, error) { // include ONLY the org_link_id that have ALL // target tags subQ = subQ. - GroupBy("tl.org_link_id"). + GroupBy(fmt.Sprintf("tl.%s", t.column)). Having(sq.Eq{"COUNT(DISTINCT tag_id)": len(tags)}) } -- 2.45.2