fix: use parallel relation fetching (#173)
optimizes performance of queries with many left joins
This commit is contained in:
@@ -6,6 +6,7 @@ class MockRepo {
|
|||||||
softRemove,
|
softRemove,
|
||||||
find,
|
find,
|
||||||
findOne,
|
findOne,
|
||||||
|
findOneWithRelations,
|
||||||
findOneOrFail,
|
findOneOrFail,
|
||||||
save,
|
save,
|
||||||
findAndCount,
|
findAndCount,
|
||||||
@@ -19,6 +20,7 @@ class MockRepo {
|
|||||||
this.findOneOrFail_ = findOneOrFail;
|
this.findOneOrFail_ = findOneOrFail;
|
||||||
this.save_ = save;
|
this.save_ = save;
|
||||||
this.findAndCount_ = findAndCount;
|
this.findAndCount_ = findAndCount;
|
||||||
|
this.findOneWithRelations_ = findOneWithRelations;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFindOne(fn) {
|
setFindOne(fn) {
|
||||||
@@ -53,6 +55,11 @@ class MockRepo {
|
|||||||
return this.findOneOrFail_(...args);
|
return this.findOneOrFail_(...args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
findOneWithRelations = jest.fn().mockImplementation((...args) => {
|
||||||
|
if (this.findOneWithRelations_) {
|
||||||
|
return this.findOneWithRelations_(...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
findOne = jest.fn().mockImplementation((...args) => {
|
findOne = jest.fn().mockImplementation((...args) => {
|
||||||
if (this.findOne_) {
|
if (this.findOne_) {
|
||||||
return this.findOne_(...args);
|
return this.findOne_(...args);
|
||||||
|
|||||||
+1105
-1227
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,56 @@
|
|||||||
import { EntityRepository, Repository } from "typeorm"
|
import { flatten, groupBy, map, merge } from "lodash"
|
||||||
|
import { EntityRepository, Repository, FindManyOptions } from "typeorm"
|
||||||
import { Order } from "../models/order"
|
import { Order } from "../models/order"
|
||||||
|
|
||||||
@EntityRepository(Order)
|
@EntityRepository(Order)
|
||||||
export class OrderRepository extends Repository<Order> {}
|
export class OrderRepository extends Repository<Order> {
|
||||||
|
public async findWithRelations(
|
||||||
|
relations: Array<keyof Order> = [],
|
||||||
|
optionsWithoutRelations: Omit<FindManyOptions<Order>, "relations"> = {}
|
||||||
|
): Promise<Order[]> {
|
||||||
|
const entities = await this.find(optionsWithoutRelations)
|
||||||
|
const entitiesIds = entities.map(({ id }) => id)
|
||||||
|
const entitiesIdsWithRelations = await Promise.all(
|
||||||
|
relations.map(relation => {
|
||||||
|
const relationParts = relation.split(".") as string[]
|
||||||
|
|
||||||
|
const partialRelations = relationParts.reduce(
|
||||||
|
(acc: string[], _: string, index: number) => {
|
||||||
|
const toPush = []
|
||||||
|
|
||||||
|
for (let i = 0; i <= index; i++) {
|
||||||
|
toPush.push(relationParts[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.push(toPush.join("."))
|
||||||
|
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
[] as string[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return this.findByIds(entitiesIds, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: partialRelations,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
).then(flatten)
|
||||||
|
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||||
|
|
||||||
|
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||||
|
return map(entitiesAndRelationsById, entityAndRelations =>
|
||||||
|
merge({}, ...entityAndRelations)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findOneWithRelations(
|
||||||
|
relations: Array<keyof Order> = [],
|
||||||
|
optionsWithoutRelations: Omit<FindManyOptions<Order>, "relations"> = {}
|
||||||
|
): Promise<Order> {
|
||||||
|
const result = await this.findWithRelations(
|
||||||
|
relations,
|
||||||
|
optionsWithoutRelations
|
||||||
|
)
|
||||||
|
return result[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
describe("retrieve", () => {
|
describe("retrieve", () => {
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: q => {
|
||||||
return Promise.resolve({})
|
return Promise.resolve({})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -412,8 +412,8 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
it("calls order model functions", async () => {
|
it("calls order model functions", async () => {
|
||||||
await orderService.retrieve(IdMap.getId("test-order"))
|
await orderService.retrieve(IdMap.getId("test-order"))
|
||||||
expect(orderRepo.findOne).toHaveBeenCalledTimes(1)
|
expect(orderRepo.findOneWithRelations).toHaveBeenCalledTimes(1)
|
||||||
expect(orderRepo.findOne).toHaveBeenCalledWith({
|
expect(orderRepo.findOneWithRelations).toHaveBeenCalledWith(undefined, {
|
||||||
where: { id: IdMap.getId("test-order") },
|
where: { id: IdMap.getId("test-order") },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -446,7 +446,7 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
case IdMap.getId("fulfilled-order"):
|
case IdMap.getId("fulfilled-order"):
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -527,7 +527,7 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
describe("cancel", () => {
|
describe("cancel", () => {
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
case IdMap.getId("paid-order"):
|
case IdMap.getId("paid-order"):
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -606,7 +606,7 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
describe("capturePayment", () => {
|
describe("capturePayment", () => {
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
case IdMap.getId("fail"):
|
case IdMap.getId("fail"):
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -718,7 +718,7 @@ describe("OrderService", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
case "partial":
|
case "partial":
|
||||||
return Promise.resolve(partialOrder)
|
return Promise.resolve(partialOrder)
|
||||||
@@ -870,7 +870,7 @@ describe("OrderService", () => {
|
|||||||
payments: [{ id: "payment_test" }],
|
payments: [{ id: "payment_test" }],
|
||||||
}
|
}
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
default:
|
default:
|
||||||
return Promise.resolve(order)
|
return Promise.resolve(order)
|
||||||
@@ -1043,7 +1043,7 @@ describe("OrderService", () => {
|
|||||||
payments: [{ id: "payment_test" }],
|
payments: [{ id: "payment_test" }],
|
||||||
}
|
}
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
default:
|
default:
|
||||||
return Promise.resolve(order)
|
return Promise.resolve(order)
|
||||||
@@ -1129,7 +1129,7 @@ describe("OrderService", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
switch (q.where.id) {
|
switch (q.where.id) {
|
||||||
case IdMap.getId("partial"):
|
case IdMap.getId("partial"):
|
||||||
return Promise.resolve(partialOrder)
|
return Promise.resolve(partialOrder)
|
||||||
@@ -1206,9 +1206,7 @@ describe("OrderService", () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: jest
|
findOneWithRelations: () => Promise.resolve({ id: IdMap.getId("order") }),
|
||||||
.fn()
|
|
||||||
.mockReturnValue(Promise.resolve({ id: IdMap.getId("order") })),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails if order/swap relationship not satisfied", async () => {
|
it("fails if order/swap relationship not satisfied", async () => {
|
||||||
@@ -1268,7 +1266,7 @@ describe("OrderService", () => {
|
|||||||
|
|
||||||
it("registers a swap as received", async () => {
|
it("registers a swap as received", async () => {
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: jest.fn().mockReturnValue(
|
findOneWithRelations: () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
id: IdMap.getId("order_123"),
|
id: IdMap.getId("order_123"),
|
||||||
items: [
|
items: [
|
||||||
@@ -1278,8 +1276,7 @@ describe("OrderService", () => {
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
}),
|
||||||
),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const swapService = {
|
const swapService = {
|
||||||
@@ -1329,7 +1326,7 @@ describe("OrderService", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const orderRepo = MockRepository({
|
const orderRepo = MockRepository({
|
||||||
findOne: jest.fn().mockImplementation(q => {
|
findOneWithRelations: (rel, q) => {
|
||||||
if (q.where.id === IdMap.getId("cannot")) {
|
if (q.where.id === IdMap.getId("cannot")) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
id: IdMap.getId("order"),
|
id: IdMap.getId("order"),
|
||||||
@@ -1353,7 +1350,7 @@ describe("OrderService", () => {
|
|||||||
total: 100,
|
total: 100,
|
||||||
refunded_total: 0,
|
refunded_total: 0,
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const paymentProviderService = {
|
const paymentProviderService = {
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ class OrderService extends BaseService {
|
|||||||
query.select = select
|
query.select = select
|
||||||
}
|
}
|
||||||
|
|
||||||
const raw = await orderRepo.findOne(query)
|
const raw = await orderRepo.findOneWithRelations(query.relations, query)
|
||||||
|
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
|
|||||||
+11
-774
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user