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