Как реализовать INTERSECT в TypeORM с посдержкой take() и skip()
Мне необходимо реализовать фильтрацию продуктов в интернет магазине. При этом там предусмотренна пагинация.
Саму фильтрацию я сделал, но без потдержки пагинации.
Схема БД:

Моя реализация:
async function (request: Request, response: Response, next: NextFunction) {
try {
const category = request.query["category"]
delete request.query["category"]
// delete request.query["limit"]
// delete request.query["offset"]
let data = await getConnection().transaction(async manager => {
return Promise.all(
Object.keys(request.query).map(attribute => {
let values = request.query[attribute]
if (typeof values === "string") values = [values]
return manager.createQueryBuilder(ProductEntity, "product")
.innerJoin("product.properties", "property")
.where(`product.categoryId = :category_id`, { category_id: category })
.andWhere(`property.attributeSlug = :attribute_id`, { attribute_id: attribute })
.andWhere(`property.valueSlug IN (:...value_ids)`, { value_ids: values })
.getMany()
})
)
})
// Костыль для реализации INTERSECT.
let products = data.pop() || []
if (data.length > 0)
products = products.filter(product => {
for (const set of data)
if (set.findIndex(part => product.id === part.id) === -1)
return false
return true
})
return response.status(200)
.json(
products
);
} catch (error) {
next(error);
}
}
Однако данный метод не позволяет реализвать частичную выборку, необходимую для пагинации.
Также буду благодарен если Вы подскажете как оптимизировать данный код, а то мне не сильно нравиться потенциальные 100+ запросов в БД.
Ответы (1 шт):
Автор решения: nörbörnën
→ Ссылка
Нужно просто собрать условия через or
import { getConnection, Brackets } from 'typeorm';
const query: Record<string, any> = {};
const { category, ...filter } = query;
const qb = getConnection()
.createQueryBuilder(ProductEntity, 'product')
.where({ categoryId: category })
.innerJoin('product.properties', 'property');
qb.andWhere(
new Brackets((querybuilder) => {
Object.keys(filter).forEach((attribute_id) => {
const values = filter[attribute_id];
if (values !== null && values !== undefined) {
querybuilder.orWhere(
`property.attributeSlug = :attribute_id and property.valueSlug IN (:...value_ids)`,
{ attribute_id, value_ids: Array.isArray(values) ? values : [values] },
);
}
});
}),
);
// .take().skip() или .limit().offset()
const items = await qb.getMany();