From c18138d8bb51fad4a92ea23b00b1b882ded6b2bb Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 29 Nov 2022 14:34:31 +0100 Subject: [PATCH 1/3] feat(shell-api): add convertShardKeyToHashed() MONGOSH-1050 --- packages/cli-repl/test/e2e.spec.ts | 4 +++ packages/connectivity-tests/test/atlas.sh | 4 ++- packages/shell-api/src/integration.spec.ts | 7 ++++++ packages/shell-api/src/mongo.ts | 29 ++++++++++++++++++++++ packages/shell-api/src/shell-api.ts | 5 ++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/cli-repl/test/e2e.spec.ts b/packages/cli-repl/test/e2e.spec.ts index 6d4eb70677..9cdd3bb996 100644 --- a/packages/cli-repl/test/e2e.spec.ts +++ b/packages/cli-repl/test/e2e.spec.ts @@ -523,6 +523,10 @@ describe('e2e', function() { expect(await shell.executeLine('typeof JSON.parse(JSON.stringify(db.test.insertOne({}))).insertedId')).to.include('string'); }); + it('allows calling convertShardKeyToHashed() as a global function', async function() { + expect(await shell.executeLine('convertShardKeyToHashed({foo:"bar"})')).to.include('Long("4975617422686807705")'); + }); + describe('document validation errors', () => { context('post-4.4', () => { skipIfServerVersion(testServer, '<= 4.4'); diff --git a/packages/connectivity-tests/test/atlas.sh b/packages/connectivity-tests/test/atlas.sh index 93ae5c7e48..b370516bb6 100755 --- a/packages/connectivity-tests/test/atlas.sh +++ b/packages/connectivity-tests/test/atlas.sh @@ -38,7 +38,9 @@ fi FAILED=no -CONNECTION_STATUS_COMMAND='db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers' +# convertShardKeyToHashed() may seem weird to include here, but it's the best +# way to make sure that it works in our standard environments we connect to +CONNECTION_STATUS_COMMAND='convertShardKeyToHashed("asdf");db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers' CONNECTION_STATUS_CHECK_STRING="user: '${ATLAS_USERNAME}'" function check_failed() { diff --git a/packages/shell-api/src/integration.spec.ts b/packages/shell-api/src/integration.spec.ts index 222f6aeb38..66b7ec1b7d 100644 --- a/packages/shell-api/src/integration.spec.ts +++ b/packages/shell-api/src/integration.spec.ts @@ -2047,6 +2047,13 @@ describe('Shell API (integration)', function() { } }); }); + describe('convertShardKeyToHashed', () => { + it('converts a shard key to its hashed representation', async() => { + const result = await mongo.convertShardKeyToHashed({ foo: 'bar' }); + expect(result.constructor.name).to.equal('Long'); + expect(result.toString()).to.equal('4975617422686807705'); + }); + }); }); describe('PlanCache', () => { skipIfApiStrict(); diff --git a/packages/shell-api/src/mongo.ts b/packages/shell-api/src/mongo.ts index b4ba65224c..406c7e771b 100644 --- a/packages/shell-api/src/mongo.ts +++ b/packages/shell-api/src/mongo.ts @@ -667,4 +667,33 @@ export default class Mongo extends ShellApiClass { } return this._keyVault; } + + @returnsPromise + async convertShardKeyToHashed(value: any): Promise { + const pipeline = [ + { $limit: 1 }, + { $project: { _id: { $toHashedIndexKey: { $literal: value } } } } + ]; + let result; + try { + // Try $documents if available. + result = await (await this.getDB('admin').aggregate([ { $documents: [{}] }, ...pipeline ])).next(); + } catch { + try { + // If that fails, try a default collection like admin.system.version. + result = await (await this.getDB('admin').getCollection('system.version').aggregate(pipeline)).next(); + } catch { + // If that fails, try using $collStats for local.oplog.rs. + try { + result = await (await this.getDB('local').getCollection('oplog.rs').aggregate([ { $collStats: {} }, ...pipeline ])).next(); + } catch { /* throw exception below */ } + } + } + if (!result) { + throw new MongoshRuntimeError( + 'Could not find a suitable way to run convertShardKeyToHashed() -- tried $documents and aggregating on admin.system.version and local.oplog.rs', + CommonErrors.CommandFailed); + } + return result._id; + } } diff --git a/packages/shell-api/src/shell-api.ts b/packages/shell-api/src/shell-api.ts index 304807c693..03701138c9 100644 --- a/packages/shell-api/src/shell-api.ts +++ b/packages/shell-api/src/shell-api.ts @@ -316,6 +316,11 @@ export default class ShellApi extends ShellApiClass { await this._print(origArgs, 'printjson'); } + @returnsPromise + async convertShardKeyToHashed(value: any): Promise { + return this._instanceState.currentDb._mongo.convertShardKeyToHashed(value); + } + @directShellCommand @returnsPromise async cls(): Promise { From 026334878bbe5f482389b0748f981ee4f493b061 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 29 Nov 2022 15:29:54 +0100 Subject: [PATCH 2/3] fixup: skip tests on < 4.4 --- packages/cli-repl/test/e2e.spec.ts | 7 +++++-- packages/shell-api/src/integration.spec.ts | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/cli-repl/test/e2e.spec.ts b/packages/cli-repl/test/e2e.spec.ts index 9cdd3bb996..5899f30dad 100644 --- a/packages/cli-repl/test/e2e.spec.ts +++ b/packages/cli-repl/test/e2e.spec.ts @@ -523,8 +523,11 @@ describe('e2e', function() { expect(await shell.executeLine('typeof JSON.parse(JSON.stringify(db.test.insertOne({}))).insertedId')).to.include('string'); }); - it('allows calling convertShardKeyToHashed() as a global function', async function() { - expect(await shell.executeLine('convertShardKeyToHashed({foo:"bar"})')).to.include('Long("4975617422686807705")'); + context('post-4.2', () => { + skipIfServerVersion(testServer, '< 4.4'); + it('allows calling convertShardKeyToHashed() as a global function', async function() { + expect(await shell.executeLine('convertShardKeyToHashed({foo:"bar"})')).to.include('Long("4975617422686807705")'); + }); }); describe('document validation errors', () => { diff --git a/packages/shell-api/src/integration.spec.ts b/packages/shell-api/src/integration.spec.ts index 66b7ec1b7d..db12e754bd 100644 --- a/packages/shell-api/src/integration.spec.ts +++ b/packages/shell-api/src/integration.spec.ts @@ -2048,6 +2048,8 @@ describe('Shell API (integration)', function() { }); }); describe('convertShardKeyToHashed', () => { + skipIfServerVersion(testServer, '< 4.4'); + it('converts a shard key to its hashed representation', async() => { const result = await mongo.convertShardKeyToHashed({ foo: 'bar' }); expect(result.constructor.name).to.equal('Long'); From fe94c1491f9e00c5bca07fef3fb815a66fc97835 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 30 Nov 2022 11:14:10 +0100 Subject: [PATCH 3/3] fixup: add definitions to i18n package --- packages/i18n/src/locales/en_US.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/i18n/src/locales/en_US.ts b/packages/i18n/src/locales/en_US.ts index df98da0bb2..5905302589 100644 --- a/packages/i18n/src/locales/en_US.ts +++ b/packages/i18n/src/locales/en_US.ts @@ -168,6 +168,10 @@ const translations: Catalog = { }, isInteractive: { description: 'Returns whether the shell will enter or has entered interactive mode' + }, + convertShardKeyToHashed: { + description: 'Returns the hashed value for the input using the same hashing function as a hashed index.', + link: 'https://www.mongodb.com/docs/manual/reference/method/convertShardKeyToHashed/' } } }, @@ -1798,6 +1802,10 @@ const translations: Catalog = { getClientEncryption: { description: 'Returns the ClientEncryption object for the current database collection. The ClientEncryption object supports explicit (manual) encryption and decryption of field values for Client-Side field level encryption.', link: 'https://docs.mongodb.com/manual/reference/method/getClientEncryption/#getClientEncryption' + }, + convertShardKeyToHashed: { + description: 'Returns the hashed value for the input using the same hashing function as a hashed index.', + link: 'https://www.mongodb.com/docs/manual/reference/method/convertShardKeyToHashed/' } } },