mgo map 处理源码阅读

mgo 存储的时候是 用json.marshal(in interface{})的方法,

内部调用了

func (e *encoder) addDoc(v reflect.Value) {
    for {
        if vi, ok := v.Interface().(Getter); ok {
            getv, err := vi.GetBSON()
            if err != nil {
                panic(err)
            }
            v = reflect.ValueOf(getv)
            continue
        }
        if v.Kind() == reflect.Ptr {
            v = v.Elem()
            continue
        }
        break
    }

    if v.Type() == typeRaw {
        raw := v.Interface().(Raw)
        if raw.Kind != 0x03 && raw.Kind != 0x00 {
            panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document")
        }
        e.addBytes(raw.Data...)
        return
    }

    start := e.reserveInt32()

    switch v.Kind() {
    case reflect.Map:
        e.addMap(v)
    case reflect.Struct:
        e.addStruct(v)
    case reflect.Array, reflect.Slice:
        e.addSlice(v)
    default:
        panic("Can't marshal " + v.Type().String() + " as a BSON document")
    }

    e.addBytes(0)
    e.setInt32(start, int32(len(e.out)-start))
}

如果是Map的话就调用addMap方法。

func (e *encoder) addMap(v reflect.Value) {
    for _, k := range v.MapKeys() {
        e.addElem(k.String(), v.MapIndex(k), false)
    }
}
func (e *encoder) addElemName(kind byte, name string) {
    e.addBytes(kind)
    e.addBytes([]byte(name)...)
    e.addBytes(0)
}

func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {

    if !v.IsValid() {
        e.addElemName('\x0A', name)
        return
    }

    if getter, ok := v.Interface().(Getter); ok {
        getv, err := getter.GetBSON()
        if err != nil {
            panic(err)
        }
        e.addElem(name, reflect.ValueOf(getv), minSize)
        return
    }

    switch v.Kind() {

    case reflect.Interface:
        e.addElem(name, v.Elem(), minSize)

    case reflect.Ptr:
        e.addElem(name, v.Elem(), minSize)

    case reflect.String:
        s := v.String()
        switch v.Type() {
        case typeObjectId:
            if len(s) != 12 {
                panic("ObjectIDs must be exactly 12 bytes long (got " +
                    strconv.Itoa(len(s)) + ")")
            }
            e.addElemName('\x07', name)
            e.addBytes([]byte(s)...)
        case typeSymbol:
            e.addElemName('\x0E', name)
            e.addStr(s)
        default:
            e.addElemName('\x02', name)
            e.addStr(s)
        }

    case reflect.Float32, reflect.Float64:
        e.addElemName('\x01', name)
        e.addInt64(int64(math.Float64bits(v.Float())))

    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        u := v.Uint()
        if int64(u) < 0 {
            panic("BSON has no uint64 type, and value is too large to fit correctly in an int64")
        } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) {
            e.addElemName('\x10', name)
            e.addInt32(int32(u))
        } else {
            e.addElemName('\x12', name)
            e.addInt64(int64(u))
        }

    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        switch v.Type() {
        case typeMongoTimestamp:
            e.addElemName('\x11', name)
            e.addInt64(v.Int())

        case typeOrderKey:
            if v.Int() == int64(MaxKey) {
                e.addElemName('\x7F', name)
            } else {
                e.addElemName('\xFF', name)
            }

        default:
            i := v.Int()
            if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 {
                // It fits into an int32, encode as such.
                e.addElemName('\x10', name)
                e.addInt32(int32(i))
            } else {
                e.addElemName('\x12', name)
                e.addInt64(i)
            }
        }

    case reflect.Bool:
        e.addElemName('\x08', name)
        if v.Bool() {
            e.addBytes(1)
        } else {
            e.addBytes(0)
        }

    case reflect.Map:
        e.addElemName('\x03', name)
        e.addDoc(v)

    case reflect.Slice:
        vt := v.Type()
        et := vt.Elem()
        if et.Kind() == reflect.Uint8 {
            e.addElemName('\x05', name)
            e.addBinary('\x00', v.Bytes())
        } else if et == typeDocElem || et == typeRawDocElem {
            e.addElemName('\x03', name)
            e.addDoc(v)
        } else {
            e.addElemName('\x04', name)
            e.addDoc(v)
        }

    case reflect.Array:
        et := v.Type().Elem()
        if et.Kind() == reflect.Uint8 {
            e.addElemName('\x05', name)
            e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte))
        } else {
            e.addElemName('\x04', name)
            e.addDoc(v)
        }

    case reflect.Struct:
        switch s := v.Interface().(type) {

        case Raw:
            kind := s.Kind
            if kind == 0x00 {
                kind = 0x03
            }
            e.addElemName(kind, name)
            e.addBytes(s.Data...)

        case Binary:
            e.addElemName('\x05', name)
            e.addBinary(s.Kind, s.Data)

        case RegEx:
            e.addElemName('\x0B', name)
            e.addCStr(s.Pattern)
            e.addCStr(s.Options)

        case JavaScript:
            if s.Scope == nil {
                e.addElemName('\x0D', name)
                e.addStr(s.Code)
            } else {
                e.addElemName('\x0F', name)
                start := e.reserveInt32()
                e.addStr(s.Code)
                e.addDoc(reflect.ValueOf(s.Scope))
                e.setInt32(start, int32(len(e.out)-start))
            }

        case time.Time:
            // MongoDB handles timestamps as milliseconds.
            e.addElemName('\x09', name)
            e.addInt64(s.Unix() * 1000 + int64(s.Nanosecond() / 1e6))

        case url.URL:
            e.addElemName('\x02', name)
            e.addStr(s.String())

        case undefined:
            e.addElemName('\x06', name)

        default:
            e.addElemName('\x03', name)
            e.addDoc(v)
        }

    default:
        panic("Can't marshal " + v.Type().String() + " in a BSON document")
    }
}

再根据反射的类型存值。

对于 map[int]string 

map插入以后变成了 <int value> "asdas",<int value> "adasd"

处理方法是

M is a convenient alias for a map[string]interface{} map, useful for dealing with BSON in a native way. 

只能直接用bson.M来存了。

posted @ 2014-02-25 15:33  ggaaooppeenngg  阅读(1458)  评论(0编辑  收藏  举报