From bada1ecc7ac912873d53c34d54bd403aaf7aa727 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Mon, 8 Apr 2024 20:55:07 +0300 Subject: [PATCH 1/7] fix(NODE-6029): update types for collection listing collection indexes --- src/collection.ts | 37 +++++++++++++------- src/db.ts | 5 ++- src/index.ts | 9 ++++- src/operations/indexes.ts | 16 +++++++-- test/types/indexes_test-d.ts | 65 ++++++++++++++++++++++++++++++++---- 5 files changed, 110 insertions(+), 22 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 8fcdf90bff9..63dbd1acdea 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -52,7 +52,8 @@ import type { CreateIndexesOptions, DropIndexesOptions, IndexDescription, - IndexDirection, + IndexDescriptionCompact, + IndexDescriptionInfo, IndexInformationOptions, IndexSpecification, ListIndexesOptions @@ -121,6 +122,13 @@ export interface CollectionPrivate { writeConcern?: WriteConcern; } +/** @public */ +export type IndexesInformation = TFull extends true + ? IndexDescriptionInfo[] + : TFull extends false + ? IndexDescriptionCompact + : IndexDescriptionInfo[] | IndexDescriptionCompact; + /** * The **Collection** class is an internal class that embodies a MongoDB collection * allowing for insert/find/update/delete and other command operation on that MongoDB collection. @@ -693,8 +701,13 @@ export class Collection { * * @param options - Optional settings for the command */ - async indexInformation(options?: IndexInformationOptions): Promise { - return await this.indexes({ ...options, full: options?.full ?? false }); + async indexInformation( + options?: IndexInformationOptions + ): Promise> { + return (await this.indexes({ + ...options, + full: options?.full ?? false + })) as IndexesInformation; } /** @@ -798,20 +811,20 @@ export class Collection { * * @param options - Optional settings for the command */ - async indexes(options?: IndexInformationOptions): Promise { - const indexes = await this.listIndexes(options).toArray(); + async indexes( + options?: IndexInformationOptions + ): Promise> { + const indexes: IndexDescriptionInfo[] = await this.listIndexes(options).toArray(); const full = options?.full ?? true; if (full) { - return indexes; + return indexes as IndexesInformation; } - const object: Record< - string, - Array<[name: string, direction: IndexDirection]> - > = Object.fromEntries(indexes.map(({ name, key }) => [name, Object.entries(key)])); + const object: IndexDescriptionCompact = Object.fromEntries( + indexes.map(({ name, key }) => [name, Object.entries(key)]) + ); - // @ts-expect-error TODO(NODE-6029): fix return type of `indexes()` and `indexInformation()` - return object; + return object as IndexesInformation; } /** diff --git a/src/db.ts b/src/db.ts index 1dda4834c2e..92f2657b2e7 100644 --- a/src/db.ts +++ b/src/db.ts @@ -482,7 +482,10 @@ export class Db { * @param name - The name of the collection. * @param options - Optional settings for the command */ - async indexInformation(name: string, options?: IndexInformationOptions): Promise { + async indexInformation( + name: string, + options?: IndexInformationOptions + ) { return await this.collection(name).indexInformation(resolveOptions(this, options)); } diff --git a/src/index.ts b/src/index.ts index 795f6835c8c..034fb6904e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -291,7 +291,12 @@ export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_ export type { CompressorName } from './cmap/wire_protocol/compression'; export type { JSTypeOf, OnDemandDocument } from './cmap/wire_protocol/on_demand/document'; export type { MongoDBResponse, MongoDBResponseConstructor } from './cmap/wire_protocol/responses'; -export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection'; +export type { + CollectionOptions, + CollectionPrivate, + IndexesInformation, + ModifyResult +} from './collection'; export type { COMMAND_FAILED, COMMAND_STARTED, @@ -471,6 +476,8 @@ export type { CreateIndexesOptions, DropIndexesOptions, IndexDescription, + IndexDescriptionCompact, + IndexDescriptionInfo, IndexDirection, IndexSpecification, ListIndexesOptions diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index e32ccfae182..dca6458f3b8 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -72,7 +72,7 @@ export type IndexSpecification = OneOrMore< >; /** @public */ -export interface IndexInformationOptions extends ListIndexesOptions { +export interface IndexInformationOptions extends ListIndexesOptions { /** * When `true`, an array of index descriptions is returned. * When `false`, the driver returns an object that with keys corresponding to index names with values @@ -91,7 +91,7 @@ export interface IndexInformationOptions extends ListIndexesOptions { * } * ``` */ - full?: boolean; + full?: TFull; } /** @public */ @@ -214,6 +214,18 @@ function resolveIndexDescription( ); } +/** + * @public + * The index information returned by the listIndexes command. https://www.mongodb.com/docs/manual/reference/command/listIndexes/#mongodb-dbcommand-dbcmd.listIndexes + */ +export type IndexDescriptionInfo = Omit & { + key: { [key: string]: IndexDirection }; + v?: IndexDescription['version']; +}; + +/** @public */ +export type IndexDescriptionCompact = Record; + /** * @internal * diff --git a/test/types/indexes_test-d.ts b/test/types/indexes_test-d.ts index 7cca9d71062..7a12a7d76d4 100644 --- a/test/types/indexes_test-d.ts +++ b/test/types/indexes_test-d.ts @@ -1,16 +1,69 @@ -import { expectType } from 'tsd'; +import { expectAssignable, expectType } from 'tsd'; -import { type Document, MongoClient } from '../../src'; +import { MongoClient } from '../../src'; +import { type IndexDescriptionCompact, type IndexDescriptionInfo } from '../mongodb'; const client = new MongoClient(''); const db = client.db('test'); const collection = db.collection('test.find'); -// Promise variant testing -expectType>(collection.indexes()); -expectType>(collection.indexes({})); +const exampleFullIndexes: IndexDescriptionInfo[] = [ + { v: 2, key: { _id: 1 }, name: '_id_' }, + { v: 2, key: { listingName: 'hashed' }, name: 'listingName_hashed' }, + { + v: 2, + key: { 'auctionDetails.listingId': 1 }, + name: 'auctionDetails_listingId_1', + unique: true + } +]; +const exampleCompactIndexes: IndexDescriptionCompact = { + _id_: [['_id', 1]], + listingName_hashed: [['listingName', 'hashed']], + auctionDetails_listingId_1: [['auctionDetails.listingId', 1]] +}; + +const ambiguousFullness = Math.random() > 0.5; + +// test Collection.prototype.indexes + +const defaultIndexes = await collection.indexes(); +const emptyOptionsIndexes = await collection.indexes({}); +const fullIndexes = await collection.indexes({ full: true }); +const notFullIndexes = await collection.indexes({ full: false }); +const ambiguousIndexes = await collection.indexes({ full: ambiguousFullness }); + +expectAssignable(exampleFullIndexes); +expectAssignable(exampleFullIndexes); +expectAssignable(exampleCompactIndexes); +expectAssignable(exampleCompactIndexes); + +expectType(defaultIndexes); +expectType(emptyOptionsIndexes); +expectType(fullIndexes); +expectType(notFullIndexes); +expectType(ambiguousIndexes); + +// test Collection.prototype.indexInformation + +const defaultIndexInfo = await collection.indexInformation(); +const emptyOptionsIndexInfo = await collection.indexInformation({}); +const fullIndexInfo = await collection.indexInformation({ full: true }); +const notFullIndexInfo = await collection.indexInformation({ full: false }); +const ambiguousIndexInfo = await collection.indexInformation({ full: ambiguousFullness }); + +expectAssignable(exampleFullIndexes); +expectAssignable(exampleFullIndexes); +expectAssignable(exampleCompactIndexes); +expectAssignable(exampleCompactIndexes); + +expectType(defaultIndexInfo); +expectType(emptyOptionsIndexInfo); +expectType(fullIndexInfo); +expectType(notFullIndexInfo); +expectType(ambiguousIndexInfo); // Explicit check for iterable result for (const index of await collection.indexes()) { - expectType(index); + expectType(index); } From e8f4ab190194785e56a634031737295d269e0a35 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Tue, 9 Apr 2024 22:44:48 +0300 Subject: [PATCH 2/7] fix(NODE-6029): fix review comments --- src/collection.ts | 32 ++++++++++++++++++++++---------- src/db.ts | 22 ++++++++++++++++++---- src/operations/indexes.ts | 6 +++--- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 63dbd1acdea..01b4a23f743 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -701,13 +701,21 @@ export class Collection { * * @param options - Optional settings for the command */ - async indexInformation( - options?: IndexInformationOptions - ): Promise> { - return (await this.indexes({ + indexInformation( + options: IndexInformationOptions & { full: true } + ): Promise; + indexInformation( + options: IndexInformationOptions & { full?: false } + ): Promise; + indexInformation( + options: IndexInformationOptions & { full: boolean } + ): Promise; + indexInformation(): Promise; + async indexInformation(options?: IndexInformationOptions) { + return await this.indexes({ ...options, full: options?.full ?? false - })) as IndexesInformation; + }); } /** @@ -811,20 +819,24 @@ export class Collection { * * @param options - Optional settings for the command */ - async indexes( - options?: IndexInformationOptions - ): Promise> { + indexes(options: IndexInformationOptions & { full?: true }): Promise; + indexes(options: IndexInformationOptions & { full: false }): Promise; + indexes( + options: IndexInformationOptions & { full: boolean } + ): Promise; + indexes(): Promise; + async indexes(options?: IndexInformationOptions) { const indexes: IndexDescriptionInfo[] = await this.listIndexes(options).toArray(); const full = options?.full ?? true; if (full) { - return indexes as IndexesInformation; + return indexes; } const object: IndexDescriptionCompact = Object.fromEntries( indexes.map(({ name, key }) => [name, Object.entries(key)]) ); - return object as IndexesInformation; + return object; } /** diff --git a/src/db.ts b/src/db.ts index 92f2657b2e7..46d2143d3d9 100644 --- a/src/db.ts +++ b/src/db.ts @@ -25,6 +25,8 @@ import { executeOperation } from './operations/execute_operation'; import { CreateIndexesOperation, type CreateIndexesOptions, + type IndexDescriptionCompact, + type IndexDescriptionInfo, type IndexInformationOptions, type IndexSpecification } from './operations/indexes'; @@ -482,11 +484,23 @@ export class Db { * @param name - The name of the collection. * @param options - Optional settings for the command */ - async indexInformation( + indexInformation( name: string, - options?: IndexInformationOptions - ) { - return await this.collection(name).indexInformation(resolveOptions(this, options)); + options: IndexInformationOptions & { full: true } + ): Promise; + indexInformation( + name: string, + options: IndexInformationOptions & { full?: false } + ): Promise; + indexInformation( + name: string, + options: IndexInformationOptions & { full: boolean } + ): Promise; + indexInformation(name: string): Promise; + async indexInformation(name: string, options?: IndexInformationOptions) { + return await this.collection(name).indexInformation( + resolveOptions(this, options as IndexInformationOptions & { full: boolean }) + ); } /** diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index dca6458f3b8..8bf390f28b7 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -72,7 +72,7 @@ export type IndexSpecification = OneOrMore< >; /** @public */ -export interface IndexInformationOptions extends ListIndexesOptions { +export interface IndexInformationOptions extends ListIndexesOptions { /** * When `true`, an array of index descriptions is returned. * When `false`, the driver returns an object that with keys corresponding to index names with values @@ -91,7 +91,7 @@ export interface IndexInformationOptions extends ListInde * } * ``` */ - full?: TFull; + full?: boolean; } /** @public */ @@ -221,7 +221,7 @@ function resolveIndexDescription( export type IndexDescriptionInfo = Omit & { key: { [key: string]: IndexDirection }; v?: IndexDescription['version']; -}; +} & Document; /** @public */ export type IndexDescriptionCompact = Record; From 1ba6e8d06327b8298f962f8fee2beadaffafe21f Mon Sep 17 00:00:00 2001 From: prenaissance Date: Tue, 9 Apr 2024 22:48:33 +0300 Subject: [PATCH 3/7] test(NODE-6029): add type tests for Db.prototype.indexInformation --- test/types/indexes_test-d.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/types/indexes_test-d.ts b/test/types/indexes_test-d.ts index 7a12a7d76d4..2d806e0bb4b 100644 --- a/test/types/indexes_test-d.ts +++ b/test/types/indexes_test-d.ts @@ -67,3 +67,21 @@ expectType(ambiguousIndexInfo) for (const index of await collection.indexes()) { expectType(index); } + +const dbDefaultIndexInfo = await db.indexInformation('some-collection'); +const dbEmptyOptionsIndexInfo = await db.indexInformation('some-collection', {}); +const dbFullIndexInfo = await db.indexInformation('some-collection', { full: true }); +const dbNotFullIndexInfo = await db.indexInformation('some-collection', { full: false }); +const dbAmbiguousIndexInfo = await db.indexInformation('some-collection', { + full: ambiguousFullness +}); + +expectAssignable(exampleFullIndexes); +expectAssignable(exampleFullIndexes); +expectAssignable(exampleCompactIndexes); + +expectType(dbDefaultIndexInfo); +expectType(dbEmptyOptionsIndexInfo); +expectType(dbFullIndexInfo); +expectType(dbNotFullIndexInfo); +expectType(dbAmbiguousIndexInfo); From d859a6a08a26ad3a787178b728375f91d9968293 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Tue, 9 Apr 2024 22:51:35 +0300 Subject: [PATCH 4/7] test(NODE-6029): add comment --- test/types/indexes_test-d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/types/indexes_test-d.ts b/test/types/indexes_test-d.ts index 2d806e0bb4b..b2e7d8479ae 100644 --- a/test/types/indexes_test-d.ts +++ b/test/types/indexes_test-d.ts @@ -68,6 +68,8 @@ for (const index of await collection.indexes()) { expectType(index); } +// test Db.prototype.indexInformation + const dbDefaultIndexInfo = await db.indexInformation('some-collection'); const dbEmptyOptionsIndexInfo = await db.indexInformation('some-collection', {}); const dbFullIndexInfo = await db.indexInformation('some-collection', { full: true }); From 1f18df233aa10566ecbc4b4bf4b604898c896660 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Wed, 10 Apr 2024 00:32:44 +0300 Subject: [PATCH 5/7] fix(NODE-6029): remove unused type --- src/collection.ts | 7 ------- src/index.ts | 7 +------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 01b4a23f743..7321b1f8fb6 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -122,13 +122,6 @@ export interface CollectionPrivate { writeConcern?: WriteConcern; } -/** @public */ -export type IndexesInformation = TFull extends true - ? IndexDescriptionInfo[] - : TFull extends false - ? IndexDescriptionCompact - : IndexDescriptionInfo[] | IndexDescriptionCompact; - /** * The **Collection** class is an internal class that embodies a MongoDB collection * allowing for insert/find/update/delete and other command operation on that MongoDB collection. diff --git a/src/index.ts b/src/index.ts index 034fb6904e6..f9188d5952c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -291,12 +291,7 @@ export type { StreamDescription, StreamDescriptionOptions } from './cmap/stream_ export type { CompressorName } from './cmap/wire_protocol/compression'; export type { JSTypeOf, OnDemandDocument } from './cmap/wire_protocol/on_demand/document'; export type { MongoDBResponse, MongoDBResponseConstructor } from './cmap/wire_protocol/responses'; -export type { - CollectionOptions, - CollectionPrivate, - IndexesInformation, - ModifyResult -} from './collection'; +export type { CollectionOptions, CollectionPrivate, ModifyResult } from './collection'; export type { COMMAND_FAILED, COMMAND_STARTED, From cce822eadb609e7600418242484c4e5741c59e44 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Wed, 10 Apr 2024 00:37:24 +0300 Subject: [PATCH 6/7] style(NODE-6029): explicitly declare return types --- src/collection.ts | 8 ++++++-- src/db.ts | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 7321b1f8fb6..4c7c3b89b0c 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -704,7 +704,9 @@ export class Collection { options: IndexInformationOptions & { full: boolean } ): Promise; indexInformation(): Promise; - async indexInformation(options?: IndexInformationOptions) { + async indexInformation( + options?: IndexInformationOptions + ): Promise { return await this.indexes({ ...options, full: options?.full ?? false @@ -818,7 +820,9 @@ export class Collection { options: IndexInformationOptions & { full: boolean } ): Promise; indexes(): Promise; - async indexes(options?: IndexInformationOptions) { + async indexes( + options?: IndexInformationOptions + ): Promise { const indexes: IndexDescriptionInfo[] = await this.listIndexes(options).toArray(); const full = options?.full ?? true; if (full) { diff --git a/src/db.ts b/src/db.ts index 46d2143d3d9..9c2648cda34 100644 --- a/src/db.ts +++ b/src/db.ts @@ -497,7 +497,10 @@ export class Db { options: IndexInformationOptions & { full: boolean } ): Promise; indexInformation(name: string): Promise; - async indexInformation(name: string, options?: IndexInformationOptions) { + async indexInformation( + name: string, + options?: IndexInformationOptions + ): Promise { return await this.collection(name).indexInformation( resolveOptions(this, options as IndexInformationOptions & { full: boolean }) ); From 6f8450dbf2073b9abeb7053ef6bb602d7c4fb438 Mon Sep 17 00:00:00 2001 From: prenaissance Date: Wed, 10 Apr 2024 18:45:02 +0300 Subject: [PATCH 7/7] fix(NODE-6029): fix non-literal index options return type --- src/collection.ts | 6 +++--- src/db.ts | 6 ++---- test/types/indexes_test-d.ts | 12 +++++++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 4c7c3b89b0c..4d021ecc01a 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -701,7 +701,7 @@ export class Collection { options: IndexInformationOptions & { full?: false } ): Promise; indexInformation( - options: IndexInformationOptions & { full: boolean } + options: IndexInformationOptions ): Promise; indexInformation(): Promise; async indexInformation( @@ -817,9 +817,9 @@ export class Collection { indexes(options: IndexInformationOptions & { full?: true }): Promise; indexes(options: IndexInformationOptions & { full: false }): Promise; indexes( - options: IndexInformationOptions & { full: boolean } + options: IndexInformationOptions ): Promise; - indexes(): Promise; + indexes(options?: ListIndexesOptions): Promise; async indexes( options?: IndexInformationOptions ): Promise { diff --git a/src/db.ts b/src/db.ts index 9c2648cda34..abb068f5db6 100644 --- a/src/db.ts +++ b/src/db.ts @@ -494,16 +494,14 @@ export class Db { ): Promise; indexInformation( name: string, - options: IndexInformationOptions & { full: boolean } + options: IndexInformationOptions ): Promise; indexInformation(name: string): Promise; async indexInformation( name: string, options?: IndexInformationOptions ): Promise { - return await this.collection(name).indexInformation( - resolveOptions(this, options as IndexInformationOptions & { full: boolean }) - ); + return await this.collection(name).indexInformation(resolveOptions(this, options)); } /** diff --git a/test/types/indexes_test-d.ts b/test/types/indexes_test-d.ts index b2e7d8479ae..8d68a34f30f 100644 --- a/test/types/indexes_test-d.ts +++ b/test/types/indexes_test-d.ts @@ -1,6 +1,6 @@ import { expectAssignable, expectType } from 'tsd'; -import { MongoClient } from '../../src'; +import { type IndexInformationOptions, MongoClient } from '../../src'; import { type IndexDescriptionCompact, type IndexDescriptionInfo } from '../mongodb'; const client = new MongoClient(''); @@ -87,3 +87,13 @@ expectType(dbEmptyOptionsIndexInfo); expectType(dbFullIndexInfo); expectType(dbNotFullIndexInfo); expectType(dbAmbiguousIndexInfo); + +// test indexInformation with non-literal options +const options: IndexInformationOptions = {}; +const indexInfo = await db.collection('some-collection').indexInformation(options); +const indexes = await db.collection('some-collection').indexes(options); +const indexDbInfo = await db.indexInformation('some-collection', options); + +expectType(indexInfo); +expectType(indexes); +expectType(indexDbInfo);