[feature] Hashtag federation (in/out), hashtag client API endpoints (#2032)

* update go-fed

* do the things

* remove unused columns from tags

* update to latest lingo from main

* further tag shenanigans

* serve stub page at tag endpoint

* we did it lads

* tests, oh tests, ohhh tests, oh tests (doo doo doo doo)

* swagger docs

* document hashtag usage + federation

* instanceGet

* don't bother parsing tag href

* rename whereStartsWith -> whereStartsLike

* remove GetOrCreateTag

* dont cache status tag timelineability
This commit is contained in:
tobi 2023-07-31 15:47:35 +02:00 committed by GitHub
commit 2796a2e82f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 2536 additions and 482 deletions

View file

@ -19,6 +19,7 @@ package bundb
import (
"context"
"strings"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
@ -385,3 +386,101 @@ func (s *searchDB) statusText() *bun.SelectQuery {
return statusText
}
// Query example (SQLite):
//
// SELECT "tag"."id" FROM "tags" AS "tag"
// WHERE ("tag"."id" < 'ZZZZZZZZZZZZZZZZZZZZZZZZZZ')
// AND (("tag"."name") LIKE 'welcome%' ESCAPE '\')
// ORDER BY "tag"."id" DESC LIMIT 10
func (s *searchDB) SearchForTags(
ctx context.Context,
query string,
maxID string,
minID string,
limit int,
offset int,
) ([]*gtsmodel.Tag, error) {
// Ensure reasonable
if limit < 0 {
limit = 0
}
// Make educated guess for slice size
var (
tagIDs = make([]string, 0, limit)
frontToBack = true
)
q := s.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("tags"), bun.Ident("tag")).
// Select only IDs from table
Column("tag.id")
// Return only items with a LOWER id than maxID.
if maxID == "" {
maxID = id.Highest
}
q = q.Where("? < ?", bun.Ident("tag.id"), maxID)
if minID != "" {
// return only tags HIGHER (ie., newer) than minID
q = q.Where("? > ?", bun.Ident("tag.id"), minID)
// page up
frontToBack = false
}
// Normalize tag 'name' string.
name := strings.TrimSpace(query)
name = strings.ToLower(name)
// Search using LIKE for tags that start with `name`.
q = whereStartsLike(q, bun.Ident("tag.name"), name)
if limit > 0 {
// Limit amount of tags returned.
q = q.Limit(limit)
}
if frontToBack {
// Page down.
q = q.Order("tag.id DESC")
} else {
// Page up.
q = q.Order("tag.id ASC")
}
if err := q.Scan(ctx, &tagIDs); err != nil {
return nil, s.db.ProcessError(err)
}
if len(tagIDs) == 0 {
return nil, nil
}
// If we're paging up, we still want tags
// to be sorted by ID desc, so reverse slice.
// https://zchee.github.io/golang-wiki/SliceTricks/#reversing
if !frontToBack {
for l, r := 0, len(tagIDs)-1; l < r; l, r = l+1, r-1 {
tagIDs[l], tagIDs[r] = tagIDs[r], tagIDs[l]
}
}
tags := make([]*gtsmodel.Tag, 0, len(tagIDs))
for _, id := range tagIDs {
// Fetch tag from db for ID
tag, err := s.state.DB.GetTag(ctx, id)
if err != nil {
log.Errorf(ctx, "error fetching tag %q: %v", id, err)
continue
}
// Append status to slice
tags = append(tags, tag)
}
return tags, nil
}