featurebase/delete_test.go
2021-04-12 13:38:58 -05:00

232 lines
8.5 KiB
Go

// Copyright 2021 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pilosa_test
import (
"context"
"fmt"
"math"
"testing"
"time"
"github.com/pilosa/pilosa/v2"
"github.com/pilosa/pilosa/v2/test"
"github.com/stretchr/testify/require"
)
func TestExecutor_DeleteRecords(t *testing.T) {
pilosa.NotBlueGreenTest(t)
indexName := "i"
setup := func(t *testing.T, r *require.Assertions, c *test.Cluster) {
t.Helper()
c.CreateField(t, indexName, pilosa.IndexOptions{TrackExistence: true}, "setfield")
c.ImportBits(t, indexName, "setfield", [][2]uint64{
{0, 0},
{0, 1},
{0, ShardWidth + 2},
{10, 2},
{10, ShardWidth},
{10, 2 * ShardWidth},
{10, ShardWidth + 1},
{20, ShardWidth},
})
c.CreateField(t, indexName, pilosa.IndexOptions{TrackExistence: true}, "bsi", pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64))
c.ImportIntID(t, indexName, "bsi", []test.IntID{
{ID: 0, Val: 4},
{ID: 2, Val: 8},
})
c.CreateField(t, indexName, pilosa.IndexOptions{TrackExistence: true}, "timefield", pilosa.OptFieldTypeTime(pilosa.TimeQuantum("YMDH")))
c.ImportBitsWithTimestamp(t, indexName, "timefield", [][2]uint64{
{0, 0},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
}, []int64{
time.Date(2020, time.January, 2, 15, 45, 0, 0, time.UTC).Unix(),
time.Date(2019, time.January, 2, 16, 45, 0, 0, time.UTC).Unix(),
time.Date(2019, time.January, 2, 16, 45, 0, 0, time.UTC).Unix(),
time.Date(2019, time.January, 2, 17, 45, 0, 0, time.UTC).Unix(),
time.Date(2019, time.January, 2, 17, 45, 0, 0, time.UTC).Unix(),
})
}
setupKeys := func(t *testing.T, r *require.Assertions, c *test.Cluster) {
t.Helper()
c.CreateField(t, indexName, pilosa.IndexOptions{Keys: true, TrackExistence: true}, "timefield", pilosa.OptFieldKeys(), pilosa.OptFieldTypeTime(pilosa.TimeQuantum("YMDH")))
c.ImportTimeQuantumKey(t, indexName, "timefield", []test.TimeQuantumKey{
{RowKey: "fish", ColKey: "one", Ts: time.Date(2019, time.January, 2, 17, 45, 0, 0, time.UTC).Unix()},
{RowKey: "fish", ColKey: "one", Ts: time.Date(2020, time.January, 2, 17, 45, 0, 0, time.UTC).Unix()},
{RowKey: "fish", ColKey: "two", Ts: time.Date(2019, time.January, 3, 17, 45, 0, 0, time.UTC).Unix()},
})
c.CreateField(t, indexName, pilosa.IndexOptions{Keys: true, TrackExistence: true}, "keystuff")
c.ImportIDKey(t, indexName, "keystuff", []test.KeyID{
{ID: 1, Key: "A"},
{ID: 2, Key: "B"},
{ID: 3, Key: "C"},
{ID: 4, Key: "D"},
})
}
setupOverlap := func(t *testing.T, r *require.Assertions, c *test.Cluster) {
t.Helper()
c.CreateField(t, indexName, pilosa.IndexOptions{TrackExistence: true}, "setfield")
c.ImportBits(t, indexName, "setfield", [][2]uint64{
{0, 0},
{0, 1},
{1, 1},
{2, 1},
{3, 1},
{0, ShardWidth},
{2, ShardWidth},
{4, ShardWidth},
{6, ShardWidth},
})
}
tearDown := func(t *testing.T, require *require.Assertions, c *test.Cluster) {
t.Helper()
api := c.GetPrimary().API
err := api.DeleteIndex(context.Background(), indexName)
require.NoErrorf(err, "DeleteIndex %v", indexName)
}
require := require.New(t)
t.Run("DeleteRecords", func(t *testing.T) {
c := test.MustNewCluster(t, 1)
for _, n := range c.Nodes {
n.Config.Cluster.ReplicaN = 1
}
err := c.Start()
require.NoError(err, "Start cluster DeleteRecords")
defer c.Close()
t.Run("Delete", func(t *testing.T) {
setup(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Extract(All())`)
m := resp.Results[0].(pilosa.ExtractedTable)
before := convert(m.Columns)
require.Equal([]uint64{0, 1, 2, ShardWidth, ShardWidth + 1, ShardWidth + 2, 2 * ShardWidth}, before, "these records are expected")
resp = c.Query(t, indexName, fmt.Sprintf(`Delete(ConstRow(columns=[1,2,3,%v]))`, ShardWidth+1))
require.NotNil(resp, "Response should not be nil")
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Extract(All())`)
//Note none of the removed records should remain
m = resp.Results[0].(pilosa.ExtractedTable)
after := convert(m.Columns)
require.Equal([]uint64{0, ShardWidth, ShardWidth + 2, 2 * ShardWidth}, after, "these records should be remaining")
})
t.Run("DeleteKey", func(t *testing.T) {
setupKeys(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Extract(All())`)
m := resp.Results[0].(pilosa.ExtractedTable)
before := convertKey(m.Columns)
require.Equal([]string{"one", "A", "B", "C", "D", "two"}, before, "these keyed records before")
resp = c.Query(t, indexName, `Delete(ConstRow(columns=["A","one"]))`)
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Extract(All())`)
m = resp.Results[0].(pilosa.ExtractedTable)
after := convertKey(m.Columns)
require.Equal([]string{"B", "C", "D", "two"}, after, "these keyed records after delete")
})
t.Run("Delete Row", func(t *testing.T) {
setup(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Delete(Row(setfield=20))`)
require.NotNil(resp, "Response should not be nil")
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Extract(All())`)
//Note none of the removed records should remain
m := resp.Results[0].(pilosa.ExtractedTable)
after := convert(m.Columns)
require.Equal([]uint64{0, 1, 2, ShardWidth + 1, ShardWidth + 2, 2 * ShardWidth}, after, "these records are expected")
})
t.Run("Delete Not Row", func(t *testing.T) {
setup(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Delete(Not(Row(setfield=20)))`)
require.NotNil(resp, "Response should not be nil")
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Extract(All())`)
//Note none of the removed records should remain
m := resp.Results[0].(pilosa.ExtractedTable)
after := convert(m.Columns)
require.Equal([]uint64{ShardWidth}, after, "these records are expected")
})
t.Run("Delete All", func(t *testing.T) {
setup(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Count(All())`)
//Note none of the removed records should remain
before := resp.Results[0].(uint64)
require.Equal(uint64(7), before, "these records are expected")
resp = c.Query(t, indexName, `Delete(All())`)
require.NotNil(resp, "Response should not be nil")
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Count(All())`)
//Note none of the removed records should remain
after := resp.Results[0].(uint64)
require.Equal(uint64(0), after, "these records are expected")
})
t.Run("DeleteOverlap", func(t *testing.T) {
setupOverlap(t, require, c)
defer tearDown(t, require, c)
resp := c.Query(t, indexName, `Extract(All())`)
m := resp.Results[0].(pilosa.ExtractedTable)
before := convert(m.Columns)
require.Equal([]uint64{0, 1, ShardWidth}, before, "these records are expected")
resp = c.Query(t, indexName, fmt.Sprintf(`Delete(ConstRow(columns=[%v]))`, ShardWidth))
require.NotNil(resp, "Response should not be nil")
require.NotEmpty(resp.Results)
require.Equal(true, resp.Results[0], "Change should have happened")
resp = c.Query(t, indexName, `Extract(All())`)
//Note none of the removed records should remain
m = resp.Results[0].(pilosa.ExtractedTable)
after := convert(m.Columns)
require.Equal([]uint64{0, 1}, after, "these records should be remaining")
})
})
}
func convert(before []pilosa.ExtractedTableColumn) []uint64 {
result := make([]uint64, 0)
for _, i := range before {
result = append(result, i.Column.ID)
}
return result
}
func convertKey(before []pilosa.ExtractedTableColumn) []string {
result := make([]string, 0)
for _, i := range before {
result = append(result, i.Column.Key)
}
return result
}