diff --git a/packages/cli-repl/test/e2e.spec.ts b/packages/cli-repl/test/e2e.spec.ts index 6d4eb70677..5899f30dad 100644 --- a/packages/cli-repl/test/e2e.spec.ts +++ b/packages/cli-repl/test/e2e.spec.ts @@ -523,6 +523,13 @@ describe('e2e', function() { expect(await shell.executeLine('typeof JSON.parse(JSON.stringify(db.test.insertOne({}))).insertedId')).to.include('string'); }); + 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', () => { 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/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/' } } }, diff --git a/packages/shell-api/src/integration.spec.ts b/packages/shell-api/src/integration.spec.ts index 222f6aeb38..db12e754bd 100644 --- a/packages/shell-api/src/integration.spec.ts +++ b/packages/shell-api/src/integration.spec.ts @@ -2047,6 +2047,15 @@ 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'); + 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 {