blob: 97e3a6991d4b5948fd3181ae5f4f1e82be55fd73 [file] [log] [blame]
// search contains the core functionality for searching for digests across a tile.
package search
import (
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/go/tiling"
"go.skia.org/infra/golden/go/indexer"
"go.skia.org/infra/golden/go/types"
)
// AcceptFn is the callback function used by iterTile to determine whether to
// include a trace into the result or not. The second return value is an
// intermediate result that will be passed to addFn to avoid redundant computation.
// The second return value is application dependent since it will be passed
// into the call to the corresponding AddFn. Determining whether to accept a
// result might require an expensive computation and we want to avoid repeating
// that computation in the 'add' step. So we can return it here and it will
// be passed into the instance of AddFn.
type AcceptFn func(params paramtools.Params, digests []string) (bool, interface{})
// AddFn is the callback function used by iterTile to add a digest and it's
// trace to the result. acceptResult is the same value returned by the AcceptFn.
type AddFn func(test, digest, traceID string, trace *types.GoldenTrace, acceptResult interface{})
// iterTile is a generic function to extract information from a tile.
// It iterates over the tile and filters against the given query. If calls
// acceptFn to determine whether to keep a trace (after it has already been
// tested against the query) and calls addFn to add a digest and its trace.
// acceptFn == nil equals unconditional acceptance.
func iterTile(query *Query, addFn AddFn, acceptFn AcceptFn, exp ExpSlice, idx *indexer.SearchIndex) error {
tile := idx.GetTile(query.IncludeIgnores)
if acceptFn == nil {
acceptFn = func(params paramtools.Params, digests []string) (bool, interface{}) { return true, nil }
}
traceTally := idx.TalliesByTrace(query.IncludeIgnores)
lastTraceIdx, traceView, err := getTraceViewFn(tile, query.FCommitBegin, query.FCommitEnd)
if err != nil {
return err
}
// Iterate through the tile.
for id, trace := range tile.Traces {
// Check if the query matches.
if tiling.Matches(trace, query.Query) {
fullTr := trace.(*types.GoldenTrace)
params := fullTr.Params_
reducedTr := traceView(fullTr)
digests := digestsFromTrace(id, reducedTr, query.Head, lastTraceIdx, traceTally)
// If there is an acceptFn defined then check whether
// we should include this trace.
ok, acceptRet := acceptFn(params, digests)
if !ok {
continue
}
// Iterate over the digess and filter them.
test := params[types.PRIMARY_KEY_FIELD]
for _, digest := range digests {
cl := exp.Classification(test, digest)
if query.excludeClassification(cl) {
continue
}
// Fix blamer to make this easier.
if query.BlameGroupID != "" {
if cl == types.UNTRIAGED {
b := idx.GetBlame(test, digest, tile.Commits)
if query.BlameGroupID != blameGroupID(b, tile.Commits) {
continue
}
} else {
continue
}
}
// Add the digest to the results
addFn(test, digest, id, fullTr, acceptRet)
}
}
}
return nil
}
// traceViewFn returns a view of a trace that contains a subset of values but the same params.
type traceViewFn func(*types.GoldenTrace) *types.GoldenTrace
// traceViewIdentity is a no-op traceViewFn that returns the exact trace that it
// receives. This is used when no commit range is provided in the query.
func traceViewIdentity(tr *types.GoldenTrace) *types.GoldenTrace {
return tr
}
// getTraceViewFn returns a traceViewFn for the given Git hashes as well as the
// index of the last value in the resulting traces.
// If startHash occurs after endHash in the tile, an error is returned.
func getTraceViewFn(tile *tiling.Tile, startHash, endHash string) (int, traceViewFn, error) {
if startHash == "" && endHash == "" {
return tile.LastCommitIndex(), traceViewIdentity, nil
}
// Find the indices to slice the values of the trace.
startIdx, _ := tiling.FindCommit(tile.Commits, startHash)
endIdx, _ := tiling.FindCommit(tile.Commits, endHash)
if (startIdx == -1) && (endIdx == -1) {
return tile.LastCommitIndex(), traceViewIdentity, nil
}
// If either was not found set it to the beginning/end.
if startIdx == -1 {
startIdx = 0
} else if endIdx == -1 {
endIdx = tile.LastCommitIndex()
}
// Increment the last index for the slice operation in the function below.
endIdx++
if startIdx >= endIdx {
return 0, nil, sklog.FmtErrorf("Start commit occurs later than end commit.")
}
ret := func(trace *types.GoldenTrace) *types.GoldenTrace {
return &types.GoldenTrace{
Values: trace.Values[startIdx:endIdx],
Params_: trace.Params_,
}
}
return (endIdx - startIdx) - 1, ret, nil
}