blob: 3802d78901b9e0542ba911e09807d80a8aadddce [file] [log] [blame]
package paramreducer
/*
paramreducer produces a new ParamSet from a ParamSet and a Query against that ParamSet.
Given a corpus of traces that have ids of:
config=8888,cpu=x86,res=ms
config=565,cpu=x86,res=count
config=565,cpu=arm,res=cov
config=gles,cpu=arm,res=bytes
And a query against that corpus:
config=565,res=[cov,bytes]
We want to return a paramset that represents all the legal options for queries
against the subset of traces that match the query:
cpu=[arm]
config=[565, gles]
res=[count, cov]
I.e. our query matches the corpus as follows:
config=8888,cpu=x86,res=ms
** config=565,cpu=x86,res=count
** * config=565,cpu=arm,res=cov
* config=gles,cpu=arm,res=bytes
** = matches config=565 => res=[count, cov], cpu=[arm, x86]
* = matches res=[cov, bytes] => config=[565, gles], cpu=[arm]
*/
import (
"fmt"
"net/url"
"sort"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/query"
"go.skia.org/infra/go/util"
)
type SubQuery struct {
q *query.Query
ps paramtools.ParamSet
}
func (s *SubQuery) Add(key string) {
if s.q.Matches(key) {
s.ps.AddParamsFromKey(key)
}
}
type Reducer struct {
full paramtools.ParamSet
subQueries map[string]*SubQuery
}
// New returns a new Reducer for the given query and full paramset.
func New(q url.Values, full paramtools.ParamSet) (*Reducer, error) {
// Break apart the query into sub-queries, one for each key present in the query.
subQueries := map[string]*SubQuery{}
for key, values := range q {
subQuery, err := query.New(url.Values{
key: values,
})
if err != nil {
return nil, fmt.Errorf("Invalid query: %s", err)
}
subQueries[key] = &SubQuery{
q: subQuery,
ps: paramtools.ParamSet{},
}
}
return &Reducer{
full: full,
subQueries: subQueries,
}, nil
}
// Add a structured key from the full corpus.
func (r *Reducer) Add(key string) {
for _, sub := range r.subQueries {
sub.Add(key)
}
}
// Reduce all the data to a final ParamSet that represents
// all the valid options left in full ParamSet.
func (r *Reducer) Reduce() paramtools.ParamSet {
ret := paramtools.ParamSet{}
for full_k, full_v := range r.full {
ss := util.NewStringSet(full_v)
for k, sub := range r.subQueries {
if k != full_k {
ss = ss.Intersect(util.NewStringSet(sub.ps[full_k]))
}
}
values := ss.Keys()
sort.Strings(values)
if len(values) > 0 {
ret[full_k] = values
}
}
return ret
}