External IDs Migrated to Array Format
| Date | 2026-04-04 |
| Status | Accepted |
| Decision Makers | @benjaminW78 |
| Supersedes | Nested externalIds by Finish |
Context and Problem Statement
The original externalIds ADR chose a nested object structure { marketplace: { finish: id } } to store per-finish marketplace IDs. While correct, this structure required $or queries across all possible finish types (10+) to find a product by external ID — slow and hard to index.
All game importers (not just SWU) needed efficient indexed lookups on external IDs for price matching and marketplace integration.
Decision Drivers
- Query performance:
$oracross 10+ finish keys is slow and un-indexable - MongoDB multikey indexes work well on arrays but not on dynamic object keys
- All games need the same indexed lookup pattern, not just SWU
- Existing data in production needs migration
Decision Outcome
Migrate from nested objects to arrays of { finish, id } entries:
// Before (nested object)
externalIds: {
TCGplayer: { Standard: 12345, Foil: 12346 },
Cardmarket: { Standard: 67890 },
}
// After (array format)
externalIds: {
TCGplayer: [{ finish: "Standard", id: 12345 }, { finish: "Foil", id: 12346 }],
Cardmarket: [{ finish: "Standard", id: 67890 }],
}Enables a single indexed query: { "externalIds.TCGplayer.id": productId } using MongoDB multikey array indexes.
Impacted Files
| File | Change |
|---|---|
packages/games/game-configuration/src/lib/products.types.ts | ExternalIdsByFinish → ExternalIdEntry[] arrays |
packages/core/db/src/mongo/product.model.ts | Schema updated for array subdocuments |
packages/games/game-importer/src/base-importer.ts | Merge logic produces array format |
| All game importers | Produce [{ finish, id }] arrays |
| All price importers | Query by .id with $in instead of per-finish $or |
| Migration script | Converts products + productview collections |
Consequences
Positive
- Single indexed query to find product by marketplace ID
- Consistent structure across all games
- MongoDB multikey indexes on
.idfield
Negative
- Breaking change requiring data migration in production
- Schema uses
Mixedtype to preserveInferSchemaTypecompatibility
More Information
- Original decision: Nested externalIds by Finish
- PR: #2719
Last updated on