// Search: GET /search.json — full-text search across posts, topics, // users and categories. The term supports Discourse's search operators // (@user, #category, order:latest, ...); they pass through untouched. package discourse import ( "context" "fmt" "net/url" "strconv" "strings" ) // SearchRequest is the GET /search.json query. Term is required; Page is // 1-based (0 means "leave unset" — Discourse then returns page 1). type SearchRequest struct { Term string Page int } // SearchPost is one post hit: the match lives in Blurb (a plain-text // excerpt around the match) while TopicTitleHeadline carries the topic // title with highlights around matched words. type SearchPost struct { ID int `json:"id"` TopicID int `json:"topic_id"` PostNumber int `json:"post_number"` Username string `json:"username"` Name string `json:"name"` Blurb string `json:"blurb"` TopicTitleHeadline string `json:"topic_title_headline"` CreatedAt string `json:"created_at"` LikeCount int `json:"like_count"` } // SearchTopic is one topic hit. type SearchTopic struct { ID int `json:"id"` Title string `json:"title"` Slug string `json:"slug"` } // SearchUser is one user hit. type SearchUser struct { ID int `json:"id"` Username string `json:"username"` Name string `json:"name"` } // SearchResult groups the four hit lists Discourse returns; whichever // classes matched are non-empty, the rest are nil. type SearchResult struct { Posts []SearchPost `json:"posts"` Topics []SearchTopic `json:"topics"` Users []SearchUser `json:"users"` Categories []Category `json:"categories"` } // Search runs a full-text search and returns the grouped hits. func (c *Client) Search(ctx context.Context, req SearchRequest) (*SearchResult, error) { if strings.TrimSpace(req.Term) == "" { return nil, fmt.Errorf("%w: search term is required", ErrInvalidRequest) } q := url.Values{} q.Set("term", req.Term) if req.Page > 1 { q.Set("page", strconv.Itoa(req.Page)) } var res SearchResult if err := c.Get(ctx, "/search.json?"+q.Encode(), &res); err != nil { return nil, err } return &res, nil }