1// Copyright (C) MongoDB, Inc. 2014-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7// Package bsonutil provides utilities for processing BSON data. 8package bsonutil 9 10import ( 11 "encoding/base64" 12 "encoding/hex" 13 "errors" 14 "fmt" 15 "strconv" 16 "time" 17 18 "github.com/mongodb/mongo-tools-common/json" 19 "github.com/mongodb/mongo-tools-common/util" 20 "go.mongodb.org/mongo-driver/bson" 21 "go.mongodb.org/mongo-driver/bson/primitive" 22) 23 24var ErrNoSuchField = errors.New("no such field") 25 26// ConvertLegacyExtJSONDocumentToBSON iterates through the document map and converts JSON 27// values to their corresponding BSON values. It also replaces any extended JSON 28// type value (e.g. $date) with the corresponding BSON type 29func ConvertLegacyExtJSONDocumentToBSON(doc map[string]interface{}) error { 30 for key, jsonValue := range doc { 31 var bsonValue interface{} 32 var err error 33 34 switch v := jsonValue.(type) { 35 case map[string]interface{}, bson.D: // subdocument 36 bsonValue, err = ParseSpecialKeys(v) 37 default: 38 bsonValue, err = ConvertLegacyExtJSONValueToBSON(v) 39 } 40 if err != nil { 41 return err 42 } 43 44 doc[key] = bsonValue 45 } 46 return nil 47} 48 49// GetExtendedBsonD iterates through the document and returns a bson.D that adds type 50// information for each key in document. 51func GetExtendedBsonD(doc bson.D) (bson.D, error) { 52 var err error 53 var bsonDoc bson.D 54 for _, docElem := range doc { 55 var bsonValue interface{} 56 switch v := docElem.Value.(type) { 57 case map[string]interface{}, bson.D: // subdocument 58 bsonValue, err = ParseSpecialKeys(v) 59 default: 60 bsonValue, err = ConvertLegacyExtJSONValueToBSON(v) 61 } 62 if err != nil { 63 return nil, err 64 } 65 bsonDoc = append(bsonDoc, bson.E{ 66 Key: docElem.Key, 67 Value: bsonValue, 68 }) 69 } 70 return bsonDoc, nil 71} 72 73// FindValueByKey returns the value of keyName in document. If keyName is not found 74// in the top-level of the document, ErrNoSuchField is returned as the error. 75func FindValueByKey(keyName string, document *bson.D) (interface{}, error) { 76 for _, key := range *document { 77 if key.Key == keyName { 78 return key.Value, nil 79 } 80 } 81 return nil, ErrNoSuchField 82} 83 84// FindIntByKey returns the value of keyName in the document as an int for 85// either int32 or int64 underlying type. 86func FindIntByKey(keyName string, document *bson.D) (int, error) { 87 raw, err := FindValueByKey(keyName, document) 88 if err != nil { 89 return 0, err 90 } 91 switch x := raw.(type) { 92 case int32: 93 return int(x), nil 94 case int64: 95 return int(x), nil 96 case int: 97 return x, nil 98 default: 99 return 0, fmt.Errorf("field '%s' is not an integer type", keyName) 100 } 101} 102 103// FindSubdocumentByKey returns the value of keyName in document as a document. 104// Returns an error if keyName is not found in the top-level of the document, 105// or if it is found but its value is not a document. 106func FindSubdocumentByKey(keyName string, document *bson.D) (bson.D, error) { 107 value, err := FindValueByKey(keyName, document) 108 if err != nil { 109 return bson.D{}, err 110 } 111 doc, ok := value.(bson.D) 112 if !ok { 113 return bson.D{}, fmt.Errorf("field '%s' is not a document", keyName) 114 } 115 return doc, nil 116} 117 118// ParseSpecialKeys takes a JSON document and inspects it for any extended JSON 119// type (e.g $numberLong) and replaces any such values with the corresponding 120// BSON type. (uses legacy extJSON parser) 121func ParseSpecialKeys(special interface{}) (interface{}, error) { 122 // first ensure we are using a correct document type 123 var doc map[string]interface{} 124 switch v := special.(type) { 125 case bson.D: 126 doc = v.Map() 127 case map[string]interface{}: 128 doc = v 129 default: 130 return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special) 131 } 132 // check document to see if it is special 133 switch len(doc) { 134 case 1: // document has a single field 135 if jsonValue, ok := doc["$date"]; ok { 136 switch v := jsonValue.(type) { 137 case string: 138 return util.FormatDate(v) 139 case bson.D: 140 asMap := v.Map() 141 if jsonValue, ok := asMap["$numberLong"]; ok { 142 n, err := parseNumberLongField(jsonValue) 143 if err != nil { 144 return nil, err 145 } 146 return time.Unix(n/1e3, n%1e3*1e6), err 147 } 148 return nil, errors.New("expected $numberLong field in $date") 149 case map[string]interface{}: 150 if jsonValue, ok := v["$numberLong"]; ok { 151 n, err := parseNumberLongField(jsonValue) 152 if err != nil { 153 return nil, err 154 } 155 return time.Unix(n/1e3, n%1e3*1e6), err 156 } 157 return nil, errors.New("expected $numberLong field in $date") 158 159 case json.Number: 160 n, err := v.Int64() 161 return time.Unix(n/1e3, n%1e3*1e6), err 162 case float64: 163 n := int64(v) 164 return time.Unix(n/1e3, n%1e3*1e6), nil 165 case int32: 166 n := int64(v) 167 return time.Unix(n/1e3, n%1e3*1e6), nil 168 case int64: 169 return time.Unix(v/1e3, v%1e3*1e6), nil 170 171 case json.ISODate: 172 return v, nil 173 174 default: 175 return nil, errors.New("invalid type for $date field") 176 } 177 } 178 179 if jsonValue, ok := doc["$code"]; ok { 180 switch v := jsonValue.(type) { 181 case string: 182 return primitive.JavaScript(v), nil 183 default: 184 return nil, errors.New("expected $code field to have string value") 185 } 186 } 187 188 if jsonValue, ok := doc["$oid"]; ok { 189 switch v := jsonValue.(type) { 190 case string: 191 return primitive.ObjectIDFromHex(v) 192 default: 193 return nil, errors.New("expected $oid field to have string value") 194 } 195 } 196 197 if jsonValue, ok := doc["$numberLong"]; ok { 198 return parseNumberLongField(jsonValue) 199 } 200 201 if jsonValue, ok := doc["$numberInt"]; ok { 202 switch v := jsonValue.(type) { 203 case string: 204 // all of decimal, hex, and octal are supported here 205 n, err := strconv.ParseInt(v, 0, 32) 206 return int32(n), err 207 208 default: 209 return nil, errors.New("expected $numberInt field to have string value") 210 } 211 } 212 213 if jsonValue, ok := doc["$timestamp"]; ok { 214 ts := json.Timestamp{} 215 216 var tsDoc map[string]interface{} 217 switch internalDoc := jsonValue.(type) { 218 case map[string]interface{}: 219 tsDoc = internalDoc 220 case bson.D: 221 tsDoc = internalDoc.Map() 222 default: 223 return nil, errors.New("expected $timestamp key to have internal document") 224 } 225 226 if seconds, ok := tsDoc["t"]; ok { 227 if asUint32, err := util.ToUInt32(seconds); err == nil { 228 ts.Seconds = asUint32 229 } else { 230 return nil, errors.New("expected $timestamp 't' field to be a numeric type") 231 } 232 } else { 233 return nil, errors.New("expected $timestamp to have 't' field") 234 } 235 if inc, ok := tsDoc["i"]; ok { 236 if asUint32, err := util.ToUInt32(inc); err == nil { 237 ts.Increment = asUint32 238 } else { 239 return nil, errors.New("expected $timestamp 'i' field to be a numeric type") 240 } 241 } else { 242 return nil, errors.New("expected $timestamp to have 'i' field") 243 } 244 // see BSON spec for details on the bit fiddling here 245 return primitive.Timestamp{T: uint32(ts.Seconds), I: uint32(ts.Increment)}, nil 246 } 247 248 if jsonValue, ok := doc["$numberDecimal"]; ok { 249 switch v := jsonValue.(type) { 250 case string: 251 return primitive.ParseDecimal128(v) 252 default: 253 return nil, errors.New("expected $numberDecimal field to have string value") 254 } 255 } 256 257 if _, ok := doc["$undefined"]; ok { 258 return primitive.Undefined{}, nil 259 } 260 261 if _, ok := doc["$maxKey"]; ok { 262 return primitive.MaxKey{}, nil 263 } 264 265 if _, ok := doc["$minKey"]; ok { 266 return primitive.MinKey{}, nil 267 } 268 269 case 2: // document has two fields 270 if jsonValue, ok := doc["$code"]; ok { 271 code := primitive.CodeWithScope{} 272 switch v := jsonValue.(type) { 273 case string: 274 code.Code = primitive.JavaScript(v) 275 default: 276 return nil, errors.New("expected $code field to have string value") 277 } 278 279 if jsonValue, ok = doc["$scope"]; ok { 280 switch v2 := jsonValue.(type) { 281 case map[string]interface{}, bson.D: 282 x, err := ParseSpecialKeys(v2) 283 if err != nil { 284 return nil, err 285 } 286 code.Scope = x 287 return code, nil 288 default: 289 return nil, errors.New("expected $scope field to contain map") 290 } 291 } else { 292 return nil, errors.New("expected $scope field with $code field") 293 } 294 } 295 296 if jsonValue, ok := doc["$regex"]; ok { 297 regex := primitive.Regex{} 298 299 switch pattern := jsonValue.(type) { 300 case string: 301 regex.Pattern = pattern 302 303 default: 304 return nil, errors.New("expected $regex field to have string value") 305 } 306 if jsonValue, ok = doc["$options"]; !ok { 307 return nil, errors.New("expected $options field with $regex field") 308 } 309 310 switch options := jsonValue.(type) { 311 case string: 312 regex.Options = options 313 314 default: 315 return nil, errors.New("expected $options field to have string value") 316 } 317 318 // Validate regular expression options 319 for i := range regex.Options { 320 switch o := regex.Options[i]; o { 321 default: 322 return nil, fmt.Errorf("invalid regular expression option '%v'", o) 323 324 case 'g', 'i', 'm', 's': // allowed 325 } 326 } 327 return regex, nil 328 } 329 330 if jsonValue, ok := doc["$binary"]; ok { 331 binary := primitive.Binary{} 332 333 switch data := jsonValue.(type) { 334 case string: 335 bytes, err := base64.StdEncoding.DecodeString(data) 336 if err != nil { 337 return nil, err 338 } 339 binary.Data = bytes 340 341 default: 342 return nil, errors.New("expected $binary field to have string value") 343 } 344 if jsonValue, ok = doc["$type"]; !ok { 345 return nil, errors.New("expected $type field with $binary field") 346 } 347 348 switch typ := jsonValue.(type) { 349 case string: 350 kind, err := hex.DecodeString(typ) 351 if err != nil { 352 return nil, err 353 } else if len(kind) != 1 { 354 return nil, errors.New("expected single byte (as hexadecimal string) for $type field") 355 } 356 binary.Subtype = kind[0] 357 358 default: 359 return nil, errors.New("expected $type field to have string value") 360 } 361 return binary, nil 362 } 363 } 364 365 // nothing matched, so we recurse deeper 366 switch v := special.(type) { 367 case bson.D: 368 return GetExtendedBsonD(v) 369 case map[string]interface{}: 370 return ConvertLegacyExtJSONValueToBSON(v) 371 default: 372 return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special) 373 } 374} 375 376// ParseLegacyExtJSONValue takes any value generated by the json package and returns a 377// BSON version of that value. 378func ParseLegacyExtJSONValue(jsonValue interface{}) (interface{}, error) { 379 switch v := jsonValue.(type) { 380 case map[string]interface{}, bson.D: // subdocument 381 return ParseSpecialKeys(v) 382 383 default: 384 return ConvertLegacyExtJSONValueToBSON(v) 385 } 386} 387 388func parseNumberLongField(jsonValue interface{}) (int64, error) { 389 switch v := jsonValue.(type) { 390 case string: 391 // all of decimal, hex, and octal are supported here 392 return strconv.ParseInt(v, 0, 64) 393 394 default: 395 return 0, errors.New("expected $numberLong field to have string value") 396 } 397} 398