Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/mongodb-constants/src/bson-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,46 @@ const BSON_TYPES = [
description: 'BSON Regex type',
snippet: "RegExp('${1:source}', '${2:opts}')",
},
{
name: 'LegacyJavaUUID',
value: 'LegacyJavaUUID',
label: 'LegacyJavaUUID',
score: 1,
meta: 'bson-legacy-uuid',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meta here will make it into Compass' autocompleter:

Image

version: '0.0.0',
description: 'BSON Binary subtype 3 (Java legacy UUID)',
snippet: "LegacyJavaUUID('${1:uuid}')",
},
{
name: 'LegacyCSharpUUID',
value: 'LegacyCSharpUUID',
label: 'LegacyCSharpUUID',
score: 1,
meta: 'bson-legacy-uuid',
version: '0.0.0',
description: 'BSON Binary subtype 3 (CSharp legacy UUID)',
snippet: "LegacyCSharpUUID('${1:uuid}')",
},
{
name: 'LegacyPythonUUID',
value: 'LegacyPythonUUID',
label: 'LegacyPythonUUID',
score: 1,
meta: 'bson-legacy-uuid',
version: '0.0.0',
description: 'BSON Binary subtype 3 (Python legacy UUID)',
snippet: "LegacyPythonUUID('${1:uuid}')",
},
{
name: 'UUID',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously UUID didn't show up in Compass' autocomplete. I'm thinking it was just missing. This will make it start showing up:

Image

value: 'UUID',
label: 'UUID',
score: 1,
meta: 'bson',
version: '0.0.0',
description: 'BSON Binary subtype 4',
snippet: "UUID('${1:uuid}')",
},
] as const;

export { BSON_TYPES };
63 changes: 63 additions & 0 deletions packages/query-parser/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,36 @@ describe('mongodb-query-parser', function () {
);
});

it('should support LegacyJavaUUID', function () {
assert.deepEqual(
convert('LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")'),
{
$binary: 'd2ZVRDMiEQD/7t3Mu6qZiA==',
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
},
);
});

it('should support LegacyCSharpUUID', function () {
assert.deepEqual(
convert('LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")'),
{
$binary: 'MyIRAFVEd2aImaq7zN3u/w==',
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
},
);
});

it('should support LegacyPythonUUID', function () {
assert.deepEqual(
convert('LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")'),
{
$binary: 'ABEiM0RVZneImaq7zN3u/w==',
$type: `0${bson.Binary.SUBTYPE_UUID_OLD}`,
},
);
});

// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
it('should support Binary.createFromHexString', function () {
assert.deepEqual(
Expand Down Expand Up @@ -643,6 +673,39 @@ e s`,
);
});

it('does not stringify LegacyJavaUUID', function () {
const res = parseFilter(
'{name: LegacyJavaUUID("00112233-4455-6677-8899-aabbccddeeff")}',
);
const stringified = stringify(res);
assert.equal(
stringified,
"{name: BinData(3, 'd2ZVRDMiEQD/7t3Mu6qZiA==')}",
);
});

it('does not stringify LegacyCSharpUUID', function () {
const res = parseFilter(
'{name: LegacyCSharpUUID("00112233-4455-6677-8899-aabbccddeeff")}',
);
const stringified = stringify(res);
assert.equal(
stringified,
"{name: BinData(3, 'MyIRAFVEd2aImaq7zN3u/w==')}",
);
});

it('does not stringify LegacyPythonUUID', function () {
const res = parseFilter(
'{name: LegacyPythonUUID("00112233-4455-6677-8899-aabbccddeeff")}',
);
const stringified = stringify(res);
assert.equal(
stringified,
"{name: BinData(3, 'ABEiM0RVZneImaq7zN3u/w==')}",
);
});

// https://www.mongodb.com/docs/manual/reference/method/Binary.createFromHexString/
it('should support Binary.createFromHexString', function () {
const res = parseFilter(
Expand Down
78 changes: 78 additions & 0 deletions packages/shell-bson-parser/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,78 @@ describe('@mongodb-js/shell-bson-parser', function () {
});
});

it('should create new UUIDs', function () {
expect(parse('{name: UUID()}'))
.to.have.property('name')
.that.is.instanceOf(bson.Binary);
expect(parse('{name: LegacyCSharpUUID()}'))
.to.have.property('name')
.that.is.instanceOf(bson.Binary);
expect(parse('{name: LegacyJavaUUID()}'))
.to.have.property('name')
.that.is.instanceOf(bson.Binary);
expect(parse('{name: LegacyPythonUUID()}'))
.to.have.property('name')
.that.is.instanceOf(bson.Binary);
});

describe('with a set UUID generation', function () {
let sandbox: SinonSandbox;

beforeEach(function () {
sandbox = createSandbox();

sandbox.replace((bson as any).UUID.prototype, 'toHexString', function () {
return '00112233-4455-6677-8899-aabbccddeeff';
});
sandbox.replace((bson as any).UUID.prototype, 'toBinary', function () {
return new bson.Binary(
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
4,
);
});
});
afterEach(function () {
sandbox.restore();
});

it('should create new UUIDs in the correct formats for legacy', function () {
expect(parse('{name: UUID()}')).to.have.deep.equal({
name: new bson.Binary(
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
4,
),
});
expect(parse('{name: LegacyCSharpUUID()}')).to.have.deep.equal({
name: new bson.Binary(
Buffer.from('33221100554477668899aabbccddeeff', 'hex'),
3,
),
});
expect(parse('{name: LegacyJavaUUID()}')).to.have.deep.equal({
name: new bson.Binary(
Buffer.from('7766554433221100ffeeddccbbaa9988', 'hex'),
3,
),
});
expect(parse('{name: LegacyPythonUUID()}')).to.have.deep.equal({
name: new bson.Binary(
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
3,
),
});
});
});

it('should accept a complex query', function () {
expect(
parse(`{
RegExp: /test/ig,
Binary: new Binary(),
BinData: BinData(3, 'dGVzdAo='),
LegacyCSharpUUID: LegacyCSharpUUID('00112233-4455-6677-8899-aabbccddeeff'),
LegacyJavaUUID: LegacyJavaUUID('00112233-4455-6677-8899-aabbccddeeff'),
LegacyPythonUUID: LegacyPythonUUID('00112233-4455-6677-8899-aabbccddeeff'),
UUID: UUID('3d37923d-ab8e-4931-9e46-93df5fd3599e'),
Code: Code('function() {}'),
DBRef: new DBRef('tests', new ObjectId("5e159ba7eac34211f2252aaa"), 'test'),
Expand Down Expand Up @@ -84,6 +150,18 @@ describe('@mongodb-js/shell-bson-parser', function () {
RegExp: /test/gi,
Binary: new bson.Binary(),
BinData: new bson.Binary(Buffer.from('dGVzdAo=', 'base64'), 3),
LegacyCSharpUUID: new bson.Binary(
Buffer.from('33221100554477668899aabbccddeeff', 'hex'),
3,
),
LegacyJavaUUID: new bson.Binary(
Buffer.from('7766554433221100ffeeddccbbaa9988', 'hex'),
3,
),
LegacyPythonUUID: new bson.Binary(
Buffer.from('00112233445566778899aabbccddeeff', 'hex'),
3,
),
UUID: new bson.Binary(
Buffer.from('3d37923dab8e49319e4693df5fd3599e', 'hex'),
4,
Expand Down
72 changes: 72 additions & 0 deletions packages/shell-bson-parser/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,78 @@ const SCOPE_ANY: { [x: string]: Function } = lookupMap({
Binary: function (buffer: any, subType: any) {
return new bson.Binary(buffer, subType);
},

// Legacy UUID functions from
// https://github.com/mongodb/mongo-csharp-driver/blob/ac2b2a61c6b7a193cf0266dfb8c65f86c2bf7572/uuidhelpers.js
LegacyJavaUUID: function (u: any) {
if (u === undefined) {
// Generate a new UUID and format it.
u = new bson.UUID().toHexString();
Copy link
Member Author

@Anemy Anemy Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatting with @nbbeeken I think we were thinking of throwing an error here instead. This is when someone writes new LegacyJavaUUID() without a string in the quotes. Currently UUID creates a new UUID so I had initially written it to also generate a new UUID (of subtype 3).
Any other opinions on if it we should throw an error or allow it? I'll add an error throw otherwise. I'll ask Betsy on it too.

}

let hex: string = String.prototype.replace.call(u, /[{}-]/g, () => '');
let msb = String.prototype.substring.call(hex, 0, 16);
let lsb = String.prototype.substring.call(hex, 16, 32);
msb =
String.prototype.substring.call(msb, 14, 16) +
String.prototype.substring.call(msb, 12, 14) +
String.prototype.substring.call(msb, 10, 12) +
String.prototype.substring.call(msb, 8, 10) +
String.prototype.substring.call(msb, 6, 8) +
String.prototype.substring.call(msb, 4, 6) +
String.prototype.substring.call(msb, 2, 4) +
String.prototype.substring.call(msb, 0, 2);
lsb =
String.prototype.substring.call(lsb, 14, 16) +
String.prototype.substring.call(lsb, 12, 14) +
String.prototype.substring.call(lsb, 10, 12) +
String.prototype.substring.call(lsb, 8, 10) +
String.prototype.substring.call(lsb, 6, 8) +
String.prototype.substring.call(lsb, 4, 6) +
String.prototype.substring.call(lsb, 2, 4) +
String.prototype.substring.call(lsb, 0, 2);
hex = msb + lsb;

const hexBuffer = Buffer.from(hex, 'hex');
return new bson.Binary(hexBuffer, 3);
},
LegacyCSharpUUID: function (u: any) {
if (u === undefined) {
// Generate a new UUID and format it.
u = new bson.UUID().toHexString();
}

let hex: string = String.prototype.replace.call(u, /[{}-]/g, () => '');
const a =
String.prototype.substring.call(hex, 6, 8) +
String.prototype.substring.call(hex, 4, 6) +
String.prototype.substring.call(hex, 2, 4) +
String.prototype.substring.call(hex, 0, 2);
const b =
String.prototype.substring.call(hex, 10, 12) +
String.prototype.substring.call(hex, 8, 10);
const c =
String.prototype.substring.call(hex, 14, 16) +
String.prototype.substring.call(hex, 12, 14);
const d = String.prototype.substring.call(hex, 16, 32);
hex = a + b + c + d;

const hexBuffer = Buffer.from(hex, 'hex');
return new bson.Binary(hexBuffer, 3);
},
LegacyPythonUUID: function (u: any) {
if (u === undefined) {
return new bson.Binary(new bson.UUID().toBinary().buffer, 3);
}

return new bson.Binary(
Buffer.from(
String.prototype.replace.call(u, /[{}-]/g, () => ''),
'hex',
),
3,
);
},
BinData: function (t: any, d: any) {
return new bson.Binary(Buffer.from(d, 'base64'), t);
},
Expand Down
Loading