Skip to content

Commit

Permalink
Find return type (#11)
Browse files Browse the repository at this point in the history
* feat: more accurate return types

* feat: more accurate return types

* feat: more accurate return types

* feat: more accurate return types

* lint: format

* lint: format

* fix: for null type

* fix: for null type

* Revert "fix: for null type"

This reverts commit aa689c6.

* Revert "fix: for null type"

This reverts commit 0e0feb5.

* lint: format code

---------

Co-authored-by: Yuuki-Sakura <admin@zy.ci>
  • Loading branch information
RubenGeo and Yuuki-Sakura authored Nov 18, 2024
1 parent b60d546 commit 5b6a174
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 60 deletions.
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

0 comments on commit 5b6a174

Please sign in to comment.