~netlandish/links

a74880338ae6abaaef49c31b46e4ad92567f7e38 — Yader Velasquez 9 months ago efbcbee
Change tag queryset and to support many tags (inclusive and exclusive)
Add support for excluded tags to graphql

References: https://todo.code.netlandish.com/~netlandish/links/47
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()
}