Merge pull request #6047 from influxdata/js-6040-boolean-distinct

Support the distinct() call for booleans
pull/6059/head
Jonathan A. Sternberg 2016-03-17 17:17:21 -04:00
commit 43a5e84aaf
4 changed files with 61 additions and 22 deletions

View File

@ -2285,13 +2285,13 @@ func TestServer_Query_Aggregates_IntMany(t *testing.T) {
name: "distinct as call - int",
params: url.Values{"db": []string{"db0"}},
command: `SELECT DISTINCT(value) FROM intmany`,
exp: `{"results":[{"series":[{"name":"intmany","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",2],["2000-01-01T00:00:10Z",4],["2000-01-01T00:00:40Z",5],["2000-01-01T00:01:00Z",7],["2000-01-01T00:01:10Z",9]]}]}]}`,
exp: `{"results":[{"series":[{"name":"intmany","columns":["time","distinct"],"values":[["1970-01-01T00:00:00Z",2],["1970-01-01T00:00:00Z",4],["1970-01-01T00:00:00Z",5],["1970-01-01T00:00:00Z",7],["1970-01-01T00:00:00Z",9]]}]}]}`,
},
&Query{
name: "distinct alt syntax - int",
params: url.Values{"db": []string{"db0"}},
command: `SELECT DISTINCT value FROM intmany`,
exp: `{"results":[{"series":[{"name":"intmany","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",2],["2000-01-01T00:00:10Z",4],["2000-01-01T00:00:40Z",5],["2000-01-01T00:01:00Z",7],["2000-01-01T00:01:10Z",9]]}]}]}`,
exp: `{"results":[{"series":[{"name":"intmany","columns":["time","distinct"],"values":[["1970-01-01T00:00:00Z",2],["1970-01-01T00:00:00Z",4],["1970-01-01T00:00:00Z",5],["1970-01-01T00:00:00Z",7],["1970-01-01T00:00:00Z",9]]}]}]}`,
},
&Query{
name: "distinct select tag - int",
@ -2659,13 +2659,13 @@ func TestServer_Query_Aggregates_FloatMany(t *testing.T) {
name: "distinct as call - float",
params: url.Values{"db": []string{"db0"}},
command: `SELECT DISTINCT(value) FROM floatmany`,
exp: `{"results":[{"series":[{"name":"floatmany","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",2],["2000-01-01T00:00:10Z",4],["2000-01-01T00:00:40Z",5],["2000-01-01T00:01:00Z",7],["2000-01-01T00:01:10Z",9]]}]}]}`,
exp: `{"results":[{"series":[{"name":"floatmany","columns":["time","distinct"],"values":[["1970-01-01T00:00:00Z",2],["1970-01-01T00:00:00Z",4],["1970-01-01T00:00:00Z",5],["1970-01-01T00:00:00Z",7],["1970-01-01T00:00:00Z",9]]}]}]}`,
},
&Query{
name: "distinct alt syntax - float",
params: url.Values{"db": []string{"db0"}},
command: `SELECT DISTINCT value FROM floatmany`,
exp: `{"results":[{"series":[{"name":"floatmany","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",2],["2000-01-01T00:00:10Z",4],["2000-01-01T00:00:40Z",5],["2000-01-01T00:01:00Z",7],["2000-01-01T00:01:10Z",9]]}]}]}`,
exp: `{"results":[{"series":[{"name":"floatmany","columns":["time","distinct"],"values":[["1970-01-01T00:00:00Z",2],["1970-01-01T00:00:00Z",4],["1970-01-01T00:00:00Z",5],["1970-01-01T00:00:00Z",7],["1970-01-01T00:00:00Z",9]]}]}]}`,
},
&Query{
name: "distinct select tag - float",
@ -3134,7 +3134,7 @@ func TestServer_Query_AggregateSelectors(t *testing.T) {
name: "distinct - baseline 30s",
params: url.Values{"db": []string{"db0"}},
command: `SELECT distinct(rx) FROM network where time >= '2000-01-01T00:00:00Z' AND time <= '2000-01-01T00:01:29Z' group by time(30s)`,
exp: `{"results":[{"series":[{"name":"network","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",10],["2000-01-01T00:00:10Z",40],["2000-01-01T00:00:30Z",40],["2000-01-01T00:00:40Z",50],["2000-01-01T00:01:00Z",70],["2000-01-01T00:01:10Z",90],["2000-01-01T00:01:20Z",5]]}]}]}`,
exp: `{"results":[{"series":[{"name":"network","columns":["time","distinct"],"values":[["2000-01-01T00:00:00Z",10],["2000-01-01T00:00:00Z",40],["2000-01-01T00:00:30Z",40],["2000-01-01T00:00:30Z",50],["2000-01-01T00:01:00Z",70],["2000-01-01T00:01:00Z",90],["2000-01-01T00:01:00Z",5]]}]}]}`,
},
&Query{
name: "distinct - time",

View File

@ -379,6 +379,12 @@ func NewDistinctIterator(input Iterator, opt IteratorOptions) (Iterator, error)
return fn, fn
}
return &stringReduceStringIterator{input: newBufStringIterator(input), opt: opt, create: createFn}, nil
case BooleanIterator:
createFn := func() (BooleanPointAggregator, BooleanPointEmitter) {
fn := NewBooleanSliceFuncReducer(BooleanDistinctReduceSlice)
return fn, fn
}
return &booleanReduceBooleanIterator{input: newBufBooleanIterator(input), opt: opt, create: createFn}, nil
default:
return nil, fmt.Errorf("unsupported distinct iterator type: %T", input)
}
@ -435,6 +441,23 @@ func StringDistinctReduceSlice(a []StringPoint) []StringPoint {
return points
}
// BooleanDistinctReduceSlice returns the distinct value within a window.
func BooleanDistinctReduceSlice(a []BooleanPoint) []BooleanPoint {
m := make(map[bool]BooleanPoint)
for _, p := range a {
if _, ok := m[p.Value]; !ok {
m[p.Value] = p
}
}
points := make([]BooleanPoint, 0, len(m))
for _, p := range m {
points = append(points, BooleanPoint{Time: p.Time, Value: p.Value})
}
sort.Sort(booleanPoints(points))
return points
}
// newMeanIterator returns an iterator for operating on a mean() call.
func newMeanIterator(input Iterator, opt IteratorOptions) (Iterator, error) {
switch input := input.(type) {

View File

@ -214,7 +214,11 @@ func buildExprIterator(expr Expr, ic IteratorCreator, opt IteratorOptions) (Iter
if err != nil {
return nil, err
}
return NewDistinctIterator(input, opt)
input, err = NewDistinctIterator(input, opt)
if err != nil {
return nil, err
}
return NewIntervalIterator(input, opt), nil
case "derivative", "non_negative_derivative":
input, err := buildExprIterator(expr.Args[0], ic, opt)
if err != nil {

View File

@ -66,8 +66,8 @@ func TestSelect_Distinct_Float(t *testing.T) {
t.Fatal(err)
} else if a := Iterators(itrs).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 20}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 1 * Second, Value: 19}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 5 * Second, Value: 10}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 19}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: 10}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: 2}},
}) {
t.Fatalf("unexpected points: %s", spew.Sdump(a))
@ -95,8 +95,8 @@ func TestSelect_Distinct_Integer(t *testing.T) {
t.Fatal(err)
} else if a := Iterators(itrs).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 20}},
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 1 * Second, Value: 19}},
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 5 * Second, Value: 10}},
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 19}},
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: 10}},
{&influxql.IntegerPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: 2}},
}) {
t.Fatalf("unexpected points: %s", spew.Sdump(a))
@ -124,29 +124,41 @@ func TestSelect_Distinct_String(t *testing.T) {
t.Fatal(err)
} else if a := Iterators(itrs).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: "a"}},
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 1 * Second, Value: "b"}},
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 5 * Second, Value: "c"}},
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: "b"}},
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: "c"}},
{&influxql.StringPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: "d"}},
}) {
t.Fatalf("unexpected points: %s", spew.Sdump(a))
}
}
// Ensure a SELECT distinct() query cannot be executed on booleans.
// Ensure a SELECT distinct() query can be executed.
func TestSelect_Distinct_Boolean(t *testing.T) {
var ic IteratorCreator
ic.CreateIteratorFn = func(opt influxql.IteratorOptions) (influxql.Iterator, error) {
return &BooleanIterator{}, nil
return &BooleanIterator{Points: []influxql.BooleanPoint{
{Name: "cpu", Tags: ParseTags("region=west,host=A"), Time: 0 * Second, Value: true},
{Name: "cpu", Tags: ParseTags("region=west,host=A"), Time: 1 * Second, Value: false},
{Name: "cpu", Tags: ParseTags("region=west,host=B"), Time: 5 * Second, Value: false},
{Name: "cpu", Tags: ParseTags("region=east,host=A"), Time: 9 * Second, Value: true},
{Name: "cpu", Tags: ParseTags("region=east,host=A"), Time: 10 * Second, Value: false},
{Name: "cpu", Tags: ParseTags("region=east,host=A"), Time: 11 * Second, Value: false},
{Name: "cpu", Tags: ParseTags("region=east,host=A"), Time: 12 * Second, Value: true},
}}, nil
}
// Execute selection.
itrs, err := influxql.Select(MustParseSelectStatement(`SELECT distinct(value) FROM cpu WHERE time >= '1970-01-01T00:00:00Z' AND time < '1970-01-02T00:00:00Z' GROUP BY time(10s), host fill(none)`), &ic, nil)
if err == nil || err.Error() != "unsupported distinct iterator type: *influxql_test.BooleanIterator" {
t.Errorf("unexpected error: %s", err)
}
if itrs != nil {
influxql.Iterators(itrs).Close()
if err != nil {
t.Fatal(err)
} else if a := Iterators(itrs).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.BooleanPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: true}},
{&influxql.BooleanPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: false}},
{&influxql.BooleanPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: false}},
{&influxql.BooleanPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: false}},
{&influxql.BooleanPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: true}},
}) {
t.Errorf("unexpected points: %s", spew.Sdump(a))
}
}
@ -1827,8 +1839,8 @@ func TestSelect_ParenExpr(t *testing.T) {
t.Fatal(err)
} else if a := Iterators(itrs).ReadAll(); !deep.Equal(a, [][]influxql.Point{
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 20}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 1 * Second, Value: 19}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 5 * Second, Value: 10}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 0 * Second, Value: 19}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=B"), Time: 0 * Second, Value: 10}},
{&influxql.FloatPoint{Name: "cpu", Tags: ParseTags("host=A"), Time: 10 * Second, Value: 2}},
}) {
t.Fatalf("unexpected points: %s", spew.Sdump(a))