Repository: genjidb/genji (go)

Issue:
MapScan doesn't decode nested documents
[MapScan](https://sourcegraph.com/github.com/genjidb/genji@master/-/blob/document/scan.go#L180:9) should decode the entire document into a `map[string]interface{}`, including nested documents and arrays.

- documents should be decoded into `map[string]interface{}`
- arrays should be decoded into `[]interface{}`

Currently, nested documents are returned as byte slices.


Pull request:
Fix scan of encoded documents and arrays
Fixes #191 

Patch:
diff --git a/document/scan.go b/document/scan.go
index 9d080123..bdfc9cd8 100644
--- a/document/scan.go
+++ b/document/scan.go
@@ -285,6 +285,23 @@ func scanValue(v Value, ref reflect.Value) error {
 		ref.SetFloat(v.V.(float64))
 		return nil
 	case reflect.Interface:
+		switch v.Type {
+		case DocumentValue:
+			m := make(map[string]interface{})
+			vm := reflect.ValueOf(m)
+			ref.Set(vm)
+			return mapScan(v.V.(Document), vm)
+		case ArrayValue:
+			var s []interface{}
+			vs := reflect.ValueOf(&s)
+			err := sliceScan(v.V.(Array), vs)
+			if err != nil {
+				return err
+			}
+			ref.Set(vs.Elem())
+			return nil
+		}
+
 		ref.Set(reflect.ValueOf(v.V))
 		return nil
 	}


Test patch:
diff --git a/document/scan_test.go b/document/scan_test.go
index 0cbacd3c..4cc01f48 100644
--- a/document/scan_test.go
+++ b/document/scan_test.go
@@ -1,16 +1,34 @@
 package document_test
 
 import (
+	"bytes"
 	"testing"
 	"time"
 
 	"github.com/genjidb/genji/document"
+	"github.com/genjidb/genji/document/encoding/msgpack"
 	"github.com/stretchr/testify/require"
 )
 
 func TestScan(t *testing.T) {
 	now := time.Now()
 
+	simpleDoc := document.NewFieldBuffer().
+		Add("foo", document.NewTextValue("foo")).
+		Add("bar", document.NewTextValue("bar")).
+		Add("baz", document.NewArrayValue(document.NewValueBuffer(
+			document.NewIntegerValue(10),
+			document.NewDoubleValue(20.5),
+		)))
+
+	nestedDoc := document.NewFieldBuffer().
+		Add("foo", document.NewDocumentValue(simpleDoc))
+
+	var buf bytes.Buffer
+	codec := msgpack.NewCodec()
+	err := codec.NewEncoder(&buf).EncodeDocument(nestedDoc)
+	require.NoError(t, err)
+
 	doc := document.NewFieldBuffer().
 		Add("a", document.NewBlobValue([]byte("foo"))).
 		Add("b", document.NewTextValue("bar")).
@@ -48,7 +66,8 @@ func TestScan(t *testing.T) {
 				Add("bar", document.NewTextValue("bar")),
 		)).
 		Add("o", document.NewDurationValue(10*time.Nanosecond)).
-		Add("p", document.NewTextValue(now.Format(time.RFC3339Nano)))
+		Add("p", document.NewTextValue(now.Format(time.RFC3339Nano))).
+		Add("r", document.NewDocumentValue(codec.NewDocument(buf.Bytes())))
 
 	type foo struct {
 		Foo string
@@ -72,8 +91,9 @@ func TestScan(t *testing.T) {
 	var n map[string]string
 	var o time.Duration
 	var p time.Time
+	var r map[string]interface{}
 
-	err := document.Scan(doc, &a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p)
+	err = document.Scan(doc, &a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &r)
 	require.NoError(t, err)
 	require.Equal(t, a, []byte("foo"))
 	require.Equal(t, b, "bar")
@@ -92,6 +112,15 @@ func TestScan(t *testing.T) {
 	require.Equal(t, map[string]string{"foo": "foo", "bar": "bar"}, n)
 	require.Equal(t, 10*time.Nanosecond, o)
 	require.Equal(t, now.Format(time.RFC3339Nano), p.Format(time.RFC3339Nano))
+	require.Equal(t, map[string]interface{}{
+		"foo": map[string]interface{}{
+			"foo": "foo",
+			"bar": "bar",
+			"baz": []interface{}{
+				int64(10), float64(20.5),
+			},
+		},
+	}, r)
 
 	t.Run("DocumentScanner", func(t *testing.T) {
 		var ds documentScanner
@@ -107,14 +136,14 @@ func TestScan(t *testing.T) {
 		m := make(map[string]interface{})
 		err := document.MapScan(doc, m)
 		require.NoError(t, err)
-		require.Len(t, m, 16)
+		require.Len(t, m, 17)
 	})
 
 	t.Run("MapPtr", func(t *testing.T) {
 		var m map[string]interface{}
 		err := document.MapScan(doc, &m)
 		require.NoError(t, err)
-		require.Len(t, m, 16)
+		require.Len(t, m, 17)
 	})
 
 	t.Run("Small Slice", func(t *testing.T) {