-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(NODE-6029): update types for collection listing indexes #4072
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
bada1ec
e8f4ab1
1ba6e8d
d859a6a
1f18df2
cce822e
6f8450d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 boolean = boolean> = 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<TSchema extends Document = Document> { | |
* | ||
* @param options - Optional settings for the command | ||
*/ | ||
async indexInformation(options?: IndexInformationOptions): Promise<Document> { | ||
return await this.indexes({ ...options, full: options?.full ?? false }); | ||
async indexInformation<TFull extends boolean = false>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's preferrable to avoid computed types as the return types from our APIs, since they don't provided users with information about the return type of a function until a user 1) reads the source of the computed type or 2) writes code using the API and checks the computed return type. Could we accomplish the same here using overloads and removing all indexInformation(
options: IndexInformationOptions & { full: true }
): Promise<IndexDescriptionInfo[]>;
indexInformation(
options: IndexInformationOptions & { full: false }
): Promise<IndexDescriptionCompact>;
indexInformation(
options: IndexInformationOptions & { full: boolean }
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>; This has the additional benefits of
for example - here's what VSCode could hypothetically display if we used overloads: ![]() ![]() |
||
options?: IndexInformationOptions<TFull> | ||
): Promise<IndexesInformation<TFull>> { | ||
return (await this.indexes({ | ||
...options, | ||
full: options?.full ?? false | ||
})) as IndexesInformation<TFull>; | ||
} | ||
|
||
/** | ||
|
@@ -798,20 +811,20 @@ export class Collection<TSchema extends Document = Document> { | |
* | ||
* @param options - Optional settings for the command | ||
*/ | ||
async indexes(options?: IndexInformationOptions): Promise<Document[]> { | ||
const indexes = await this.listIndexes(options).toArray(); | ||
async indexes<TFull extends boolean = true>( | ||
options?: IndexInformationOptions<TFull> | ||
): Promise<IndexesInformation<TFull>> { | ||
const indexes: IndexDescriptionInfo[] = await this.listIndexes(options).toArray(); | ||
const full = options?.full ?? true; | ||
if (full) { | ||
return indexes; | ||
return indexes as IndexesInformation<TFull>; | ||
} | ||
|
||
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<TFull>; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,7 +72,7 @@ export type IndexSpecification = OneOrMore< | |
>; | ||
|
||
/** @public */ | ||
export interface IndexInformationOptions extends ListIndexesOptions { | ||
export interface IndexInformationOptions<TFull extends boolean> extends ListIndexesOptions { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could consider this a breaking change, since existing users' code that makes use of |
||
/** | ||
* 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<IndexDescription, 'key' | 'version'> & { | ||
key: { [key: string]: IndexDirection }; | ||
v?: IndexDescription['version']; | ||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We generally try to keep our type definitions for server responses flexible so that if the server modifies a response in a future server version the driver is still compatible with it. Could we |
||
/** @public */ | ||
export type IndexDescriptionCompact = Record<string, [name: string, direction: IndexDirection][]>; | ||
|
||
/** | ||
* @internal | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Promise<Document[]>>(collection.indexes()); | ||
expectType<Promise<Document[]>>(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<typeof fullIndexes>(exampleFullIndexes); | ||
expectAssignable<typeof ambiguousIndexes>(exampleFullIndexes); | ||
expectAssignable<typeof ambiguousIndexes>(exampleCompactIndexes); | ||
expectAssignable<typeof notFullIndexes>(exampleCompactIndexes); | ||
|
||
expectType<IndexDescriptionInfo[]>(defaultIndexes); | ||
expectType<IndexDescriptionInfo[]>(emptyOptionsIndexes); | ||
expectType<IndexDescriptionInfo[]>(fullIndexes); | ||
expectType<IndexDescriptionCompact>(notFullIndexes); | ||
expectType<IndexDescriptionInfo[] | IndexDescriptionCompact>(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<typeof fullIndexInfo>(exampleFullIndexes); | ||
expectAssignable<typeof ambiguousIndexInfo>(exampleFullIndexes); | ||
expectAssignable<typeof ambiguousIndexInfo>(exampleCompactIndexes); | ||
expectAssignable<typeof notFullIndexInfo>(exampleCompactIndexes); | ||
|
||
expectType<IndexDescriptionCompact>(defaultIndexInfo); | ||
expectType<IndexDescriptionCompact>(emptyOptionsIndexInfo); | ||
expectType<IndexDescriptionInfo[]>(fullIndexInfo); | ||
expectType<IndexDescriptionCompact>(notFullIndexInfo); | ||
expectType<IndexDescriptionInfo[] | IndexDescriptionCompact>(ambiguousIndexInfo); | ||
|
||
// Explicit check for iterable result | ||
for (const index of await collection.indexes()) { | ||
expectType<Document>(index); | ||
expectType<IndexDescriptionInfo>(index); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.