Skip to content

Commit

Permalink
fix(NODE-6029): update types for collection listing indexes (#4072)
Browse files Browse the repository at this point in the history
  • Loading branch information
prenaissance committed Apr 11, 2024
1 parent 67d7bab commit 232bf3c
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 17 deletions.
42 changes: 32 additions & 10 deletions src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ import type {
CreateIndexesOptions,
DropIndexesOptions,
IndexDescription,
IndexDirection,
IndexDescriptionCompact,
IndexDescriptionInfo,
IndexInformationOptions,
IndexSpecification,
ListIndexesOptions
Expand Down Expand Up @@ -695,8 +696,23 @@ 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 });
indexInformation(
options: IndexInformationOptions & { full: true }
): Promise<IndexDescriptionInfo[]>;
indexInformation(
options: IndexInformationOptions & { full?: false }
): Promise<IndexDescriptionCompact>;
indexInformation(
options: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexInformation(): Promise<IndexDescriptionCompact>;
async indexInformation(
options?: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]> {
return await this.indexes({
...options,
full: options?.full ?? false
});
}

/**
Expand Down Expand Up @@ -800,19 +816,25 @@ 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();
indexes(options: IndexInformationOptions & { full?: true }): Promise<IndexDescriptionInfo[]>;
indexes(options: IndexInformationOptions & { full: false }): Promise<IndexDescriptionCompact>;
indexes(
options: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexes(options?: ListIndexesOptions): Promise<IndexDescriptionInfo[]>;
async indexes(
options?: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]> {
const indexes: IndexDescriptionInfo[] = await this.listIndexes(options).toArray();
const full = options?.full ?? true;
if (full) {
return indexes;
}

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;
}

Expand Down
20 changes: 19 additions & 1 deletion src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -485,7 +487,23 @@ export class Db {
* @param name - The name of the collection.
* @param options - Optional settings for the command
*/
async indexInformation(name: string, options?: IndexInformationOptions): Promise<Document> {
indexInformation(
name: string,
options: IndexInformationOptions & { full: true }
): Promise<IndexDescriptionInfo[]>;
indexInformation(
name: string,
options: IndexInformationOptions & { full?: false }
): Promise<IndexDescriptionCompact>;
indexInformation(
name: string,
options: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexInformation(name: string): Promise<IndexDescriptionCompact>;
async indexInformation(
name: string,
options?: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]> {
return await this.collection(name).indexInformation(resolveOptions(this, options));
}

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ export type {
CreateIndexesOptions,
DropIndexesOptions,
IndexDescription,
IndexDescriptionCompact,
IndexDescriptionInfo,
IndexDirection,
IndexSpecification,
ListIndexesOptions
Expand Down
12 changes: 12 additions & 0 deletions src/operations/indexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
} & Document;

/** @public */
export type IndexDescriptionCompact = Record<string, [name: string, direction: IndexDirection][]>;

/**
* @internal
*
Expand Down
95 changes: 89 additions & 6 deletions test/types/indexes_test-d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,99 @@
import { expectType } from 'tsd';
import { expectAssignable, expectType } from 'tsd';

import { type Document, MongoClient } from '../../src';
import { type IndexInformationOptions, 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);
}

// 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 });
const dbNotFullIndexInfo = await db.indexInformation('some-collection', { full: false });
const dbAmbiguousIndexInfo = await db.indexInformation('some-collection', {
full: ambiguousFullness
});

expectAssignable<typeof dbFullIndexInfo>(exampleFullIndexes);
expectAssignable<typeof dbAmbiguousIndexInfo>(exampleFullIndexes);
expectAssignable<typeof dbAmbiguousIndexInfo>(exampleCompactIndexes);

expectType<IndexDescriptionCompact>(dbDefaultIndexInfo);
expectType<IndexDescriptionCompact>(dbEmptyOptionsIndexInfo);
expectType<IndexDescriptionInfo[]>(dbFullIndexInfo);
expectType<IndexDescriptionCompact>(dbNotFullIndexInfo);
expectType<IndexDescriptionInfo[] | IndexDescriptionCompact>(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<IndexDescriptionCompact | IndexDescriptionInfo[]>(indexInfo);
expectType<IndexDescriptionCompact | IndexDescriptionInfo[]>(indexes);
expectType<IndexDescriptionCompact | IndexDescriptionInfo[]>(indexDbInfo);

0 comments on commit 232bf3c

Please sign in to comment.