...

Source file src/github.com/google/go-github/v33/github/search.go

Documentation: github.com/google/go-github/v33/github

     1  // Copyright 2013 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"strconv"
    12  
    13  	qs "github.com/google/go-querystring/query"
    14  )
    15  
    16  // SearchService provides access to the search related functions
    17  // in the GitHub API.
    18  //
    19  // Each method takes a query string defining the search keywords and any search qualifiers.
    20  // For example, when searching issues, the query "gopher is:issue language:go" will search
    21  // for issues containing the word "gopher" in Go repositories. The method call
    22  //   opts :=  &github.SearchOptions{Sort: "created", Order: "asc"}
    23  //   cl.Search.Issues(ctx, "gopher is:issue language:go", opts)
    24  // will search for such issues, sorting by creation date in ascending order
    25  // (i.e., oldest first).
    26  //
    27  // If query includes multiple conditions, it MUST NOT include "+" as the condition separator.
    28  // You have to use " " as the separator instead.
    29  // For example, querying with "language:c++" and "leveldb", then query should be
    30  // "language:c++ leveldb" but not "language:c+++leveldb".
    31  //
    32  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/
    33  type SearchService service
    34  
    35  // SearchOptions specifies optional parameters to the SearchService methods.
    36  type SearchOptions struct {
    37  	// How to sort the search results. Possible values are:
    38  	//   - for repositories: stars, fork, updated
    39  	//   - for commits: author-date, committer-date
    40  	//   - for code: indexed
    41  	//   - for issues: comments, created, updated
    42  	//   - for users: followers, repositories, joined
    43  	//
    44  	// Default is to sort by best match.
    45  	Sort string `url:"sort,omitempty"`
    46  
    47  	// Sort order if sort parameter is provided. Possible values are: asc,
    48  	// desc. Default is desc.
    49  	Order string `url:"order,omitempty"`
    50  
    51  	// Whether to retrieve text match metadata with a query
    52  	TextMatch bool `url:"-"`
    53  
    54  	ListOptions
    55  }
    56  
    57  // Common search parameters.
    58  type searchParameters struct {
    59  	Query        string
    60  	RepositoryID *int64 // Sent if non-nil.
    61  }
    62  
    63  // RepositoriesSearchResult represents the result of a repositories search.
    64  type RepositoriesSearchResult struct {
    65  	Total             *int          `json:"total_count,omitempty"`
    66  	IncompleteResults *bool         `json:"incomplete_results,omitempty"`
    67  	Repositories      []*Repository `json:"items,omitempty"`
    68  }
    69  
    70  // Repositories searches repositories via various criteria.
    71  //
    72  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-repositories
    73  func (s *SearchService) Repositories(ctx context.Context, query string, opts *SearchOptions) (*RepositoriesSearchResult, *Response, error) {
    74  	result := new(RepositoriesSearchResult)
    75  	resp, err := s.search(ctx, "repositories", &searchParameters{Query: query}, opts, result)
    76  	return result, resp, err
    77  }
    78  
    79  // TopicsSearchResult represents the result of a topics search.
    80  type TopicsSearchResult struct {
    81  	Total             *int           `json:"total_count,omitempty"`
    82  	IncompleteResults *bool          `json:"incomplete_results,omitempty"`
    83  	Topics            []*TopicResult `json:"items,omitempty"`
    84  }
    85  
    86  type TopicResult struct {
    87  	Name             *string    `json:"name,omitempty"`
    88  	DisplayName      *string    `json:"display_name,omitempty"`
    89  	ShortDescription *string    `json:"short_description,omitempty"`
    90  	Description      *string    `json:"description,omitempty"`
    91  	CreatedBy        *string    `json:"created_by,omitempty"`
    92  	CreatedAt        *Timestamp `json:"created_at,omitempty"`
    93  	UpdatedAt        *string    `json:"updated_at,omitempty"`
    94  	Featured         *bool      `json:"featured,omitempty"`
    95  	Curated          *bool      `json:"curated,omitempty"`
    96  	Score            *float64   `json:"score,omitempty"`
    97  }
    98  
    99  // Topics finds topics via various criteria. Results are sorted by best match.
   100  // Please see https://help.github.com/en/articles/searching-topics for more
   101  // information about search qualifiers.
   102  //
   103  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-topics
   104  func (s *SearchService) Topics(ctx context.Context, query string, opts *SearchOptions) (*TopicsSearchResult, *Response, error) {
   105  	result := new(TopicsSearchResult)
   106  	resp, err := s.search(ctx, "topics", &searchParameters{Query: query}, opts, result)
   107  	return result, resp, err
   108  }
   109  
   110  // CommitsSearchResult represents the result of a commits search.
   111  type CommitsSearchResult struct {
   112  	Total             *int            `json:"total_count,omitempty"`
   113  	IncompleteResults *bool           `json:"incomplete_results,omitempty"`
   114  	Commits           []*CommitResult `json:"items,omitempty"`
   115  }
   116  
   117  // CommitResult represents a commit object as returned in commit search endpoint response.
   118  type CommitResult struct {
   119  	SHA         *string   `json:"sha,omitempty"`
   120  	Commit      *Commit   `json:"commit,omitempty"`
   121  	Author      *User     `json:"author,omitempty"`
   122  	Committer   *User     `json:"committer,omitempty"`
   123  	Parents     []*Commit `json:"parents,omitempty"`
   124  	HTMLURL     *string   `json:"html_url,omitempty"`
   125  	URL         *string   `json:"url,omitempty"`
   126  	CommentsURL *string   `json:"comments_url,omitempty"`
   127  
   128  	Repository *Repository `json:"repository,omitempty"`
   129  	Score      *float64    `json:"score,omitempty"`
   130  }
   131  
   132  // Commits searches commits via various criteria.
   133  //
   134  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-commits
   135  func (s *SearchService) Commits(ctx context.Context, query string, opts *SearchOptions) (*CommitsSearchResult, *Response, error) {
   136  	result := new(CommitsSearchResult)
   137  	resp, err := s.search(ctx, "commits", &searchParameters{Query: query}, opts, result)
   138  	return result, resp, err
   139  }
   140  
   141  // IssuesSearchResult represents the result of an issues search.
   142  type IssuesSearchResult struct {
   143  	Total             *int     `json:"total_count,omitempty"`
   144  	IncompleteResults *bool    `json:"incomplete_results,omitempty"`
   145  	Issues            []*Issue `json:"items,omitempty"`
   146  }
   147  
   148  // Issues searches issues via various criteria.
   149  //
   150  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-issues-and-pull-requests
   151  func (s *SearchService) Issues(ctx context.Context, query string, opts *SearchOptions) (*IssuesSearchResult, *Response, error) {
   152  	result := new(IssuesSearchResult)
   153  	resp, err := s.search(ctx, "issues", &searchParameters{Query: query}, opts, result)
   154  	return result, resp, err
   155  }
   156  
   157  // UsersSearchResult represents the result of a users search.
   158  type UsersSearchResult struct {
   159  	Total             *int    `json:"total_count,omitempty"`
   160  	IncompleteResults *bool   `json:"incomplete_results,omitempty"`
   161  	Users             []*User `json:"items,omitempty"`
   162  }
   163  
   164  // Users searches users via various criteria.
   165  //
   166  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-users
   167  func (s *SearchService) Users(ctx context.Context, query string, opts *SearchOptions) (*UsersSearchResult, *Response, error) {
   168  	result := new(UsersSearchResult)
   169  	resp, err := s.search(ctx, "users", &searchParameters{Query: query}, opts, result)
   170  	return result, resp, err
   171  }
   172  
   173  // Match represents a single text match.
   174  type Match struct {
   175  	Text    *string `json:"text,omitempty"`
   176  	Indices []int   `json:"indices,omitempty"`
   177  }
   178  
   179  // TextMatch represents a text match for a SearchResult
   180  type TextMatch struct {
   181  	ObjectURL  *string  `json:"object_url,omitempty"`
   182  	ObjectType *string  `json:"object_type,omitempty"`
   183  	Property   *string  `json:"property,omitempty"`
   184  	Fragment   *string  `json:"fragment,omitempty"`
   185  	Matches    []*Match `json:"matches,omitempty"`
   186  }
   187  
   188  func (tm TextMatch) String() string {
   189  	return Stringify(tm)
   190  }
   191  
   192  // CodeSearchResult represents the result of a code search.
   193  type CodeSearchResult struct {
   194  	Total             *int          `json:"total_count,omitempty"`
   195  	IncompleteResults *bool         `json:"incomplete_results,omitempty"`
   196  	CodeResults       []*CodeResult `json:"items,omitempty"`
   197  }
   198  
   199  // CodeResult represents a single search result.
   200  type CodeResult struct {
   201  	Name        *string      `json:"name,omitempty"`
   202  	Path        *string      `json:"path,omitempty"`
   203  	SHA         *string      `json:"sha,omitempty"`
   204  	HTMLURL     *string      `json:"html_url,omitempty"`
   205  	Repository  *Repository  `json:"repository,omitempty"`
   206  	TextMatches []*TextMatch `json:"text_matches,omitempty"`
   207  }
   208  
   209  func (c CodeResult) String() string {
   210  	return Stringify(c)
   211  }
   212  
   213  // Code searches code via various criteria.
   214  //
   215  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-code
   216  func (s *SearchService) Code(ctx context.Context, query string, opts *SearchOptions) (*CodeSearchResult, *Response, error) {
   217  	result := new(CodeSearchResult)
   218  	resp, err := s.search(ctx, "code", &searchParameters{Query: query}, opts, result)
   219  	return result, resp, err
   220  }
   221  
   222  // LabelsSearchResult represents the result of a code search.
   223  type LabelsSearchResult struct {
   224  	Total             *int           `json:"total_count,omitempty"`
   225  	IncompleteResults *bool          `json:"incomplete_results,omitempty"`
   226  	Labels            []*LabelResult `json:"items,omitempty"`
   227  }
   228  
   229  // LabelResult represents a single search result.
   230  type LabelResult struct {
   231  	ID          *int64   `json:"id,omitempty"`
   232  	URL         *string  `json:"url,omitempty"`
   233  	Name        *string  `json:"name,omitempty"`
   234  	Color       *string  `json:"color,omitempty"`
   235  	Default     *bool    `json:"default,omitempty"`
   236  	Description *string  `json:"description,omitempty"`
   237  	Score       *float64 `json:"score,omitempty"`
   238  }
   239  
   240  func (l LabelResult) String() string {
   241  	return Stringify(l)
   242  }
   243  
   244  // Labels searches labels in the repository with ID repoID via various criteria.
   245  //
   246  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#search-labels
   247  func (s *SearchService) Labels(ctx context.Context, repoID int64, query string, opts *SearchOptions) (*LabelsSearchResult, *Response, error) {
   248  	result := new(LabelsSearchResult)
   249  	resp, err := s.search(ctx, "labels", &searchParameters{RepositoryID: &repoID, Query: query}, opts, result)
   250  	return result, resp, err
   251  }
   252  
   253  // Helper function that executes search queries against different
   254  // GitHub search types (repositories, commits, code, issues, users, labels)
   255  //
   256  // If searchParameters.Query includes multiple condition, it MUST NOT include "+" as condition separator.
   257  // For example, querying with "language:c++" and "leveldb", then searchParameters.Query should be "language:c++ leveldb" but not "language:c+++leveldb".
   258  func (s *SearchService) search(ctx context.Context, searchType string, parameters *searchParameters, opts *SearchOptions, result interface{}) (*Response, error) {
   259  	params, err := qs.Values(opts)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	if parameters.RepositoryID != nil {
   264  		params.Set("repository_id", strconv.FormatInt(*parameters.RepositoryID, 10))
   265  	}
   266  	params.Set("q", parameters.Query)
   267  	u := fmt.Sprintf("search/%s?%s", searchType, params.Encode())
   268  
   269  	req, err := s.client.NewRequest("GET", u, nil)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	switch {
   275  	case searchType == "commits":
   276  		// Accept header for search commits preview endpoint
   277  		// TODO: remove custom Accept header when this API fully launches.
   278  		req.Header.Set("Accept", mediaTypeCommitSearchPreview)
   279  	case searchType == "topics":
   280  		// Accept header for search repositories based on topics preview endpoint
   281  		// TODO: remove custom Accept header when this API fully launches.
   282  		req.Header.Set("Accept", mediaTypeTopicsPreview)
   283  	case searchType == "repositories":
   284  		// Accept header for search repositories based on topics preview endpoint
   285  		// TODO: remove custom Accept header when this API fully launches.
   286  		req.Header.Set("Accept", mediaTypeTopicsPreview)
   287  	case opts != nil && opts.TextMatch:
   288  		// Accept header defaults to "application/vnd.github.v3+json"
   289  		// We change it here to fetch back text-match metadata
   290  		req.Header.Set("Accept", "application/vnd.github.v3.text-match+json")
   291  	}
   292  
   293  	return s.client.Do(ctx, req, result)
   294  }
   295  

View as plain text