Skip to content

Commit 1e6d02d

Browse files
committed
feat(repository): fetch and hydrate polymorphs via bulk queries rather than individual ones
#9
1 parent 15782b0 commit 1e6d02d

File tree

2 files changed

+292
-92
lines changed

2 files changed

+292
-92
lines changed

src/__tests__/polymorphic.repository.spec.ts

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DataSource, Repository } from 'typeorm';
1+
import { DataSource, DeepPartial } from 'typeorm';
22
import { AdvertEntity } from './entities/advert.entity';
33
import { UserEntity } from './entities/user.entity';
44
import { config } from 'dotenv';
@@ -324,4 +324,124 @@ describe('AbstractPolymorphicRepository', () => {
324324
});
325325
});
326326
});
327+
328+
describe('Batch', () => {
329+
describe('hydrate', () => {
330+
it('Can hydrate entities with properly', async () => {
331+
const advertRepository = AbstractPolymorphicRepository.createRepository(
332+
connection,
333+
AdvertRepository,
334+
);
335+
const userRepository = AbstractPolymorphicRepository.createRepository(
336+
connection,
337+
UserRepository,
338+
);
339+
const merchantRepository = connection.getRepository(MerchantEntity);
340+
341+
const user = await userRepository.save(new UserEntity());
342+
const user2 = await userRepository.save(new UserEntity());
343+
const user3 = await userRepository.save(new UserEntity());
344+
const merchant = await merchantRepository.save(new MerchantEntity());
345+
const merchant2 = await merchantRepository.save(new MerchantEntity());
346+
347+
type ManifestItem = {
348+
config: DeepPartial<AdvertEntity>;
349+
advert: AdvertEntity | null;
350+
user: UserEntity | null;
351+
};
352+
const manifest: Array<ManifestItem> = [
353+
{ config: { owner: user }, advert: null, user: user },
354+
{ config: { owner: user2 }, advert: null, user: user2 },
355+
{ config: { owner: user3 }, advert: null, user: user3 },
356+
{ config: { owner: merchant }, advert: null, user: null },
357+
{ config: { owner: merchant2 }, advert: null, user: null },
358+
{ config: { creator: merchant2 }, advert: null, user: null },
359+
{
360+
config: { owner: user, creator: merchant2 },
361+
advert: null,
362+
user: user,
363+
},
364+
{
365+
config: { owner: user2, creator: merchant2 },
366+
advert: null,
367+
user: user2,
368+
},
369+
];
370+
371+
// save all the items first the maximize the chance of
372+
// hydration errors
373+
for (const item of manifest) {
374+
const entity = await advertRepository.save(
375+
advertRepository.create(item.config),
376+
);
377+
item.advert = entity;
378+
}
379+
380+
/********************************
381+
* test advert hydration (parent)
382+
********************************/
383+
const adverts = await advertRepository.find();
384+
const advertManifestMap = manifest.reduce((acc, item) => {
385+
if (item.advert) {
386+
acc[item.advert.id] = item;
387+
}
388+
return acc;
389+
}, {});
390+
391+
for (const advert of adverts) {
392+
const manifestItem = advertManifestMap[advert.id];
393+
if (!manifestItem) {
394+
throw new Error('this should not happen.');
395+
}
396+
397+
const {
398+
config: { owner, creator },
399+
} = manifestItem;
400+
if (owner) {
401+
expect(advert.owner).toBeInstanceOf(owner.constructor);
402+
expect(advert.owner.id).toBe(owner.id);
403+
}
404+
405+
if (creator) {
406+
expect(advert.creator).toBeInstanceOf(creator.constructor);
407+
expect(advert.creator.id).toBe(creator.id);
408+
}
409+
}
410+
411+
/********************************
412+
* test user hydration (child)
413+
********************************/
414+
const users = await userRepository.find();
415+
const usersAdvertMap = manifest.reduce<Record<number, AdvertEntity[]>>(
416+
(acc, item) => {
417+
if (item.user && item.advert) {
418+
acc[item.user.id] = acc[item.user.id] || [];
419+
acc[item.user.id].push(item.advert);
420+
}
421+
return acc;
422+
},
423+
{},
424+
);
425+
426+
for (const user of users) {
427+
const adverts = usersAdvertMap[user.id];
428+
if (!adverts || !adverts.length) {
429+
throw new Error('this should not happen.');
430+
}
431+
432+
const actualIds = user.adverts
433+
.map((advert) => {
434+
return advert.id;
435+
})
436+
.sort();
437+
const expectedIds = adverts
438+
.map((advert) => {
439+
return advert.id;
440+
})
441+
.sort();
442+
expect(actualIds).toEqual(expectedIds);
443+
}
444+
});
445+
});
446+
});
327447
});

0 commit comments

Comments
 (0)