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
45 changes: 35 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 @@ -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;

baileympearson marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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.
Expand Down Expand Up @@ -693,8 +701,21 @@ 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 & { full: boolean }
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexInformation(): Promise<IndexDescriptionCompact>;
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
async indexInformation(options?: IndexInformationOptions) {
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
return await this.indexes({
...options,
full: options?.full ?? false
});
}

/**
Expand Down Expand Up @@ -798,19 +819,23 @@ 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 & { full: boolean }
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexes(): Promise<IndexDescriptionInfo[]>;
async indexes(options?: IndexInformationOptions) {
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
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
21 changes: 19 additions & 2 deletions 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,8 +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> {
return await this.collection(name).indexInformation(resolveOptions(this, options));
indexInformation(
name: string,
options: IndexInformationOptions & { full: true }
): Promise<IndexDescriptionInfo[]>;
indexInformation(
name: string,
options: IndexInformationOptions & { full?: false }
): Promise<IndexDescriptionCompact>;
indexInformation(
name: string,
options: IndexInformationOptions & { full: boolean }
): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;
indexInformation(name: string): Promise<IndexDescriptionCompact>;
async indexInformation(name: string, options?: IndexInformationOptions) {
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
return await this.collection(name).indexInformation(
resolveOptions(this, options as IndexInformationOptions & { full: boolean })
);
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -471,6 +476,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
85 changes: 79 additions & 6 deletions test/types/indexes_test-d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,89 @@
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);
}

// 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);
Loading