Skip to content

Commit

Permalink
feat: Allow setting null values on doc fields (#2273)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves #2272

## Description

Allows setting of null values for nillable fields.
  • Loading branch information
islamaliev authored Jan 30, 2024
1 parent 5834f81 commit 8a66721
Show file tree
Hide file tree
Showing 26 changed files with 529 additions and 135 deletions.
2 changes: 1 addition & 1 deletion client/ctype.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (t CType) IsSupportedFieldCType() bool {
func (t CType) IsCompatibleWith(kind FieldKind) bool {
switch t {
case PN_COUNTER:
if kind == FieldKind_INT || kind == FieldKind_FLOAT {
if kind == FieldKind_NILLABLE_INT || kind == FieldKind_NILLABLE_FLOAT {
return true
}
return false
Expand Down
60 changes: 30 additions & 30 deletions client/descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,35 +155,35 @@ func (f FieldKind) String() string {
switch f {
case FieldKind_DocID:
return "ID"
case FieldKind_BOOL:
case FieldKind_NILLABLE_BOOL:
return "Boolean"
case FieldKind_NILLABLE_BOOL_ARRAY:
return "[Boolean]"
case FieldKind_BOOL_ARRAY:
return "[Boolean!]"
case FieldKind_INT:
case FieldKind_NILLABLE_INT:
return "Int"
case FieldKind_NILLABLE_INT_ARRAY:
return "[Int]"
case FieldKind_INT_ARRAY:
return "[Int!]"
case FieldKind_DATETIME:
case FieldKind_NILLABLE_DATETIME:
return "DateTime"
case FieldKind_FLOAT:
case FieldKind_NILLABLE_FLOAT:
return "Float"
case FieldKind_NILLABLE_FLOAT_ARRAY:
return "[Float]"
case FieldKind_FLOAT_ARRAY:
return "[Float!]"
case FieldKind_STRING:
case FieldKind_NILLABLE_STRING:
return "String"
case FieldKind_NILLABLE_STRING_ARRAY:
return "[String]"
case FieldKind_STRING_ARRAY:
return "[String!]"
case FieldKind_BLOB:
case FieldKind_NILLABLE_BLOB:
return "Blob"
case FieldKind_JSON:
case FieldKind_NILLABLE_JSON:
return "JSON"
default:
return fmt.Sprint(uint8(f))
Expand All @@ -192,22 +192,22 @@ func (f FieldKind) String() string {

// Note: These values are serialized and persisted in the database, avoid modifying existing values.
const (
FieldKind_None FieldKind = 0
FieldKind_DocID FieldKind = 1
FieldKind_BOOL FieldKind = 2
FieldKind_BOOL_ARRAY FieldKind = 3
FieldKind_INT FieldKind = 4
FieldKind_INT_ARRAY FieldKind = 5
FieldKind_FLOAT FieldKind = 6
FieldKind_FLOAT_ARRAY FieldKind = 7
_ FieldKind = 8 // safe to repurpose (was never used)
_ FieldKind = 9 // safe to repurpose (previously old field)
FieldKind_DATETIME FieldKind = 10
FieldKind_STRING FieldKind = 11
FieldKind_STRING_ARRAY FieldKind = 12
FieldKind_BLOB FieldKind = 13
FieldKind_JSON FieldKind = 14
_ FieldKind = 15 // safe to repurpose (was never used)
FieldKind_None FieldKind = 0
FieldKind_DocID FieldKind = 1
FieldKind_NILLABLE_BOOL FieldKind = 2
FieldKind_BOOL_ARRAY FieldKind = 3
FieldKind_NILLABLE_INT FieldKind = 4
FieldKind_INT_ARRAY FieldKind = 5
FieldKind_NILLABLE_FLOAT FieldKind = 6
FieldKind_FLOAT_ARRAY FieldKind = 7
_ FieldKind = 8 // safe to repurpose (was never used)
_ FieldKind = 9 // safe to repurpose (previously old field)
FieldKind_NILLABLE_DATETIME FieldKind = 10
FieldKind_NILLABLE_STRING FieldKind = 11
FieldKind_STRING_ARRAY FieldKind = 12
FieldKind_NILLABLE_BLOB FieldKind = 13
FieldKind_NILLABLE_JSON FieldKind = 14
_ FieldKind = 15 // safe to repurpose (was never used)

// Embedded object, but accessed via foreign keys
FieldKind_FOREIGN_OBJECT FieldKind = 16
Expand All @@ -230,21 +230,21 @@ const (
// equality is not guaranteed.
var FieldKindStringToEnumMapping = map[string]FieldKind{
"ID": FieldKind_DocID,
"Boolean": FieldKind_BOOL,
"Boolean": FieldKind_NILLABLE_BOOL,
"[Boolean]": FieldKind_NILLABLE_BOOL_ARRAY,
"[Boolean!]": FieldKind_BOOL_ARRAY,
"Int": FieldKind_INT,
"Int": FieldKind_NILLABLE_INT,
"[Int]": FieldKind_NILLABLE_INT_ARRAY,
"[Int!]": FieldKind_INT_ARRAY,
"DateTime": FieldKind_DATETIME,
"Float": FieldKind_FLOAT,
"DateTime": FieldKind_NILLABLE_DATETIME,
"Float": FieldKind_NILLABLE_FLOAT,
"[Float]": FieldKind_NILLABLE_FLOAT_ARRAY,
"[Float!]": FieldKind_FLOAT_ARRAY,
"String": FieldKind_STRING,
"String": FieldKind_NILLABLE_STRING,
"[String]": FieldKind_NILLABLE_STRING_ARRAY,
"[String!]": FieldKind_STRING_ARRAY,
"Blob": FieldKind_BLOB,
"JSON": FieldKind_JSON,
"Blob": FieldKind_NILLABLE_BLOB,
"JSON": FieldKind_NILLABLE_JSON,
}

// RelationType describes the type of relation between two types.
Expand Down
30 changes: 25 additions & 5 deletions client/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,33 @@ func NewDocsFromJSON(obj []byte, sd SchemaDescription) ([]*Document, error) {
return docs, nil
}

func isNillableKind(kind FieldKind) bool {
switch kind {
case FieldKind_NILLABLE_STRING, FieldKind_NILLABLE_BLOB, FieldKind_NILLABLE_JSON,
FieldKind_NILLABLE_BOOL, FieldKind_NILLABLE_FLOAT, FieldKind_NILLABLE_DATETIME,
FieldKind_NILLABLE_INT:
return true
default:
return false
}
}

// validateFieldSchema takes a given value as an interface,
// and ensures it matches the supplied field description.
// It will do any minor parsing, like dates, and return
// the typed value again as an interface.
func validateFieldSchema(val any, field FieldDescription) (any, error) {
if isNillableKind(field.Kind) {
if val == nil {
return nil, nil
}
if v, ok := val.(*fastjson.Value); ok && v.Type() == fastjson.TypeNull {
return nil, nil
}
}

switch field.Kind {
case FieldKind_DocID, FieldKind_STRING, FieldKind_BLOB, FieldKind_JSON:
case FieldKind_DocID, FieldKind_NILLABLE_STRING, FieldKind_NILLABLE_BLOB, FieldKind_NILLABLE_JSON:
return getString(val)

case FieldKind_STRING_ARRAY:
Expand All @@ -186,7 +206,7 @@ func validateFieldSchema(val any, field FieldDescription) (any, error) {
case FieldKind_NILLABLE_STRING_ARRAY:
return getNillableArray(val, getString)

case FieldKind_BOOL:
case FieldKind_NILLABLE_BOOL:
return getBool(val)

case FieldKind_BOOL_ARRAY:
Expand All @@ -195,7 +215,7 @@ func validateFieldSchema(val any, field FieldDescription) (any, error) {
case FieldKind_NILLABLE_BOOL_ARRAY:
return getNillableArray(val, getBool)

case FieldKind_FLOAT:
case FieldKind_NILLABLE_FLOAT:
return getFloat64(val)

case FieldKind_FLOAT_ARRAY:
Expand All @@ -204,10 +224,10 @@ func validateFieldSchema(val any, field FieldDescription) (any, error) {
case FieldKind_NILLABLE_FLOAT_ARRAY:
return getNillableArray(val, getFloat64)

case FieldKind_DATETIME:
case FieldKind_NILLABLE_DATETIME:
return getDateTime(val)

case FieldKind_INT:
case FieldKind_NILLABLE_INT:
return getInt64(val)

case FieldKind_INT_ARRAY:
Expand Down
4 changes: 2 additions & 2 deletions client/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ var (
{
Name: "Name",
Typ: LWW_REGISTER,
Kind: FieldKind_STRING,
Kind: FieldKind_NILLABLE_STRING,
},
{
Name: "Age",
Typ: LWW_REGISTER,
Kind: FieldKind_INT,
Kind: FieldKind_NILLABLE_INT,
},
},
},
Expand Down
6 changes: 3 additions & 3 deletions core/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func DecodeFieldValue(fieldDesc client.FieldDescription, val any) (any, error) {
}
} else { // CBOR often encodes values typed as floats as ints
switch fieldDesc.Kind {
case client.FieldKind_FLOAT:
case client.FieldKind_NILLABLE_FLOAT:
switch v := val.(type) {
case int64:
return float64(v), nil
Expand All @@ -107,7 +107,7 @@ func DecodeFieldValue(fieldDesc client.FieldDescription, val any) (any, error) {
case uint:
return float64(v), nil
}
case client.FieldKind_INT:
case client.FieldKind_NILLABLE_INT:
switch v := val.(type) {
case float64:
return int64(v), nil
Expand All @@ -120,7 +120,7 @@ func DecodeFieldValue(fieldDesc client.FieldDescription, val any) (any, error) {
case uint:
return int64(v), nil
}
case client.FieldKind_DATETIME:
case client.FieldKind_NILLABLE_DATETIME:
switch v := val.(type) {
case string:
return time.Parse(time.RFC3339, v)
Expand Down
12 changes: 6 additions & 6 deletions db/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,23 @@ func canConvertIndexFieldValue[T any](val any) bool {

func getValidateIndexFieldFunc(kind client.FieldKind) func(any) bool {
switch kind {
case client.FieldKind_STRING, client.FieldKind_FOREIGN_OBJECT:
case client.FieldKind_NILLABLE_STRING, client.FieldKind_FOREIGN_OBJECT:
return canConvertIndexFieldValue[string]
case client.FieldKind_INT:
case client.FieldKind_NILLABLE_INT:
return canConvertIndexFieldValue[int64]
case client.FieldKind_FLOAT:
case client.FieldKind_NILLABLE_FLOAT:
return canConvertIndexFieldValue[float64]
case client.FieldKind_BOOL:
case client.FieldKind_NILLABLE_BOOL:
return canConvertIndexFieldValue[bool]
case client.FieldKind_BLOB:
case client.FieldKind_NILLABLE_BLOB:
return func(val any) bool {
blobStrVal, ok := val.(string)
if !ok {
return false
}
return types.BlobPattern.MatchString(blobStrVal)
}
case client.FieldKind_DATETIME:
case client.FieldKind_NILLABLE_DATETIME:
return func(val any) bool {
timeStrVal, ok := val.(string)
if !ok {
Expand Down
4 changes: 2 additions & 2 deletions merkle/crdt/merklecrdt.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ func InstanceWithStore(
), nil
case client.PN_COUNTER:
switch kind {
case client.FieldKind_INT:
case client.FieldKind_NILLABLE_INT:
return NewMerklePNCounter[int64](
store,
schemaVersionKey,
key,
fieldName,
), nil
case client.FieldKind_FLOAT:
case client.FieldKind_NILLABLE_FLOAT:
return NewMerklePNCounter[float64](
store,
schemaVersionKey,
Expand Down
2 changes: 1 addition & 1 deletion net/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var sd = client.SchemaDescription{
Fields: []client.FieldDescription{
{
Name: "test",
Kind: client.FieldKind_STRING,
Kind: client.FieldKind_NILLABLE_STRING,
Typ: client.LWW_REGISTER,
},
},
Expand Down
4 changes: 2 additions & 2 deletions planner/sum.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (p *Planner) isValueFloat(
return false, client.NewErrFieldNotExist(source.Name)
}
return fieldDescription.Kind == client.FieldKind_FLOAT_ARRAY ||
fieldDescription.Kind == client.FieldKind_FLOAT ||
fieldDescription.Kind == client.FieldKind_NILLABLE_FLOAT ||
fieldDescription.Kind == client.FieldKind_NILLABLE_FLOAT_ARRAY, nil
}

Expand Down Expand Up @@ -136,7 +136,7 @@ func (p *Planner) isValueFloat(
}

return fieldDescription.Kind == client.FieldKind_FLOAT_ARRAY ||
fieldDescription.Kind == client.FieldKind_FLOAT ||
fieldDescription.Kind == client.FieldKind_NILLABLE_FLOAT ||
fieldDescription.Kind == client.FieldKind_NILLABLE_FLOAT_ARRAY, nil
}

Expand Down
14 changes: 7 additions & 7 deletions request/graphql/schema/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,19 +455,19 @@ func astTypeToKind(t ast.Type) (client.FieldKind, error) {
case typeID:
return client.FieldKind_DocID, nil
case typeBoolean:
return client.FieldKind_BOOL, nil
return client.FieldKind_NILLABLE_BOOL, nil
case typeInt:
return client.FieldKind_INT, nil
return client.FieldKind_NILLABLE_INT, nil
case typeFloat:
return client.FieldKind_FLOAT, nil
return client.FieldKind_NILLABLE_FLOAT, nil
case typeDateTime:
return client.FieldKind_DATETIME, nil
return client.FieldKind_NILLABLE_DATETIME, nil
case typeString:
return client.FieldKind_STRING, nil
return client.FieldKind_NILLABLE_STRING, nil
case typeBlob:
return client.FieldKind_BLOB, nil
return client.FieldKind_NILLABLE_BLOB, nil
case typeJSON:
return client.FieldKind_JSON, nil
return client.FieldKind_NILLABLE_JSON, nil
default:
return client.FieldKind_FOREIGN_OBJECT, nil
}
Expand Down
Loading

0 comments on commit 8a66721

Please sign in to comment.