func GetCollectionOptions(coll *mgo.Collection) (*bson.D, error) { iter, useFullName, err := GetCollections(coll.Database, coll.Name) if err != nil { return nil, err } comparisonName := coll.Name if useFullName { comparisonName = coll.FullName } collInfo := &bson.D{} for iter.Next(collInfo) { name, err := bsonutil.FindValueByKey("name", collInfo) if err != nil { collInfo = nil continue } if nameStr, ok := name.(string); ok { if nameStr == comparisonName { // we've found the collection we're looking for return collInfo, nil } } else { collInfo = nil continue } } err = iter.Err() if err != nil { return nil, err } // The given collection was not found, but no error encountered. return nil, nil }
// getUpsertValue takes a given BSON document and a given field, and returns the // field's associated value in the document. The field is specified using dot // notation for nested fields. e.g. "person.age" would return 34 would return // 34 in the document: bson.M{"person": bson.M{"age": 34}} whereas, // "person.name" would return nil func getUpsertValue(field string, document bson.D) interface{} { index := strings.Index(field, ".") if index == -1 { // grab the value (ignoring errors because we are okay with nil) val, _ := bsonutil.FindValueByKey(field, &document) return val } // recurse into subdocuments left := field[0:index] subDoc, _ := bsonutil.FindValueByKey(left, &document) if subDoc == nil { return nil } subDocD, ok := subDoc.(bson.D) if !ok { return nil } return getUpsertValue(field[index+1:], subDocD) }
// extractFieldByName takes a field name and document, and returns a value representing // the value of that field in the document in a format that can be printed as a string. // It will also handle dot-delimited field names for nested arrays or documents. func extractFieldByName(fieldName string, document interface{}) interface{} { dotParts := strings.Split(fieldName, ".") var subdoc interface{} = document for _, path := range dotParts { docValue := reflect.ValueOf(subdoc) if !docValue.IsValid() { return "" } docType := docValue.Type() docKind := docType.Kind() if docKind == reflect.Map { subdocVal := docValue.MapIndex(reflect.ValueOf(path)) if subdocVal.Kind() == reflect.Invalid { return "" } subdoc = subdocVal.Interface() } else if docKind == reflect.Slice { if docType == marshalDType { // dive into a D as a document asD := bson.D(subdoc.(bsonutil.MarshalD)) var err error subdoc, err = bsonutil.FindValueByKey(path, &asD) if err != nil { return "" } } else { // check that the path can be converted to int arrayIndex, err := strconv.Atoi(path) if err != nil { return "" } // bounds check for slice if arrayIndex < 0 || arrayIndex >= docValue.Len() { return "" } subdocVal := docValue.Index(arrayIndex) if subdocVal.Kind() == reflect.Invalid { return "" } subdoc = subdocVal.Interface() } } else { // trying to index into a non-compound type - just return blank. return "" } } return subdoc }
// CreateCollectionIntent builds an intent for a given collection and // puts it into the intent manager. func (dump *MongoDump) CreateCollectionIntent(dbName, colName string) error { if dump.shouldSkipCollection(colName) { log.Logvf(log.DebugLow, "skipping dump of %v.%v, it is excluded", dbName, colName) return nil } intent, err := dump.NewIntent(dbName, colName) if err != nil { return err } session, err := dump.sessionProvider.GetSession() if err != nil { return err } defer session.Close() opts, err := db.GetCollectionOptions(session.DB(dbName).C(colName)) if err != nil { return fmt.Errorf("error getting collection options: %v", err) } intent.Options = nil if opts != nil { optsInterface, _ := bsonutil.FindValueByKey("options", opts) if optsInterface != nil { if optsD, ok := optsInterface.(bson.D); ok { intent.Options = &optsD } else { return fmt.Errorf("Failed to parse collection options as bson.D") } } } dump.manager.Put(intent) log.Logvf(log.DebugLow, "enqueued collection '%v'", intent.Namespace()) return nil }
// setNestedValue takes a nested field - in the form "a.b.c" - // its associated value, and a document. It then assigns that // value to the appropriate nested field within the document func setNestedValue(key string, value interface{}, document *bson.D) { index := strings.Index(key, ".") if index == -1 { *document = append(*document, bson.DocElem{key, value}) return } keyName := key[0:index] subDocument := &bson.D{} elem, err := bsonutil.FindValueByKey(keyName, document) if err != nil { // no such key in the document elem = nil } var existingKey bool if elem != nil { subDocument = elem.(*bson.D) existingKey = true } setNestedValue(key[index+1:], value, subDocument) if !existingKey { *document = append(*document, bson.DocElem{keyName, subDocument}) } }