From 99f5b0daed63fa5960e1c1c0e14ed26ff2dcc829 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 20 Feb 2023 08:27:49 +0100 Subject: [PATCH 1/5] fixes autocomplete not working for db.aggregate --- packages/autocomplete/src/index.spec.ts | 58 +++++++++++++++++++++++++ packages/autocomplete/src/index.ts | 23 +++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/autocomplete/src/index.spec.ts b/packages/autocomplete/src/index.spec.ts index 36db9a214..d788f147f 100644 --- a/packages/autocomplete/src/index.spec.ts +++ b/packages/autocomplete/src/index.spec.ts @@ -5,6 +5,17 @@ import { expect } from 'chai'; let collections: string[]; let databases: string[]; +const standalone600 = { + topology: () => Topologies.Standalone, + apiVersionInfo: () => undefined, + connectionInfo: () => ({ + is_atlas: false, + is_data_federation: false, + server_version: '6.0.0' + }), + getCollectionCompletionsForCurrentDb: () => collections, + getDatabaseCompletions: () => databases +}; const standalone440 = { topology: () => Topologies.Standalone, apiVersionInfo: () => undefined, @@ -324,6 +335,53 @@ describe('completer.completer', () => { }); }); + context('when context is db aggregation query', () => { + it('has several matches for db level stages', async() => { + const query = 'db.aggregate([{'; + expect(await completer(standalone440, query)).to.deep.equal([ + [ + 'db.aggregate([{$changeStream', + 'db.aggregate([{$currentOp', + 'db.aggregate([{$listLocalSessions', + ], + query + ]); + expect(await completer(standalone600, query)).to.deep.equal([ + [ + 'db.aggregate([{$documents', + 'db.aggregate([{$changeStream', + 'db.aggregate([{$currentOp', + 'db.aggregate([{$listLocalSessions', + ], + query + ]); + }); + + it('does not have a match', async() => { + const query = 'db.aggregate([{$mat'; + expect(await completer(standalone440, query)).to.deep.equal([[], query]); + }); + + it('matches a db aggregation stage', async() => { + const query = 'db.aggregate([{$lis'; + expect(await completer(standalone440, query)).to.deep.equal([ + ['db.aggregate([{$listLocalSessions'], + query + ]); + }); + + it('completes the followup stages', async() => { + const query = 'db.aggregate([{$currentOp: {}}, {$ma'; + expect(await completer(standalone440, query)).to.deep.equal([ + [ + 'db.aggregate([{$currentOp: {}}, {$map', + 'db.aggregate([{$currentOp: {}}, {$match' + ], + query + ]); + }); + }); + context('when context is aggregation query', () => { it('has several matches', async() => { const i = 'db.shipwrecks.aggregate([ { $so'; diff --git a/packages/autocomplete/src/index.ts b/packages/autocomplete/src/index.ts index 67bb61d14..c331c3ff1 100644 --- a/packages/autocomplete/src/index.ts +++ b/packages/autocomplete/src/index.ts @@ -11,7 +11,8 @@ import { BSON_TYPES, ATLAS, ADL, - ON_PREM + ON_PREM, + DATABASE } from '@mongodb-js/mongodb-constants'; type TypeSignatureAttributes = { [key: string]: TypeSignature }; @@ -49,6 +50,10 @@ export const MATCH_COMPLETIONS = ([] as AnyCompletions).concat( BSON_TYPES ); +const DB_AGGREGATE_COMPLETIONS = STAGE_OPERATORS.filter(({ namespaces }) => { + return namespaces.length === 1 && (namespaces as readonly string[]).includes(DATABASE); +}); + /** * The project stage operator. */ @@ -116,6 +121,22 @@ async function completer(params: AutocompleteParameters, line: string): Promise< const hits = filterShellAPI(params, SHELL_COMPLETIONS, elToComplete); return [hits.length ? hits : [], line]; } else if (firstLineEl.match(/\bdb\b/) && splitLine.length === 2) { + if (elToComplete.match(/aggregate\s*\(\s*\[\s*\{\s*/)) { + const splitQuery = line.split('{'); + const prefix = splitQuery.pop()?.trim() || ''; + const command: string = prefix ? line.split(prefix).shift() as string : line; + const suggestFirstStage = splitQuery.length <= 2; + + const expressions = suggestFirstStage + ? DB_AGGREGATE_COMPLETIONS + : ([] as AnyCompletions).concat( + BASE_COMPLETIONS, + getStageAccumulators(params, elToComplete) + ); + + const hits = filterQueries(params, expressions, prefix, command); + return [hits.length ? hits : [], line]; + } // We're seeing something like 'db.foo' and expand that to all methods on // db which start with 'foo' and all collections on the current db that // start with 'foo'. From aecb31659705c181405dd22dbf862c01010fd759 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 20 Feb 2023 12:40:15 +0100 Subject: [PATCH 2/5] Update packages/autocomplete/src/index.ts Co-authored-by: Anna Henningsen --- packages/autocomplete/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/autocomplete/src/index.ts b/packages/autocomplete/src/index.ts index c331c3ff1..de1eb29ae 100644 --- a/packages/autocomplete/src/index.ts +++ b/packages/autocomplete/src/index.ts @@ -51,7 +51,7 @@ export const MATCH_COMPLETIONS = ([] as AnyCompletions).concat( ); const DB_AGGREGATE_COMPLETIONS = STAGE_OPERATORS.filter(({ namespaces }) => { - return namespaces.length === 1 && (namespaces as readonly string[]).includes(DATABASE); + return namespaces.length === 1 && namespaces[0] === DATABASE; }); /** From 548cc4deed6fc44ef81f09a6ddd37fba6de9a0bd Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 20 Feb 2023 12:41:03 +0100 Subject: [PATCH 3/5] Update packages/autocomplete/src/index.ts Co-authored-by: Anna Henningsen --- packages/autocomplete/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/autocomplete/src/index.ts b/packages/autocomplete/src/index.ts index de1eb29ae..bb9d72586 100644 --- a/packages/autocomplete/src/index.ts +++ b/packages/autocomplete/src/index.ts @@ -129,10 +129,10 @@ async function completer(params: AutocompleteParameters, line: string): Promise< const expressions = suggestFirstStage ? DB_AGGREGATE_COMPLETIONS - : ([] as AnyCompletions).concat( - BASE_COMPLETIONS, - getStageAccumulators(params, elToComplete) - ); + : [ + ...BASE_COMPLETIONS, + ...getStageAccumulators(params, elToComplete) + ]; const hits = filterQueries(params, expressions, prefix, command); return [hits.length ? hits : [], line]; From 371a8489d50aea94c4da1a9639ec9c58a3adf922 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 20 Feb 2023 14:55:47 +0100 Subject: [PATCH 4/5] addresses PR feedback - for the first stage suggestions we explicitly provide stages that are first stage only --- packages/autocomplete/package-lock.json | 14 +++++++------- packages/autocomplete/package.json | 2 +- packages/autocomplete/src/index.ts | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/autocomplete/package-lock.json b/packages/autocomplete/package-lock.json index 550e1c746..26a937234 100644 --- a/packages/autocomplete/package-lock.json +++ b/packages/autocomplete/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev.0", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/mongodb-constants": "^0.2.1", + "@mongodb-js/mongodb-constants": "^0.2.2", "semver": "^7.3.2" }, "devDependencies": { @@ -20,9 +20,9 @@ } }, "node_modules/@mongodb-js/mongodb-constants": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.2.1.tgz", - "integrity": "sha512-arCTQYvK9scQszbQQcPba7Ru5HYrc8YINcGcM4HKMCcg+8A17U+3Y3AEI7N/oe2VE+1GFsrh8MoKBch4jEvY1g==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.2.2.tgz", + "integrity": "sha512-vm1G+/WRWmXGyE9ZnhDv9toe+LRu1x0F/lGEwqWESfBiUUUuVZhj25fS2o4IL7H4pJ31sFxr7/gu+ER8OkmtzA==" }, "node_modules/ansi-colors": { "version": "3.2.3", @@ -1499,9 +1499,9 @@ }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.2.1.tgz", - "integrity": "sha512-arCTQYvK9scQszbQQcPba7Ru5HYrc8YINcGcM4HKMCcg+8A17U+3Y3AEI7N/oe2VE+1GFsrh8MoKBch4jEvY1g==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.2.2.tgz", + "integrity": "sha512-vm1G+/WRWmXGyE9ZnhDv9toe+LRu1x0F/lGEwqWESfBiUUUuVZhj25fS2o4IL7H4pJ31sFxr7/gu+ER8OkmtzA==" }, "ansi-colors": { "version": "3.2.3", diff --git a/packages/autocomplete/package.json b/packages/autocomplete/package.json index 868c2496a..b08503bb2 100644 --- a/packages/autocomplete/package.json +++ b/packages/autocomplete/package.json @@ -31,7 +31,7 @@ "mocha": "^7.1.2" }, "dependencies": { - "@mongodb-js/mongodb-constants": "^0.2.1", + "@mongodb-js/mongodb-constants": "^0.2.2", "@mongosh/shell-api": "0.0.0-dev.0", "semver": "^7.3.2" } diff --git a/packages/autocomplete/src/index.ts b/packages/autocomplete/src/index.ts index bb9d72586..18192cfb8 100644 --- a/packages/autocomplete/src/index.ts +++ b/packages/autocomplete/src/index.ts @@ -128,7 +128,7 @@ async function completer(params: AutocompleteParameters, line: string): Promise< const suggestFirstStage = splitQuery.length <= 2; const expressions = suggestFirstStage - ? DB_AGGREGATE_COMPLETIONS + ? DB_AGGREGATE_COMPLETIONS.filter(({ firstStage }) => firstStage) : [ ...BASE_COMPLETIONS, ...getStageAccumulators(params, elToComplete) @@ -175,10 +175,10 @@ async function completer(params: AutocompleteParameters, line: string): Promise< let expressions; if (splitLine[2].match(/\baggregate\b/)) { // aggregation needs extra accumulators to autocomplete properly - expressions = ([] as AnyCompletions).concat( - BASE_COMPLETIONS, - getStageAccumulators(params, elToComplete) - ); + expressions = [ + ...BASE_COMPLETIONS, + ...getStageAccumulators(params, elToComplete) + ]; } else { // collection querying just needs MATCH COMPLETIONS expressions = MATCH_COMPLETIONS; From fd1d10b06b9d81114dd5f5a2ed4dcb21db3ef3fb Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 20 Feb 2023 15:09:00 +0100 Subject: [PATCH 5/5] documents some info about a few variables --- packages/autocomplete/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/autocomplete/src/index.ts b/packages/autocomplete/src/index.ts index 18192cfb8..037d035c2 100644 --- a/packages/autocomplete/src/index.ts +++ b/packages/autocomplete/src/index.ts @@ -50,6 +50,8 @@ export const MATCH_COMPLETIONS = ([] as AnyCompletions).concat( BSON_TYPES ); +// Note: The following list is not a list of all the completions +// for `db.aggregate` but only for the first stage of `db.aggregate`. const DB_AGGREGATE_COMPLETIONS = STAGE_OPERATORS.filter(({ namespaces }) => { return namespaces.length === 1 && namespaces[0] === DATABASE; }); @@ -128,7 +130,8 @@ async function completer(params: AutocompleteParameters, line: string): Promise< const suggestFirstStage = splitQuery.length <= 2; const expressions = suggestFirstStage - ? DB_AGGREGATE_COMPLETIONS.filter(({ firstStage }) => firstStage) + // First stage in `db.aggregate` form can only be 'db' namespaced stages + ? DB_AGGREGATE_COMPLETIONS : [ ...BASE_COMPLETIONS, ...getStageAccumulators(params, elToComplete)