Skip to content

Commit

Permalink
Support scan Nebula subgraph results (vertex and edge) and slice of s…
Browse files Browse the repository at this point in the history
…truct ptr (#346)

* Support scan Nebula vertex and edge

* fix: adjust the slice length

debug

* support struct ptr in the slice

* Add tests

* Add more props

* Add supports for slice of struct ptr

* check the empty and null value

* no use SetZero

---------

Co-authored-by: Anqi <[email protected]>
  • Loading branch information
haoxins and Nicole00 authored Jan 17, 2025
1 parent e6ae114 commit 9bb4c00
Show file tree
Hide file tree
Showing 2 changed files with 422 additions and 26 deletions.
174 changes: 148 additions & 26 deletions result_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,19 @@ func (res ResultSet) Scan(v interface{}) error {
}

// Scan scans the rows into the given value.
func (res ResultSet) scanRow(row *nebula.Row, colNames []string, t reflect.Type) (reflect.Value, error) {
func (res ResultSet) scanRow(row *nebula.Row, colNames []string, rowType reflect.Type) (reflect.Value, error) {
rowVals := row.GetValues()

val := reflect.New(t).Elem()
var result reflect.Value
if rowType.Kind() == reflect.Ptr {
result = reflect.New(rowType.Elem())
} else {
result = reflect.New(rowType).Elem()
}
structVal := reflect.Indirect(result)

for fIdx := 0; fIdx < t.NumField(); fIdx++ {
f := t.Field(fIdx)
for fIdx := 0; fIdx < structVal.Type().NumField(); fIdx++ {
f := structVal.Type().Field(fIdx)
tag := f.Tag.Get("nebula")

if tag == "" {
Expand All @@ -356,31 +362,147 @@ func (res ResultSet) scanRow(row *nebula.Row, colNames []string, t reflect.Type)

rowVal := rowVals[cIdx]

switch f.Type.Kind() {
case reflect.Bool:
val.Field(fIdx).SetBool(rowVal.GetBVal())
case reflect.Int:
val.Field(fIdx).SetInt(rowVal.GetIVal())
case reflect.Int8:
val.Field(fIdx).SetInt(rowVal.GetIVal())
case reflect.Int16:
val.Field(fIdx).SetInt(rowVal.GetIVal())
case reflect.Int32:
val.Field(fIdx).SetInt(rowVal.GetIVal())
case reflect.Int64:
val.Field(fIdx).SetInt(rowVal.GetIVal())
case reflect.Float32:
val.Field(fIdx).SetFloat(rowVal.GetFVal())
case reflect.Float64:
val.Field(fIdx).SetFloat(rowVal.GetFVal())
case reflect.String:
val.Field(fIdx).SetString(string(rowVal.GetSVal()))
default:
return val, errors.New("scan: not support type")
if f.Type.Kind() == reflect.Slice {
list := rowVal.GetLVal()
err := scanListCol(list.Values, structVal.Field(fIdx), f.Type)
if err != nil {
return result, err
}
} else {
err := scanPrimitiveCol(rowVal, structVal.Field(fIdx), f.Type.Kind())
if err != nil {
return result, err
}
}
}

return result, nil
}

func scanListCol(vals []*nebula.Value, listVal reflect.Value, sliceType reflect.Type) error {
switch sliceType.Elem().Kind() {
case reflect.Struct:
var listCol = reflect.MakeSlice(sliceType, 0, len(vals))
for _, val := range vals {
ele := reflect.New(sliceType.Elem()).Elem()
err := scanStructField(val, ele, sliceType.Elem())
if err != nil {
return err
}
listCol = reflect.Append(listCol, ele)
}
listVal.Set(listCol)
case reflect.Ptr:
var listCol = reflect.MakeSlice(sliceType, 0, len(vals))
for _, val := range vals {
ele := reflect.New(sliceType.Elem().Elem())
err := scanStructField(val, reflect.Indirect(ele), sliceType.Elem().Elem())
if err != nil {
return err
}
listCol = reflect.Append(listCol, ele)
}
listVal.Set(listCol)
default:
return errors.New("scan: not support list type")
}

return val, nil
return nil
}

func scanStructField(val *nebula.Value, eleVal reflect.Value, eleType reflect.Type) error {
vertex := val.GetVVal()
if vertex != nil {
tags := vertex.GetTags()
vid := vertex.GetVid()

if len(tags) != 0 {
tag := tags[0]

props := tag.GetProps()
props["_vid"] = vid
tagName := tag.GetName()
props["_tag_name"] = &nebula.Value{SVal: tagName}

err := scanValFromProps(props, eleVal, eleType)
if err != nil {
return err
}
return nil
}
// no tags, continue
}

edge := val.GetEVal()
if edge != nil {
props := edge.GetProps()

src := edge.GetSrc()
dst := edge.GetDst()
name := edge.GetName()
props["_src"] = src
props["_dst"] = dst
props["_name"] = &nebula.Value{SVal: name}

err := scanValFromProps(props, eleVal, eleType)
if err != nil {
return err
}
return nil
}

return nil
}

func scanValFromProps(props map[string]*nebula.Value, val reflect.Value, tpe reflect.Type) error {
for fIdx := 0; fIdx < tpe.NumField(); fIdx++ {
f := tpe.Field(fIdx)
n := f.Tag.Get("nebula")
v, ok := props[n]
if !ok {
continue
}
err := scanPrimitiveCol(v, val.Field(fIdx), f.Type.Kind())
if err != nil {
return err
}
}

return nil
}

func scanPrimitiveCol(rowVal *nebula.Value, val reflect.Value, kind reflect.Kind) error {
w := ValueWrapper{value: rowVal}
if w.IsNull() || w.IsEmpty() {
// SetZero is introduced in go 1.20
// val.SetZero()
return nil
}

switch kind {
case reflect.Bool:
val.SetBool(rowVal.GetBVal())
case reflect.Int:
val.SetInt(rowVal.GetIVal())
case reflect.Int8:
val.SetInt(rowVal.GetIVal())
case reflect.Int16:
val.SetInt(rowVal.GetIVal())
case reflect.Int32:
val.SetInt(rowVal.GetIVal())
case reflect.Int64:
val.SetInt(rowVal.GetIVal())
case reflect.Float32:
val.SetFloat(rowVal.GetFVal())
case reflect.Float64:
val.SetFloat(rowVal.GetFVal())
case reflect.String:
val.SetString(string(rowVal.GetSVal()))
default:
return errors.New("scan: not support primitive type")
}

return nil
}

// Returns the number of total rows
Expand Down
Loading

0 comments on commit 9bb4c00

Please sign in to comment.