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

Find return type #11

Merged
merged 11 commits into from
Nov 18, 2024
76 changes: 59 additions & 17 deletions src/entity-manager/EntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { UpsertOptions } from "../repository/UpsertOptions"
import { InstanceChecker } from "../util/InstanceChecker"
import { ObjectLiteral } from "../common/ObjectLiteral"
import { PickKeysByType } from "../common/PickKeysByType"
import { FindReturnType } from "../find-options/FindReturnType"

/**
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
Expand Down Expand Up @@ -69,7 +70,10 @@ export class EntityManager {
* Once created and then reused by repositories.
* Created as a future replacement for the #repositories to provide a bit more perf optimization.
*/
protected repositories = new Map<EntityTarget<any>, Repository<any>>()
protected repositories = new Map<
EntityTarget<any>,
Repository<any> | MongoRepository<any>
>()

/**
* Once created and then reused by repositories.
Expand Down Expand Up @@ -1091,18 +1095,25 @@ export class EntityManager {
/**
* Finds entities that match given find options.
*/
async find<Entity extends ObjectLiteral>(
async find<
Entity extends ObjectLiteral,
Options extends FindManyOptions<Entity>,
>(
entityClass: EntityTarget<Entity>,
options?: FindManyOptions<Entity>,
): Promise<Entity[]> {
options?: Options,
): Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>[]
> {
const metadata = this.connection.getMetadata(entityClass)
return this.createQueryBuilder<Entity>(
entityClass as any,
FindOptionsUtils.extractFindManyOptionsAlias(options) ||
metadata.name,
)
.setFindOptions(options || {})
.getMany()
.getMany() as Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>[]
>
}

/**
Expand All @@ -1126,18 +1137,35 @@ export class EntityManager {
* Also counts all entities that match given conditions,
* but ignores pagination settings (from and take options).
*/
findAndCount<Entity extends ObjectLiteral>(
findAndCount<
Entity extends ObjectLiteral,
Options extends FindManyOptions<Entity>,
>(
entityClass: EntityTarget<Entity>,
options?: FindManyOptions<Entity>,
): Promise<[Entity[], number]> {
options?: Options,
): Promise<
[
FindReturnType<Entity, Options["select"], Options["relations"]>[],
number,
]
> {
const metadata = this.connection.getMetadata(entityClass)
return this.createQueryBuilder<Entity>(
entityClass as any,
FindOptionsUtils.extractFindManyOptionsAlias(options) ||
metadata.name,
)
.setFindOptions(options || {})
.getManyAndCount()
.getManyAndCount() as Promise<
[
FindReturnType<
Entity,
Options["select"],
Options["relations"]
>[],
number,
]
>
}

/**
Expand Down Expand Up @@ -1188,10 +1216,17 @@ export class EntityManager {
* Finds first entity by a given find options.
* If entity was not found in the database - returns null.
*/
async findOne<Entity extends ObjectLiteral>(
async findOne<
Entity extends ObjectLiteral,
Options extends FindOneOptions<Entity>,
>(
entityClass: EntityTarget<Entity>,
options: FindOneOptions<Entity>,
): Promise<Entity | null> {
options: Options,
): Promise<FindReturnType<
Entity,
Options["select"],
Options["relations"]
> | null> {
const metadata = this.connection.getMetadata(entityClass)

// prepare alias for built query
Expand All @@ -1212,7 +1247,9 @@ export class EntityManager {
...options,
take: 1,
})
.getOne()
.getOne() as Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>
>
}

/**
Expand Down Expand Up @@ -1263,11 +1300,16 @@ export class EntityManager {
* Finds first entity by a given find options.
* If entity was not found in the database - rejects with error.
*/
async findOneOrFail<Entity extends ObjectLiteral>(
async findOneOrFail<
Entity extends ObjectLiteral,
Options extends FindOneOptions<Entity>,
>(
entityClass: EntityTarget<Entity>,
options: FindOneOptions<Entity>,
): Promise<Entity> {
return this.findOne<Entity>(entityClass as any, options).then(
options: Options,
): Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>
> {
return this.findOne<Entity, Options>(entityClass as any, options).then(
(value) => {
if (value === null) {
return Promise.reject(
Expand Down
128 changes: 103 additions & 25 deletions src/entity-manager/MongoEntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import {
} from "../find-options/FindOptionsSelect"
import { ObjectUtils } from "../util/ObjectUtils"
import { ColumnMetadata } from "../metadata/ColumnMetadata"
import { FindReturnType } from "../find-options/FindReturnType"
import { EntityNotFoundError } from "../error"

/**
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
Expand Down Expand Up @@ -98,13 +100,18 @@ export class MongoEntityManager extends EntityManager {
/**
* Finds entities that match given find options or conditions.
*/
async find<Entity>(
entityClassOrName: EntityTarget<Entity>,
optionsOrConditions?:
async find<
Entity extends ObjectLiteral,
Options extends
| FindManyOptions<Entity>
| Partial<Entity>
| FilterOperators<Entity>,
): Promise<Entity[]> {
>(
entityClassOrName: EntityTarget<Entity>,
optionsOrConditions?: Options,
): Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>[]
> {
const query =
this.convertFindManyOptionsOrConditionsToMongodbQuery(
optionsOrConditions,
Expand Down Expand Up @@ -136,29 +143,41 @@ export class MongoEntityManager extends EntityManager {
} else if (deleteDateColumn) {
this.filterSoftDeleted(cursor, deleteDateColumn, query)
}
return cursor.toArray()
return cursor.toArray() as Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>[]
>
}

/**
* Finds entities that match given find options or conditions.
* Also counts all entities that match given conditions,
* but ignores pagination settings (from and take options).
*/
async findAndCount<Entity>(
async findAndCount<
Entity extends ObjectLiteral,
Options extends MongoFindManyOptions<Entity>,
>(
entityClassOrName: EntityTarget<Entity>,
options?: MongoFindManyOptions<Entity>,
): Promise<[Entity[], number]> {
options?: Options,
): Promise<
[
FindReturnType<Entity, Options["select"], Options["relations"]>[],
number,
]
> {
return this.executeFindAndCount(entityClassOrName, options)
}

/**
* Finds entities that match given where conditions.
*/
async findAndCountBy<Entity>(
async findAndCountBy<Entity extends ObjectLiteral>(
entityClassOrName: EntityTarget<Entity>,
where: any,
): Promise<[Entity[], number]> {
return this.executeFindAndCount(entityClassOrName, where)
return this.executeFindAndCount(entityClassOrName, where) as Promise<
[Entity[], number]
>
}

/**
Expand Down Expand Up @@ -224,21 +243,31 @@ export class MongoEntityManager extends EntityManager {
/**
* Finds first entity that matches given conditions and/or find options.
*/
async findOne<Entity>(
async findOne<
Entity extends ObjectLiteral,
Options extends MongoFindOneOptions<Entity>,
>(
entityClassOrName: EntityTarget<Entity>,
options: MongoFindOneOptions<Entity>,
): Promise<Entity | null> {
options: Options,
): Promise<FindReturnType<
Entity,
Options["select"],
Options["relations"]
> | null> {
return this.executeFindOne(entityClassOrName, options)
}

/**
* Finds first entity that matches given WHERE conditions.
*/
async findOneBy<Entity>(
async findOneBy<Entity extends ObjectLiteral>(
entityClassOrName: EntityTarget<Entity>,
where: any,
): Promise<Entity | null> {
return this.executeFindOne(entityClassOrName, where)
return this.executeFindOne(
entityClassOrName,
where,
) as Promise<Entity | null>
}

/**
Expand All @@ -250,11 +279,39 @@ export class MongoEntityManager extends EntityManager {
* id: 1 // where "id" is your primary column name
* })
*/
async findOneById<Entity>(
async findOneById<Entity extends ObjectLiteral>(
entityClassOrName: EntityTarget<Entity>,
id: string | number | Date | ObjectId,
): Promise<Entity | null> {
return this.executeFindOne(entityClassOrName, id)
return this.executeFindOne(
entityClassOrName,
id,
) as Promise<Entity | null>
}

/**
* Finds first entity by a given find options.
* If entity was not found in the database - rejects with error.
*/
async findOneOrFail<
Entity extends ObjectLiteral,
Options extends MongoFindOneOptions<Entity>,
>(
entityClass: EntityTarget<Entity>,
options: Options,
): Promise<
FindReturnType<Entity, Options["select"], Options["relations"]>
> {
return this.findOne<Entity, Options>(entityClass as any, options).then(
(value) => {
if (value === null) {
return Promise.reject(
new EntityNotFoundError(entityClass, options),
)
}
return Promise.resolve(value)
},
)
}

/**
Expand Down Expand Up @@ -1077,11 +1134,18 @@ export class MongoEntityManager extends EntityManager {
/**
* Finds first entity that matches given conditions and/or find options.
*/
protected async executeFindOne<Entity>(
protected async executeFindOne<
Entity extends ObjectLiteral,
Options extends MongoFindOneOptions<Entity>,
>(
entityClassOrName: EntityTarget<Entity>,
optionsOrConditions?: any,
maybeOptions?: MongoFindOneOptions<Entity>,
): Promise<Entity | null> {
optionsOrConditions?: any | Options,
maybeOptions?: Options,
): Promise<FindReturnType<
Entity,
Options["select"],
Options["relations"]
> | null> {
const objectIdInstance = PlatformTools.load("mongodb").ObjectId
const id =
optionsOrConditions instanceof objectIdInstance ||
Expand Down Expand Up @@ -1124,7 +1188,13 @@ export class MongoEntityManager extends EntityManager {

// const result = await cursor.limit(1).next();
const result = await cursor.limit(1).toArray()
return result.length > 0 ? result[0] : null
return result.length > 0
? (result[0] as FindReturnType<
Entity,
Options["select"],
Options["relations"]
>)
: null
}

protected async executeFind<Entity>(
Expand Down Expand Up @@ -1169,10 +1239,18 @@ export class MongoEntityManager extends EntityManager {
/**
* Finds entities that match given find options or conditions.
*/
async executeFindAndCount<Entity>(
async executeFindAndCount<
Entity extends ObjectLiteral,
Options extends MongoFindManyOptions<Entity> | Partial<Entity>,
>(
entityClassOrName: EntityTarget<Entity>,
optionsOrConditions?: MongoFindManyOptions<Entity> | Partial<Entity>,
): Promise<[Entity[], number]> {
optionsOrConditions?: Options,
): Promise<
[
FindReturnType<Entity, Options["select"], Options["relations"]>[],
number,
]
> {
const query =
this.convertFindManyOptionsOrConditionsToMongodbQuery(
optionsOrConditions,
Expand Down
Loading
Loading