Мой метод возвращает объект счета со всем объектом пользователя. Я хотел бы, чтобы я возвращал только объект счета и пользователя с двумя атрибутами в сущности. Я использую TypeORM
/**
* Returns a bills by account bill
*/
async getByAccountBill(
accountBill: string,
id?: number
): Promise<Object | undefined> {
const userService = new UserService();
const user = await userService.getById(id);
const bills = await this.billRepository.find({
select: ["accountBill"],
where: {
accountBill: Like(`${accountBill}%`),
user: Not(`${user.id}`)
},
relations: ["user"] // I get All object Entity (userId, password, login...) I want to only name and surname
});
if (bills) {
return bills;
} else {
return undefined;
}
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Для этого вы можете использовать конструктор запросов, который является одним из самых мощных инструментов TypeOrm.
const values = this.billRepository.createQueryBuilder("bill")
.leftJoinAndSelect("bill.user", "user")
.where("bill.accountBill LIKE :accountBill", {accountBill})
.andWhere("user.id = :userId", {userId: user.id})
.select(["user.name", "user.surname"])
.execute();
// NOTE
// .execute() will return raw results.
// To return objects, use .getMany()
Привет @zenbeni, я пытаюсь сделать что-то подобное, но он вернет undefined, если нет пользователя и andWhere условие не выполняется. Как заставить работать тот же запрос, если в таблице отношений еще нет записей?
В вопросе явно упоминается метод find(). Мы хотим полагаться на проводку, которую мы уже сделали в модели. Построитель запросов — мощный инструмент, но его следует использовать только в сложных запросах, где, на мой взгляд, недостаточно связывания модели. Вопрос включает простой запрос.
@hadaytullah, вы не можете использовать проводку базовой модели, так как получение только 2 полей в User полностью выходит за рамки ванильной модели, которая включает гораздо больше полей. Для этого вы должны использовать пользовательский запрос для сопоставления объектов. Таким образом, построитель запросов, это для меня правильный инструмент.
Поскольку typeOrm не применяет выбор к объединенной таблице, мы можем использовать машинописный код Array.map() для удаления ненужных атрибутов из коллекции. Это не мудрое решение, если коллекция слишком велика. Но в то же время такое сопоставление все равно происходит на сервере базы данных, если мы используем построитель запросов с включенным в запрос выбором. Итак, мы просто перенесли эту обработку с сервера БД на сервер API. Время отклика будет хорошей метрикой, чтобы решить, какое решение find+Array.map VS querybuilder подходит для вашего приложения.
Данные - это то, где производительность, вы подразумеваете большее использование сети с вашим предлагаемым решением, больше операций ввода-вывода в базе данных, поскольку вы получаете больше данных, чем вам нужно (в большинстве случаев ввод-вывод и сеть более важны, чем любые оптимизации алгоритма в памяти ). Также вы добавляете нестандартное использование структур данных и TypeOrm с пользовательским кодом, который вам придется поддерживать впоследствии (в отличие от классического использования API-интерфейса querybuilder). Удаление данных столбца после их явного извлечения из базы данных в лучшем случае неоптимально, и его следует избегать с точки зрения производительности.
Именно ограничения моделирования typeORM привели к использованию построителя запросов или ванильного кода. Я предполагаю, что когда модели разрешат более сложные соединения, в них отпадет необходимость. Sequalize — это немного более зрелая ORM, в которой можно выполнять множество сложных объединений.
Если кого-то заинтересует, краткий список кода, связанного с отношением, и ссылки на репо...
https://github.com/typeorm/typeorm/blob/master/src/find-options/FindOptionsUtils.ts
/**
* Applies give find options to the given query builder.
*/
static applyOptionsToQueryBuilder<T>(qb: SelectQueryBuilder<T>, options: FindOneOptions<T> | FindManyOptions<T> | undefined): SelectQueryBuilder<T>;
...
if (options.loadRelationIds === true) {
qb.loadAllRelationIds();
}
else if (options.loadRelationIds instanceof Object) {
qb.loadAllRelationIds(options.loadRelationIds);
}
https://github.com/typeorm/typeorm/blob/master/src/query-builder/SelectQueryBuilder.ts
/**
* Loads all relation ids for all relations of the selected entity.
* All relation ids will be mapped to relation property themself.
* If array of strings is given then loads only relation ids of the given properties.
*/
loadAllRelationIds(options?: { relations?: string[], disableMixedMap?: boolean }): this { // todo: add skip relations
this.expressionMap.mainAlias!.metadata.relations.forEach(relation => {
if (options !== undefined && options.relations !== undefined && options.relations.indexOf(relation.propertyPath) === -1)
return;
this.loadRelationIdAndMap(
this.expressionMap.mainAlias!.name + "." + relation.propertyPath,
this.expressionMap.mainAlias!.name + "." + relation.propertyPath,
options
);
});
return this;
}
/**
* LEFT JOINs relation id and maps it into some entity's property.
* Optionally, you can add condition and parameters used in condition.
*/
loadRelationIdAndMap(mapToProperty: string, relationName: string, options?: { disableMixedMap?: boolean }): this;
/**
* LEFT JOINs relation id and maps it into some entity's property.
* Optionally, you can add condition and parameters used in condition.
*/
loadRelationIdAndMap(mapToProperty: string, relationName: string, alias: string, queryBuilderFactory: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>): this;
/**
* LEFT JOINs relation id and maps it into some entity's property.
* Optionally, you can add condition and parameters used in condition.
*/
loadRelationIdAndMap(mapToProperty: string,
relationName: string,
aliasNameOrOptions?: string|{ disableMixedMap?: boolean },
queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>): this {
const relationIdAttribute = new RelationIdAttribute(this.expressionMap);
relationIdAttribute.mapToProperty = mapToProperty;
relationIdAttribute.relationName = relationName;
if (typeof aliasNameOrOptions === "string")
relationIdAttribute.alias = aliasNameOrOptions;
if (aliasNameOrOptions instanceof Object && (aliasNameOrOptions as any).disableMixedMap)
relationIdAttribute.disableMixedMap = true;
relationIdAttribute.queryBuilderFactory = queryBuilderFactory;
this.expressionMap.relationIdAttributes.push(relationIdAttribute);
if (relationIdAttribute.relation.junctionEntityMetadata) {
this.expressionMap.createAlias({
type: "other",
name: relationIdAttribute.junctionAlias,
metadata: relationIdAttribute.relation.junctionEntityMetadata
});
}
return this;
}
https://github.com/typeorm/typeorm/blob/master/src/query-builder/relation-id/RelationIdAttribute.ts
/**
* Stores all join relation id attributes which will be used to build a JOIN query.
*/
export class RelationIdAttribute {
// -------------------------------------------------------------------------
// Public Properties
// -------------------------------------------------------------------------
/**
* Alias of the joined (destination) table.
*/
alias?: string;
/**
* Name of relation.
*/
relationName: string;
/**
* Property + alias of the object where to joined data should be mapped.
*/
mapToProperty: string;
/**
* Extra condition applied to "ON" section of join.
*/
queryBuilderFactory?: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>;
/**
* Indicates if relation id should NOT be loaded as id map.
*/
disableMixedMap = false;
...
Метод «выполнить» возвращает необработанный результат, поэтому, возможно, вам лучше использовать getMany, который возвращает объект