in web/server.go [193:288]
func (s *Server) serveSearchErr(w http.ResponseWriter, r *http.Request) error {
qvals := r.URL.Query()
queryStr := qvals.Get("q")
if queryStr == "" {
return fmt.Errorf("no query found")
}
q, err := query.Parse(queryStr)
if err != nil {
return err
}
repoOnly := true
query.VisitAtoms(q, func(q query.Q) {
_, ok := q.(*query.Repo)
repoOnly = repoOnly && ok
})
if repoOnly {
return s.serveListReposErr(q, queryStr, w, r)
}
numStr := qvals.Get("num")
num, err := strconv.Atoi(numStr)
if err != nil || num <= 0 {
num = defaultNumResults
}
sOpts := zoekt.SearchOptions{
MaxWallTime: 10 * time.Second,
}
sOpts.SetDefaults()
ctx := r.Context()
if result, err := s.Searcher.Search(ctx, q, &zoekt.SearchOptions{EstimateDocCount: true}); err != nil {
return err
} else if numdocs := result.ShardFilesConsidered; numdocs > 10000 {
// If the search touches many shards and many files, we
// have to limit the number of matches. This setting
// is based on the number of documents eligible after
// considering reponames, so large repos (both
// android, chromium are about 500k files) aren't
// covered fairly.
// 10k docs, 50 num -> max match = (250 + 250 / 10)
sOpts.ShardMaxMatchCount = num*5 + (5*num)/(numdocs/1000)
// 10k docs, 50 num -> max important match = 4
sOpts.ShardMaxImportantMatch = num/20 + num/(numdocs/500)
} else {
// Virtually no limits for a small corpus; important
// matches are just as expensive as normal matches.
n := numdocs + num*100
sOpts.ShardMaxImportantMatch = n
sOpts.ShardMaxMatchCount = n
sOpts.TotalMaxMatchCount = n
sOpts.TotalMaxImportantMatch = n
}
sOpts.MaxDocDisplayCount = num
result, err := s.Searcher.Search(ctx, q, &sOpts)
if err != nil {
return err
}
fileMatches, err := s.formatResults(result, queryStr, s.Print)
if err != nil {
return err
}
res := ResultInput{
Last: LastInput{
Query: queryStr,
Num: num,
AutoFocus: true,
},
Stats: result.Stats,
Query: q.String(),
QueryStr: queryStr,
SearchOptions: sOpts.String(),
FileMatches: fileMatches,
}
if res.Stats.Wait < res.Stats.Duration/10 {
// Suppress queueing stats if they are neglible.
res.Stats.Wait = 0
}
var buf bytes.Buffer
if err := s.result.Execute(&buf, &res); err != nil {
return err
}
w.Write(buf.Bytes())
return nil
}