Last active
June 17, 2025 18:54
-
-
Save jdnichollsc/752a5246bee09ab2faf82841e5defb42 to your computer and use it in GitHub Desktop.
NodeJS MongoDB/Mongoose UUID conversion for .NET Guid compatibility using BSON Binary (subtype 3) - https://replit.com/@jdnichollsc/MongoDB-Guid-conversion-for-NodeJSNET-compatibility?v=1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mongoose from "mongoose"; | |
// Converts a legacy MongoDB Binary (subtype 3, UUID_OLD) to a UUID string. | |
// Note: The issue is not with .NET's Guid type (which is just a 128-bit integer), | |
// but with how the legacy MongoDB driver (and the old BinData subtype 3) handled byte order. | |
// The legacy driver stored UUIDs with a mixed-endian format due to a lack of specification in early MongoDB versions. | |
export function legacyBinaryToUuidString(binaryData: mongoose.mongo.Binary | null) { | |
if (!binaryData?.buffer) return undefined; | |
const buffer = binaryData.buffer; | |
// Legacy MongoDB UUID (subtype 3) uses little-endian for the first 3 components | |
// and big-endian for the rest, due to the old driver implementation. | |
const part1 = Buffer.from(buffer.subarray(0, 4)).reverse().toString("hex"); // First 4 bytes reversed | |
const part2 = Buffer.from(buffer.subarray(4, 6)).reverse().toString("hex"); // Next 2 bytes reversed | |
const part3 = Buffer.from(buffer.subarray(6, 8)).reverse().toString("hex"); // Next 2 bytes reversed | |
const part4 = Buffer.from(buffer.subarray(8, 10)).toString("hex"); // Next 2 bytes as-is | |
const part5 = Buffer.from(buffer.subarray(10, 16)).toString("hex"); // Last 6 bytes as-is | |
return `${part1}-${part2}-${part3}-${part4}-${part5}`; | |
} | |
// Converts a UUID string to a legacy MongoDB Binary (subtype 3, UUID_OLD). | |
// This matches the byte order used by the old MongoDB .NET driver (subtype 3). | |
export function uuidStringToLegacyBinary(uuidStr: string) { | |
if (!uuidStr || typeof uuidStr !== "string") return undefined; | |
const hex = uuidStr.replace(/-/g, ""); | |
// To reverse the conversion, we need to reverse the first 3 parts back to little-endian | |
const part1 = Buffer.from(hex.slice(0, 8), "hex").reverse(); // First 4 bytes - reverse back | |
const part2 = Buffer.from(hex.slice(8, 12), "hex").reverse(); // Next 2 bytes - reverse back | |
const part3 = Buffer.from(hex.slice(12, 16), "hex").reverse(); // Next 2 bytes - reverse back | |
const part4 = Buffer.from(hex.slice(16, 20), "hex"); // Next 2 bytes - keep as-is | |
const part5 = Buffer.from(hex.slice(20, 32), "hex"); // Last 6 bytes - keep as-is | |
const legacyBuffer = Buffer.concat([part1, part2, part3, part4, part5]); | |
return new mongoose.mongo.Binary(legacyBuffer, mongoose.mongo.Binary.SUBTYPE_UUID_OLD); | |
} | |
// Normalizes a GUID value to a legacy MongoDB Binary (subtype 3) if needed. | |
// Accepts a UUID string, Buffer, or Binary. Throws for invalid input. | |
export function normalizeGuidValue(val: string | Buffer | mongoose.mongo.Binary) { | |
if (typeof val === "string") { | |
return uuidStringToLegacyBinary(val); | |
} | |
if (Buffer.isBuffer(val)) { | |
return new mongoose.mongo.Binary(val, 3); | |
} | |
if (val instanceof mongoose.mongo.Binary) { | |
return val; | |
} | |
throw new Error("Invalid CountryID format for query"); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mongoose, { Schema } from "mongoose"; | |
import { legacyBinaryToUuidString, normalizeGuidValue } from "./helpers"; | |
const addressSchema = new mongoose.Schema( | |
{ | |
Country: { type: String }, | |
CountryCode: { type: String }, | |
CountryID: { | |
type: Schema.Types.Mixed, | |
get: (val: string | mongoose.mongo.Binary | Buffer | undefined) => { | |
if (!val) return undefined; | |
if (typeof val === "string") return val; | |
if (Buffer.isBuffer(val)) { | |
const binary = new mongoose.mongo.Binary(val, 3); | |
return legacyBinaryToUuidString(binary); | |
} | |
if (val instanceof mongoose.mongo.Binary) { | |
return legacyBinaryToUuidString(val); | |
} | |
throw new Error("Unsupported format for CountryID"); | |
}, | |
set: (val: string | mongoose.mongo.Binary | Buffer | undefined) => { | |
if (!val) return undefined; | |
return normalizeGuidValue(val); | |
}, | |
}, | |
}, | |
{ | |
_id: false, | |
toObject: { getters: true }, | |
toJSON: { getters: true }, | |
}, | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment