@@ -2271,3 +2271,88 @@ func TestRoundTripLimit(t *testing.T) {
22712271 assert .EqualValues (t , 1 , c , "document with a=%d should exist (first 20 by sort)" , i )
22722272 }
22732273}
2274+
2275+ // TestRoundTripNestedFieldsCSV verifies that mongoexport correctly exports
2276+ // nested dotted field paths to CSV and that mongoimport restores them
2277+ // (from nested_fields_csv.js).
2278+ func TestRoundTripNestedFieldsCSV (t * testing.T ) {
2279+ testtype .SkipUnlessTestType (t , testtype .IntegrationTestType )
2280+
2281+ const dbName = "mongoimport_roundtrip_nestedcsv_test"
2282+
2283+ sessionProvider , _ , err := testutil .GetBareSessionProvider ()
2284+ require .NoError (t , err )
2285+ client , err := sessionProvider .GetSession ()
2286+ require .NoError (t , err )
2287+ t .Cleanup (func () {
2288+ if err := client .Database (dbName ).Drop (context .Background ()); err != nil {
2289+ t .Errorf ("dropping test database: %v" , err )
2290+ }
2291+ })
2292+
2293+ db := client .Database (dbName )
2294+ _ , err = db .Collection ("source" ).InsertMany (t .Context (), []any {
2295+ bson.D {{"a" , int32 (1 )}},
2296+ bson.D {{"a" , int32 (2 )}, {"b" , bson.D {{"c" , int32 (2 )}}}},
2297+ bson.D {{"a" , int32 (3 )}, {"b" , bson.D {{"c" , int32 (3 )}, {"d" , bson.D {{"e" , int32 (3 )}}}}}},
2298+ bson.D {{"a" , int32 (4 )}, {"x" , nil }},
2299+ })
2300+ require .NoError (t , err )
2301+
2302+ exportToolOptions , err := testutil .GetToolOptions ()
2303+ require .NoError (t , err )
2304+ exportToolOptions .Namespace = & options.Namespace {DB : dbName , Collection : "source" }
2305+ me , err := mongoexport .New (mongoexport.Options {
2306+ ToolOptions : exportToolOptions ,
2307+ OutputFormatOptions : & mongoexport.OutputFormatOptions {
2308+ Type : "csv" ,
2309+ JSONFormat : "canonical" ,
2310+ Fields : "a,b.d.e,x.y" ,
2311+ },
2312+ InputOptions : & mongoexport.InputOptions {},
2313+ })
2314+ require .NoError (t , err )
2315+ defer me .Close ()
2316+ tmpFile , err := os .CreateTemp (t .TempDir (), "export-*.csv" )
2317+ require .NoError (t , err )
2318+ _ , err = me .Export (tmpFile )
2319+ require .NoError (t , err )
2320+ require .NoError (t , tmpFile .Close ())
2321+
2322+ importToolOptions , err := testutil .GetToolOptions ()
2323+ require .NoError (t , err )
2324+ importToolOptions .Namespace = & options.Namespace {DB : dbName , Collection : "dest" }
2325+ mi , err := New (Options {
2326+ ToolOptions : importToolOptions ,
2327+ InputOptions : & InputOptions {
2328+ File : tmpFile .Name (),
2329+ Type : "csv" ,
2330+ HeaderLine : true ,
2331+ ParseGrace : "stop" ,
2332+ },
2333+ IngestOptions : & IngestOptions {},
2334+ })
2335+ require .NoError (t , err )
2336+ _ , _ , err = mi .ImportDocuments ()
2337+ require .NoError (t , err )
2338+
2339+ dest := db .Collection ("dest" )
2340+ for _ , tc := range []struct {
2341+ filter bson.D
2342+ count int64
2343+ msg string
2344+ }{
2345+ {bson.D {{"b.c" , int32 (2 )}}, 0 , "b.c should not have been exported" },
2346+ {bson.D {{"b.c" , int32 (3 )}}, 0 , "b.c should not have been exported" },
2347+ {bson.D {{"b.d.e" , int32 (3 )}}, 1 , "b.d.e=3 should be present" },
2348+ {bson.D {{"b.d.e" , "" }}, 3 , "b.d.e should be empty string for 3 docs" },
2349+ {bson.D {{"a" , int32 (1 )}}, 1 , "a=1 should be present" },
2350+ {bson.D {{"a" , int32 (2 )}}, 1 , "a=2 should be present" },
2351+ {bson.D {{"a" , int32 (3 )}}, 1 , "a=3 should be present" },
2352+ {bson.D {{"x.y" , "" }}, 4 , "x.y should be empty string for all 4 docs" },
2353+ } {
2354+ n , err := dest .CountDocuments (t .Context (), tc .filter )
2355+ require .NoError (t , err )
2356+ assert .EqualValues (t , tc .count , n , tc .msg )
2357+ }
2358+ }
0 commit comments