mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-10-09 04:17:51 +00:00
Removes column/row labels for input definition. Resolves #810
This commit is contained in:
parent
9e458aecd5
commit
83be24f4d2
7 changed files with 68 additions and 78 deletions
|
|
@ -43,10 +43,6 @@ curl localhost:10101/index/repository/input-definition/stargazer \
|
|||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "repo_id",
|
||||
"primaryKey": true
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
|
|
@ -105,8 +101,8 @@ curl localhost:10101/index/repository/input/stargazer \
|
|||
-X POST \
|
||||
-d '[
|
||||
{
|
||||
"columnID": 91720568,
|
||||
"language_id": "Go",
|
||||
"repo_id": 91720568,
|
||||
"stargazer_id": 513114,
|
||||
"time_value": "2017-05-18T20:40"
|
||||
},
|
||||
|
|
|
|||
12
handler.go
12
handler.go
|
|
@ -1760,8 +1760,7 @@ func (h *Handler) handlePostInputDefinition(w http.ResponseWriter, r *http.Reque
|
|||
return
|
||||
}
|
||||
|
||||
// Validation the input definition with the curent index's ColumnLabel.
|
||||
if err := req.Validate(index.ColumnLabel()); err != nil {
|
||||
if err := req.Validate(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
|
@ -1908,10 +1907,13 @@ func (h *Handler) InputJSONDataParser(req map[string]interface{}, index *Index,
|
|||
for _, field := range inputDef.Fields() {
|
||||
validFields[field.Name] = true
|
||||
if field.PrimaryKey {
|
||||
columnLabel := field.Name
|
||||
value, ok := req[columnLabel]
|
||||
primaryKey := field.Name
|
||||
if primaryKey != DefaultColumnLabel {
|
||||
return nil, fmt.Errorf("Primary key field should have the name: %s", DefaultColumnLabel)
|
||||
}
|
||||
value, ok := req[primaryKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("columnLabel required")
|
||||
return nil, fmt.Errorf("primary key does not exist")
|
||||
}
|
||||
rawValue, ok := value.(float64) // The default JSON marshalling will interpret this as a float
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -1309,34 +1309,6 @@ func TestHandler_DuplicatePrimaryKey(t *testing.T) {
|
|||
t.Fatalf("unexpected body: %s", body)
|
||||
}
|
||||
|
||||
// Eusure throwing error if primary field's name doesn't match columnLabel
|
||||
hldr.MustCreateIndexIfNotExists("i1", pilosa.IndexOptions{ColumnLabel: "id"})
|
||||
unmatchColumnBody := []byte(`
|
||||
{
|
||||
"frames":[{
|
||||
"name":"event-time",
|
||||
"options":{
|
||||
"timeQuantum": "YMD",
|
||||
"inverseEnabled": false,
|
||||
"cacheType": "ranked"
|
||||
}
|
||||
}],
|
||||
"fields": [
|
||||
{
|
||||
"name": "columnID",
|
||||
"primaryKey": true
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("POST", "/index/i1/input-definition/input1", bytes.NewBuffer(unmatchColumnBody)))
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
} else if body := w.Body.String(); body != pilosa.ErrInputDefinitionColumnLabel.Error()+"\n" {
|
||||
t.Fatalf("unexpected body: %s", body)
|
||||
}
|
||||
|
||||
// Eusure throwing error if request body is invalid.
|
||||
jsonErrorBody := []byte(`
|
||||
{
|
||||
|
|
@ -1578,7 +1550,7 @@ func TestHandler_CreateInput(t *testing.T) {
|
|||
}
|
||||
inputBody := []byte(`
|
||||
[{
|
||||
"id": 1,
|
||||
"columnID": 1,
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"withPet": true,
|
||||
|
|
@ -1659,14 +1631,14 @@ func TestInput_JSON(t *testing.T) {
|
|||
err string
|
||||
}{
|
||||
{json: `[{
|
||||
"id": 1,
|
||||
"columnID": 1,
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"nofield": true
|
||||
}]`,
|
||||
err: "field not found: nofield"},
|
||||
{json: `[{
|
||||
"id": "abc",
|
||||
"columnID": "abc",
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"withPet": true
|
||||
|
|
@ -1677,23 +1649,23 @@ func TestInput_JSON(t *testing.T) {
|
|||
"distanceMiles": 8,
|
||||
"withPet": true
|
||||
}]`,
|
||||
err: "columnLabel required"},
|
||||
err: "primary key does not exist"},
|
||||
{json: `[{
|
||||
"id": 1,
|
||||
"columnID": 1,
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"withPet": true
|
||||
}`,
|
||||
err: "unexpected EOF"},
|
||||
{json: `[{
|
||||
"id": 1,
|
||||
"columnID": 1,
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"noFrame": 1
|
||||
}]`,
|
||||
err: "Frame not found: foo"},
|
||||
{json: `[{
|
||||
"id": 1,
|
||||
"columnID": 1,
|
||||
"cabType": "yellow",
|
||||
"distanceMiles": 8,
|
||||
"time_value": 12345
|
||||
|
|
|
|||
3
index.go
3
index.go
|
|
@ -687,7 +687,8 @@ func (i *Index) createInputDefinition(pb *internal.InputDefinition) (*InputDefin
|
|||
|
||||
for _, fr := range pb.Frames {
|
||||
opt := FrameOptions{
|
||||
RowLabel: fr.Meta.RowLabel,
|
||||
// Deprecating row labels per #810. So, setting the default row label here.
|
||||
RowLabel: DefaultRowLabel,
|
||||
InverseEnabled: fr.Meta.InverseEnabled,
|
||||
CacheType: fr.Meta.CacheType,
|
||||
CacheSize: fr.Meta.CacheSize,
|
||||
|
|
|
|||
|
|
@ -309,14 +309,14 @@ func TestIndex_CreateInputDefinition(t *testing.T) {
|
|||
// Create Input Definition.
|
||||
frames := internal.Frame{Name: "f", Meta: &internal.FrameMeta{RowLabel: "row"}}
|
||||
action := internal.InputDefinitionAction{Frame: "f", ValueDestination: "mapping", ValueMap: map[string]uint64{"Green": 1}}
|
||||
fields := internal.InputDefinitionField{Name: "id", PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&action}}
|
||||
fields := internal.InputDefinitionField{PrimaryKey: true, InputDefinitionActions: []*internal.InputDefinitionAction{&action}}
|
||||
def := internal.InputDefinition{Name: "test", Frames: []*internal.Frame{&frames}, Fields: []*internal.InputDefinitionField{&fields}}
|
||||
inputDef, err := index.CreateInputDefinition(&def)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if inputDef.Frames()[0].Name != frames.Name {
|
||||
t.Fatalf("unexpected input definition frames %v", inputDef.Frames())
|
||||
} else if inputDef.Fields()[0].Name != fields.Name {
|
||||
} else if inputDef.Fields()[0].Name != pilosa.DefaultColumnLabel {
|
||||
t.Fatalf("unexpected input definition actions %v", inputDef.Fields())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
|
|||
inputFrame := InputFrame{
|
||||
Name: fr.Name,
|
||||
Options: FrameOptions{
|
||||
RowLabel: frameMeta.RowLabel,
|
||||
// Deprecating row labels per #810. So, setting the default row label here.
|
||||
RowLabel: DefaultRowLabel,
|
||||
InverseEnabled: frameMeta.InverseEnabled,
|
||||
CacheSize: frameMeta.CacheSize,
|
||||
CacheType: frameMeta.CacheType,
|
||||
|
|
@ -100,8 +101,11 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
|
|||
i.frames = append(i.frames, inputFrame)
|
||||
}
|
||||
|
||||
primaryKeyGiven := false
|
||||
|
||||
for _, field := range pb.Fields {
|
||||
var actions []Action
|
||||
fieldName := field.Name
|
||||
for _, action := range field.InputDefinitionActions {
|
||||
actions = append(actions, Action{
|
||||
Frame: action.Frame,
|
||||
|
|
@ -111,14 +115,27 @@ func (i *InputDefinition) LoadDefinition(pb *internal.InputDefinition) error {
|
|||
})
|
||||
}
|
||||
|
||||
if field.PrimaryKey {
|
||||
// Deprecating column labels per #810.
|
||||
// So, setting the default column label here.
|
||||
fieldName = DefaultColumnLabel
|
||||
primaryKeyGiven = true
|
||||
}
|
||||
|
||||
inputField := InputDefinitionField{
|
||||
Name: field.Name,
|
||||
Name: fieldName,
|
||||
PrimaryKey: field.PrimaryKey,
|
||||
Actions: actions,
|
||||
}
|
||||
i.fields = append(i.fields, inputField)
|
||||
}
|
||||
|
||||
if len(pb.Fields) > 0 && !primaryKeyGiven {
|
||||
// primary field is required if there are other fields.
|
||||
// add it if it doesn't exist.
|
||||
i.fields = append(i.fields, InputDefDefaultPrimaryKeyField())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +282,7 @@ type InputDefinitionInfo struct {
|
|||
}
|
||||
|
||||
// Validate the InputDefinitionInfo data.
|
||||
func (i *InputDefinitionInfo) Validate(columnLabel string) error {
|
||||
func (i *InputDefinitionInfo) Validate() error {
|
||||
numPrimaryKey := 0
|
||||
accountRowID := make(map[string]uint64)
|
||||
|
||||
|
|
@ -298,9 +315,6 @@ func (i *InputDefinitionInfo) Validate(columnLabel string) error {
|
|||
}
|
||||
if field.PrimaryKey {
|
||||
numPrimaryKey++
|
||||
if field.Name != columnLabel {
|
||||
return ErrInputDefinitionColumnLabel
|
||||
}
|
||||
} else if len(field.Actions) == 0 {
|
||||
return ErrInputDefinitionActionRequired
|
||||
}
|
||||
|
|
@ -321,7 +335,16 @@ func (i *InputDefinitionInfo) Encode() *internal.InputDefinition {
|
|||
for _, f := range i.Frames {
|
||||
def.Frames = append(def.Frames, f.Encode())
|
||||
}
|
||||
primaryKeyGiven := false
|
||||
for _, f := range i.Fields {
|
||||
if f.PrimaryKey {
|
||||
f.Name = DefaultColumnLabel
|
||||
primaryKeyGiven = true
|
||||
}
|
||||
def.Fields = append(def.Fields, f.Encode())
|
||||
}
|
||||
if len(i.Fields) > 0 && !primaryKeyGiven {
|
||||
f := InputDefDefaultPrimaryKeyField()
|
||||
def.Fields = append(def.Fields, f.Encode())
|
||||
}
|
||||
return &def
|
||||
|
|
@ -379,3 +402,11 @@ func HandleAction(a Action, value interface{}, colID uint64, timestamp int64) (*
|
|||
}
|
||||
return &bit, err
|
||||
}
|
||||
|
||||
func InputDefDefaultPrimaryKeyField() InputDefinitionField {
|
||||
return InputDefinitionField{
|
||||
Name: DefaultColumnLabel,
|
||||
PrimaryKey: true,
|
||||
Actions: []Action{},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,10 +63,6 @@ func TestInputDefinition_Encoding(t *testing.T) {
|
|||
}
|
||||
}],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"primaryKey": true
|
||||
},
|
||||
{
|
||||
"name": "cabType",
|
||||
"actions": [
|
||||
|
|
@ -96,9 +92,9 @@ func TestInputDefinition_Encoding(t *testing.T) {
|
|||
t.Fatalf("unexpected frame meta data: %v", internalDef)
|
||||
} else if len(internalDef.Fields) != 2 {
|
||||
t.Fatalf("unexpected number of Fields: %d", len(internalDef.Fields))
|
||||
} else if len(internalDef.Fields[1].InputDefinitionActions) != 1 {
|
||||
} else if len(internalDef.Fields[0].InputDefinitionActions) != 1 {
|
||||
t.Fatalf("unexpected number of Actions: %v", internalDef.Fields[1].InputDefinitionActions)
|
||||
} else if internalDef.Fields[1].InputDefinitionActions[0].ValueDestination != "mapping" {
|
||||
} else if internalDef.Fields[0].InputDefinitionActions[0].ValueDestination != "mapping" {
|
||||
t.Fatalf("unexpected ValueDestination: %v", internalDef.Fields[1].InputDefinitionActions[0])
|
||||
}
|
||||
}
|
||||
|
|
@ -110,14 +106,14 @@ func TestActionValidation(t *testing.T) {
|
|||
action := pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, ValueMap: map[string]uint64{"Green": 1}}
|
||||
field := pilosa.InputDefinitionField{Name: "id", PrimaryKey: false, Actions: []pilosa.Action{action}}
|
||||
info := pilosa.InputDefinitionInfo{Fields: []pilosa.InputDefinitionField{field}}
|
||||
err := info.Validate("id")
|
||||
err := info.Validate()
|
||||
if err != pilosa.ErrInputDefinitionAttrsRequired {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionAttrsRequired, err)
|
||||
}
|
||||
|
||||
frame := pilosa.InputFrame{Name: "f", Options: pilosa.FrameOptions{RowLabel: "row"}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if !strings.Contains(err.Error(), "rowID required for single-row-boolean") {
|
||||
t.Fatalf("Expected rowID required for single-row-boolean error, actual error: %s", err)
|
||||
}
|
||||
|
|
@ -126,7 +122,7 @@ func TestActionValidation(t *testing.T) {
|
|||
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if err != pilosa.ErrName {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrName, err)
|
||||
}
|
||||
|
|
@ -135,23 +131,15 @@ func TestActionValidation(t *testing.T) {
|
|||
action = pilosa.Action{ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if err != pilosa.ErrFrameRequired {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrFrameRequired, err)
|
||||
}
|
||||
|
||||
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("test")
|
||||
if err != pilosa.ErrInputDefinitionColumnLabel {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionColumnLabel, err)
|
||||
}
|
||||
|
||||
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
|
||||
field = pilosa.InputDefinitionField{Name: "x", PrimaryKey: false, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if err != pilosa.ErrInputDefinitionHasPrimaryKey {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionHasPrimaryKey, err)
|
||||
}
|
||||
|
|
@ -159,7 +147,7 @@ func TestActionValidation(t *testing.T) {
|
|||
action = pilosa.Action{Frame: "f", ValueDestination: "value-to-ROW", ValueMap: map[string]uint64{"Green": 1}}
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if !strings.Contains(err.Error(), "invalid ValueDestination") {
|
||||
t.Fatalf("Expected invalid ValueDestination error, actual error: %s", err)
|
||||
}
|
||||
|
|
@ -167,7 +155,7 @@ func TestActionValidation(t *testing.T) {
|
|||
action = pilosa.Action{Frame: "f", ValueDestination: pilosa.InputMapping, RowID: &rowID}
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if err != pilosa.ErrInputDefinitionValueMap {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionValueMap, err)
|
||||
}
|
||||
|
|
@ -177,15 +165,15 @@ func TestActionValidation(t *testing.T) {
|
|||
action1 := pilosa.Action{Frame: "f", ValueDestination: pilosa.InputSingleRowBool, RowID: &rowID}
|
||||
field1 := pilosa.InputDefinitionField{Name: "id", PrimaryKey: true, Actions: []pilosa.Action{action1}}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field, field1}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if !strings.Contains(err.Error(), "duplicate rowID with other field") {
|
||||
t.Fatalf("Expected duplicate rowID with other field error, actual error: %s", err)
|
||||
}
|
||||
|
||||
field = pilosa.InputDefinitionField{Name: "id", PrimaryKey: true}
|
||||
field = pilosa.InputDefinitionField{PrimaryKey: true}
|
||||
field1 = pilosa.InputDefinitionField{Name: "test", PrimaryKey: false}
|
||||
info = pilosa.InputDefinitionInfo{Frames: []pilosa.InputFrame{frame}, Fields: []pilosa.InputDefinitionField{field, field1}}
|
||||
err = info.Validate("id")
|
||||
err = info.Validate()
|
||||
if err != pilosa.ErrInputDefinitionActionRequired {
|
||||
t.Fatalf("Expect error: %s, actual err: %s", pilosa.ErrInputDefinitionActionRequired, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue