Repository: genjidb/genji (go)

Issue:
Add support for embedded structs to document.NewFromStruct
```
type Embedded struct {
	Id string
}
type Example1 struct {
	*Embedded
}
type Example2 struct {
	Id string
}
```

`select * from examples where id = ?` will fail for the first struct but pass for the second struct - am i doing something wrong?

Pull request description:
NewFromStruct supports more types.
Fix #225 .

Use `FieldBuffer` instead of `StructDocument`.

Patch:
diff --git a/document/create.go b/document/create.go
index 07a3e1e5..d69c3972 100644
--- a/document/create.go
+++ b/document/create.go
@@ -102,90 +102,71 @@ func NewFromStruct(s interface{}) (Document, error) {
 		return nil, errors.New("expected struct or pointer to struct")
 	}
 
-	return structDocument{ref: ref}, nil
+	return newFromStruct(ref)
 }
 
-type structDocument struct {
-	ref reflect.Value
-}
-
-var _ Document = (*structDocument)(nil)
+func newFromStruct(ref reflect.Value) (Document, error) {
+	var fb FieldBuffer
+	l := ref.NumField()
+	tp := ref.Type()
 
-func (s structDocument) Iterate(fn func(field string, value Value) error) error {
-	l := s.ref.NumField()
+	for i := 0; i < l; i++ {
+		f := ref.Field(i)
+		if !f.IsValid() {
+			continue
+		}
 
-	tp := s.ref.Type()
+		if f.Kind() == reflect.Ptr && !f.IsNil() {
+			f = f.Elem()
+		}
 
-	for i := 0; i < l; i++ {
 		sf := tp.Field(i)
+
+		if f.Kind() == reflect.Struct {
+			d, err := newFromStruct(f)
+			if err != nil {
+				return nil, err
+			}
+
+			if !sf.Anonymous {
+				fb.Add(strings.ToLower(sf.Name), NewDocumentValue(d))
+				continue
+			}
+
+			// Embedded struct.
+			err = d.Iterate(func(field string, value Value) error {
+				fb.Add(field, value)
+				return nil
+			})
+			if err != nil {
+				return nil, err
+			}
+
+			continue
+		}
+
 		if sf.PkgPath != "" {
 			continue
 		}
 
-		var name string
+		field := strings.ToLower(sf.Name)
 		if gtag, ok := sf.Tag.Lookup("genji"); ok {
 			if gtag == "-" {
 				continue
 			}
 
-			name = gtag
-		} else {
-			name = strings.ToLower(sf.Name)
+			field = gtag
 		}
 
-		f := s.ref.Field(i)
-
 		v, err := NewValue(f.Interface())
 		if err != nil {
-			if err.(*ErrUnsupportedType) != nil {
-				continue
-			}
-			return err
+			return nil, err
 		}
 
-		err = fn(name, v)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func (s structDocument) GetByField(field string) (Value, error) {
-	tp := s.ref.Type()
-
-	var sf reflect.StructField
-	var ok bool
-
-	ln := tp.NumField()
-	for i := 0; i < ln; i++ {
-		sf = tp.Field(i)
-		if gtag, found := sf.Tag.Lookup("genji"); found && gtag == field {
-			ok = true
-			break
-		}
-		if strings.ToLower(sf.Name) == field {
-			ok = true
-			break
-		}
+		fb.Add(field, v)
 	}
 
-	if !ok || sf.PkgPath != "" {
-		return Value{}, ErrFieldNotFound
-	}
-
-	v := s.ref.FieldByName(sf.Name)
-	if !v.IsValid() {
-		return Value{}, ErrFieldNotFound
-	}
-
-	return NewValue(v.Interface())
-}
-
-// MarshalJSON implements the json.Marshaler interface.
-func (s structDocument) MarshalJSON() ([]byte, error) {
-	return jsonDocument{Document: s}.MarshalJSON()
+	return &fb, nil
 }
 
 // NewValue creates a value whose type is infered from x.


Test patch:
diff --git a/document/document_test.go b/document/document_test.go
index fd6b098f..ff624b0e 100644
--- a/document/document_test.go
+++ b/document/document_test.go
@@ -227,7 +227,7 @@ func TestFieldBuffer(t *testing.T) {
 
 func TestNewFromStruct(t *testing.T) {
 	type group struct {
-		A int
+		Ig int
 	}
 
 	type user struct {
@@ -269,7 +269,6 @@ func TestNewFromStruct(t *testing.T) {
 
 		AA int `genji:"-"` // ignored
 
-		// embedded fields are not supported currently, they should be ignored
 		*group
 
 		// unexported fields should be ignored
@@ -293,6 +292,9 @@ func TestNewFromStruct(t *testing.T) {
 		N:  11.12,
 		Z:  26,
 		AA: 27,
+		group: &group{
+			Ig: 100,
+		},
 	}
 
 	q := 5
@@ -366,6 +368,8 @@ func TestNewFromStruct(t *testing.T) {
 				require.EqualValues(t, u.Z, v.V.(int64))
 			case 26:
 				require.EqualValues(t, document.NullValue, v.Type)
+			case 27:
+				require.EqualValues(t, document.IntegerValue, v.Type)
 			default:
 				require.FailNowf(t, "", "unknown field %q", f)
 			}
@@ -375,7 +379,7 @@ func TestNewFromStruct(t *testing.T) {
 			return nil
 		})
 		require.NoError(t, err)
-		require.Equal(t, 27, counter)
+		require.Equal(t, 28, counter)
 	})
 
 	t.Run("GetByField", func(t *testing.T) {
@@ -429,10 +433,14 @@ func TestNewFromStruct(t *testing.T) {
 		require.NoError(t, err)
 		d, ok := v.V.(document.Document)
 		require.True(t, ok)
-		v, err = d.GetByField("a")
+		v, err = d.GetByField("ig")
 		require.NoError(t, err)
 		require.EqualValues(t, 0, v.V.(int64))
 
+		v, err = doc.GetByField("ig")
+		require.NoError(t, err)
+		require.EqualValues(t, 100, v.V.(int64))
+
 		v, err = doc.GetByField("t")
 		require.NoError(t, err)
 		a, ok := v.V.(document.Array)
@@ -597,12 +605,4 @@ func BenchmarkDocumentIterate(b *testing.B) {
 		}
 	})
 
-	b.Run("Reflection", func(b *testing.B) {
-		for i := 0; i < b.N; i++ {
-			refd, _ := document.NewFromStruct(&f)
-			refd.Iterate(func(string, document.Value) error {
-				return nil
-			})
-		}
-	})
 }