mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
* removes unused filesize function * removes ioutil usage * updates ioutil.ReadAll to io.ReadAll * updates ioutil.TempFile to os.CreateTemp * updates ioutil.TempDir to os.MkdirTemp * updates ioutil.ReadAll to os.ReadAll * update ioutil.WriteFile to os.WriteFile * updates ioutil.Discard to io.Discard * updates ioutil.ReadDir to os.ReadDir where applicable * removes unused code in idk * creates type to use for context value keys * replaces assert.Nil with assert.NoError for error checks
307 lines
9 KiB
Go
307 lines
9 KiB
Go
package client
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/logger"
|
|
"github.com/featurebasedb/featurebase/v3/test"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestIngestAPIBatchAdd(t *testing.T) {
|
|
t.Run("unkeyed", func(t *testing.T) {
|
|
batch := NewIngestAPIBatch(nil, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "a",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
},
|
|
},
|
|
{
|
|
name: "b",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
keys: true,
|
|
},
|
|
},
|
|
{
|
|
name: "c",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeTime,
|
|
keys: true,
|
|
},
|
|
},
|
|
})
|
|
qt := QuantizedTime{}
|
|
qt.Set(time.Date(2007, time.January, 1, 15, 0, 0, 0, time.UTC))
|
|
err := batch.Add(Row{
|
|
ID: uint64(1),
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("adding row to batch: %v", err)
|
|
}
|
|
|
|
if batch.records[1]["a"] != uint64(2) {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["b"] != "bkey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["c"].(map[string]interface{})["time"] != "2007-01-01T15:00:00Z" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["c"].(map[string]interface{})["values"] != "ckey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("keyed", func(t *testing.T) {
|
|
batch := NewIngestAPIBatch(nil, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "a",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
},
|
|
},
|
|
{
|
|
name: "b",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
keys: true,
|
|
},
|
|
},
|
|
{
|
|
name: "c",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeTime,
|
|
keys: true,
|
|
},
|
|
},
|
|
})
|
|
qt := QuantizedTime{}
|
|
qt.Set(time.Date(2007, time.January, 1, 15, 0, 0, 0, time.UTC))
|
|
err := batch.Add(Row{
|
|
ID: "1",
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
|
|
checkResult := func(batch *ingestAPIBatch, id string, err error) {
|
|
if err != nil {
|
|
t.Fatalf("adding row to batch: %v", err)
|
|
}
|
|
|
|
if batch.recordsK[id]["a"] != uint64(2) {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["b"] != "bkey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["c"].(map[string]interface{})["time"] != "2007-01-01T15:00:00Z" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["c"].(map[string]interface{})["values"] != "ckey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
}
|
|
checkResult(batch, "1", err)
|
|
|
|
// test wrong type row ID
|
|
if err := batch.Add(Row{ID: 64.5}); !strings.Contains(err.Error(), "unsupported rowID") {
|
|
t.Fatalf("unexpected error w/ floating point rowID: %v", err)
|
|
}
|
|
|
|
// test that byte slice ID works same as string
|
|
err = batch.Add(Row{
|
|
ID: []byte("2"),
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
checkResult(batch, "2", err)
|
|
|
|
})
|
|
}
|
|
|
|
func TestIngestAPIBatch(t *testing.T) {
|
|
c := test.MustRunCluster(t, 3)
|
|
defer c.Close()
|
|
|
|
urls := make([]string, len(c.Nodes))
|
|
for i, n := range c.Nodes {
|
|
urls[i] = n.URL()
|
|
}
|
|
|
|
// Create a new client for the cluster
|
|
cli, err := newClientFromAddresses(urls, &ClientOptions{})
|
|
if err != nil {
|
|
t.Fatalf("getting new client: %v", err)
|
|
}
|
|
defer cli.Close()
|
|
|
|
_, err = cli.IngestSchema(map[string]interface{}{
|
|
"index-name": "test-1",
|
|
"index-action": "create",
|
|
"primary-key-type": "uint",
|
|
"field-action": "create",
|
|
"fields": []map[string]interface{}{
|
|
{
|
|
"field-name": "astr",
|
|
"field-type": "string",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "bint",
|
|
"field-type": "int",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "cid",
|
|
"field-type": "id",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "dtimestamp",
|
|
"field-type": "timestamp",
|
|
"field-options": map[string]interface{}{
|
|
"unit": "s",
|
|
},
|
|
},
|
|
{
|
|
"field-name": "etime",
|
|
"field-type": "string",
|
|
"field-options": map[string]interface{}{
|
|
"time-quantum": "YMD",
|
|
},
|
|
},
|
|
{
|
|
"field-name": "fdecimal",
|
|
"field-type": "decimal",
|
|
"field-options": map[string]interface{}{
|
|
"scale": 3,
|
|
},
|
|
},
|
|
{
|
|
"field-name": "gbool",
|
|
"field-type": "bool",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
},
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
schema, err := cli.Schema()
|
|
if err != nil {
|
|
t.Fatalf("getting schema: %v", err)
|
|
}
|
|
index := schema.Index("test-1")
|
|
defer cli.DeleteIndex(index)
|
|
|
|
batch := NewIngestAPIBatch(cli, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "astr",
|
|
index: &Index{name: "test-1", options: &IndexOptions{}},
|
|
options: &FieldOptions{fieldType: FieldTypeSet, keys: true},
|
|
},
|
|
{
|
|
name: "bint",
|
|
options: &FieldOptions{fieldType: FieldTypeInt},
|
|
},
|
|
{
|
|
name: "cid",
|
|
options: &FieldOptions{fieldType: FieldTypeSet, keys: false},
|
|
},
|
|
{
|
|
name: "dtimestamp",
|
|
options: &FieldOptions{fieldType: FieldTypeTimestamp},
|
|
},
|
|
{
|
|
name: "etime",
|
|
options: &FieldOptions{fieldType: FieldTypeTime, keys: true, timeQuantum: TimeQuantumYearMonthDay},
|
|
},
|
|
{
|
|
name: "fdecimal",
|
|
options: &FieldOptions{fieldType: FieldTypeDecimal, scale: 3},
|
|
},
|
|
{
|
|
name: "gbool",
|
|
options: &FieldOptions{fieldType: FieldTypeBool},
|
|
},
|
|
})
|
|
|
|
qt0 := &QuantizedTime{}
|
|
qt0.Set(time.Date(2010, time.January, 1, 0, 0, 0, 0, time.UTC))
|
|
if err := batch.Add(Row{
|
|
ID: uint64(7),
|
|
Values: []interface{}{"a", -2, 9, 1287367623, "e", 1.2345, true},
|
|
Time: *qt0,
|
|
}); err != nil {
|
|
t.Fatalf("adding row: %v", err)
|
|
}
|
|
|
|
// test nil value case
|
|
if err := batch.Add(Row{
|
|
ID: uint64(8),
|
|
Values: []interface{}{nil, nil, nil, nil, nil, nil, nil},
|
|
Time: QuantizedTime{},
|
|
}); err != nil {
|
|
t.Fatalf("error adding all nil batch which should affect nothing: %v", err)
|
|
}
|
|
|
|
if err := batch.Import(); err != nil {
|
|
t.Fatalf("importing row: %v", err)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(astr=a)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(asr=a) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(bint==-2)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(bint==-2) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(cid=9)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(cid=9) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(dtimestamp=='2010-10-18T02:07:03Z')", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(dtimestamp=='2010-10-18T02:07:03Z') result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(etime=e, from='2010-01-01', to='2010-01-02')", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(etime=e, from='2010-01-01', to='2010-01-02') result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(fdecimal==1.234)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(fdecimal==1.234) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(gbool=true)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(gbool=true) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
}
|