Skip to content
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

fix(NODE-6029): update types for collection listing indexes #4072

Merged
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 @@ -693,8 +694,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>;
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
async indexInformation(
options?: IndexInformationOptions
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]> {
return await this.indexes({
...options,
full: options?.full ?? false
});
}

/**
Expand Down Expand Up @@ -798,19 +814,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 @@ -482,7 +484,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);
Loading