Skip to content

Commit fab9688

Browse files
committed
TOOLS-4148 Convert mode_upsert_id_subdoc.js to Go
TestImportModeUpsertIDSubdoc verifies that --mode=upsert correctly uses a full subdocument _id as the upsert key, preserving field order through a round-trip of export → upsert-replace → re-import. Helpers added: - exportCollectionToFile: exports a collection to a canonical JSON temp file - writeSubdocIDFile: writes a JSON lines file for the subdoc-_id docs - subdocIDDocs: builds the 20 test docs with complex nested _ids
1 parent 14528f8 commit fab9688

2 files changed

Lines changed: 110 additions & 86 deletions

File tree

mongoimport/mongoimport_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3467,6 +3467,116 @@ func checkByIDImport(t *testing.T, coll *mongo.Collection, isMerge bool) {
34673467
}
34683468
}
34693469

3470+
// TestImportModeUpsertIDSubdoc verifies that --mode=upsert uses the full
3471+
// subdocument _id as the upsert key, preserving field order through a
3472+
// round-trip of export → upsert-replace → re-import.
3473+
func TestImportModeUpsertIDSubdoc(t *testing.T) {
3474+
testtype.SkipUnlessTestType(t, testtype.IntegrationTestType)
3475+
3476+
const (
3477+
dbName = "mongoimport_upsert_subdoc_test"
3478+
collName = "c"
3479+
)
3480+
3481+
sessionProvider, _, err := testutil.GetBareSessionProvider()
3482+
require.NoError(t, err)
3483+
client, err := sessionProvider.GetSession()
3484+
require.NoError(t, err)
3485+
t.Cleanup(func() {
3486+
_ = client.Database(dbName).Drop(context.Background())
3487+
})
3488+
3489+
coll := client.Database(dbName).Collection(collName)
3490+
ns := &options.Namespace{DB: dbName, Collection: collName}
3491+
3492+
origDocs := subdocIDDocs("string")
3493+
insertDocs := make([]any, len(origDocs))
3494+
for i, d := range origDocs {
3495+
insertDocs[i] = d
3496+
}
3497+
_, err = coll.InsertMany(t.Context(), insertDocs)
3498+
require.NoError(t, err)
3499+
3500+
exportedFile := exportCollectionToFile(t, ns)
3501+
str2File := writeSubdocIDFile(t, "str2")
3502+
3503+
t.Run("upsert with replacement data updates all docs in place", func(t *testing.T) {
3504+
require.NoError(t, runImportOpts(t, ns, str2File, IngestOptions{Mode: modeUpsert}))
3505+
n, err := coll.CountDocuments(t.Context(), bson.D{})
3506+
require.NoError(t, err)
3507+
assert.EqualValues(t, 20, n, "count should be unchanged after upsert")
3508+
n, err = coll.CountDocuments(t.Context(), bson.D{{"x", "str2"}})
3509+
require.NoError(t, err)
3510+
assert.EqualValues(t, 20, n, "all docs should have x=str2 after upsert")
3511+
})
3512+
3513+
t.Run("re-import original export reverts all docs", func(t *testing.T) {
3514+
require.NoError(t, runImportOpts(t, ns, exportedFile, IngestOptions{Mode: modeUpsert}))
3515+
n, err := coll.CountDocuments(t.Context(), bson.D{})
3516+
require.NoError(t, err)
3517+
assert.EqualValues(t, 20, n, "count should be unchanged after re-import")
3518+
n, err = coll.CountDocuments(t.Context(), bson.D{{"x", "string"}})
3519+
require.NoError(t, err)
3520+
assert.EqualValues(t, 20, n, "all docs should have x=string after re-import")
3521+
})
3522+
}
3523+
3524+
func exportCollectionToFile(t *testing.T, ns *options.Namespace) string {
3525+
t.Helper()
3526+
exportFile, err := os.CreateTemp(t.TempDir(), "export-*.json")
3527+
require.NoError(t, err)
3528+
exportToolOptions, err := testutil.GetToolOptions()
3529+
require.NoError(t, err)
3530+
exportToolOptions.Namespace = ns
3531+
me, err := mongoexport.New(mongoexport.Options{
3532+
ToolOptions: exportToolOptions,
3533+
OutputFormatOptions: &mongoexport.OutputFormatOptions{
3534+
Type: "json",
3535+
JSONFormat: "canonical",
3536+
},
3537+
InputOptions: &mongoexport.InputOptions{},
3538+
})
3539+
require.NoError(t, err)
3540+
defer me.Close()
3541+
_, err = me.Export(exportFile)
3542+
require.NoError(t, err)
3543+
require.NoError(t, exportFile.Close())
3544+
return exportFile.Name()
3545+
}
3546+
3547+
func writeSubdocIDFile(t *testing.T, xFieldValue string) string {
3548+
t.Helper()
3549+
f, err := os.CreateTemp(t.TempDir(), "subdoc-*.json")
3550+
require.NoError(t, err)
3551+
for _, doc := range subdocIDDocs(xFieldValue) {
3552+
b, err := bson.MarshalExtJSON(doc, true, false)
3553+
require.NoError(t, err)
3554+
_, err = f.Write(b)
3555+
require.NoError(t, err)
3556+
_, err = f.Write([]byte("\n"))
3557+
require.NoError(t, err)
3558+
}
3559+
require.NoError(t, f.Close())
3560+
return f.Name()
3561+
}
3562+
3563+
func subdocIDDocs(xFieldValue string) []bson.D {
3564+
docs := make([]bson.D, 0, 20)
3565+
for i := range int32(4) {
3566+
for j := range int32(5) {
3567+
docs = append(docs, bson.D{
3568+
{"_id", bson.D{
3569+
{"a", i},
3570+
{"b", bson.A{int32(0), int32(1), int32(2), bson.D{{"c", j}, {"d", "foo"}}}},
3571+
{"e", "bar"},
3572+
}},
3573+
{"x", xFieldValue},
3574+
})
3575+
}
3576+
}
3577+
return docs
3578+
}
3579+
34703580
// writeJSONLinesFile marshals each doc in docs as a JSON object and writes them
34713581
// as newline-separated lines to a file in dir named name. Returns the file path.
34723582
func writeJSONLinesFile(t *testing.T, dir, name string, docs []map[string]any) string {

test/qa-tests/jstests/import/mode_upsert_id_subdoc.js

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)