Browse Source

Group the asset profile identifiers by data source in the market data range query

pull/8273/head
Andrea Bugeja 5 days ago
parent
commit
bd89fb8f76
Failed to extract signature
  1. 22
      apps/api/src/services/market-data/market-data.service.spec.ts
  2. 36
      apps/api/src/services/market-data/market-data.service.ts

22
apps/api/src/services/market-data/market-data.service.spec.ts

@ -9,6 +9,7 @@ describe('MarketDataService', () => {
marketData: { marketData: {
createMany: jest.Mock; createMany: jest.Mock;
deleteMany: jest.Mock; deleteMany: jest.Mock;
findMany: jest.Mock;
upsert: jest.Mock; upsert: jest.Mock;
}; };
}; };
@ -19,6 +20,7 @@ describe('MarketDataService', () => {
marketData: { marketData: {
createMany: jest.fn(), createMany: jest.fn(),
deleteMany: jest.fn(), deleteMany: jest.fn(),
findMany: jest.fn().mockResolvedValue([]),
upsert: jest.fn().mockResolvedValue({}) upsert: jest.fn().mockResolvedValue({})
} }
}; };
@ -26,6 +28,26 @@ describe('MarketDataService', () => {
marketDataService = new MarketDataService(prismaService as any); marketDataService = new MarketDataService(prismaService as any);
}); });
describe('getRange', () => {
it('queries the symbols grouped by data source', async () => {
await marketDataService.getRange({
assetProfileIdentifiers: [
{ dataSource: 'YAHOO', symbol: 'AAPL' },
{ dataSource: 'COINGECKO', symbol: 'bitcoin' },
{ dataSource: 'YAHOO', symbol: 'MSFT' }
],
dateQuery: { gte: parseDate('2026-01-01') }
});
expect(
prismaService.marketData.findMany.mock.calls[0][0].where.OR
).toEqual([
{ dataSource: 'YAHOO', symbol: { in: ['AAPL', 'MSFT'] } },
{ dataSource: 'COINGECKO', symbol: { in: ['bitcoin'] } }
]);
});
});
describe('updateMany', () => { describe('updateMany', () => {
it('does not drop isCarriedForward', async () => { it('does not drop isCarriedForward', async () => {
prismaService.$transaction.mockImplementation((promises) => { prismaService.$transaction.mockImplementation((promises) => {

36
apps/api/src/services/market-data/market-data.service.ts

@ -12,6 +12,7 @@ import {
MarketDataState, MarketDataState,
Prisma Prisma
} from '@prisma/client'; } from '@prisma/client';
import { groupBy } from 'lodash-es';
@Injectable() @Injectable()
export class MarketDataService { export class MarketDataService {
@ -95,12 +96,7 @@ export class MarketDataService {
], ],
where: { where: {
date: dateQuery, date: dateQuery,
OR: assetProfileIdentifiers.map(({ dataSource, symbol }) => { OR: this.getAssetProfileIdentifiersCondition(assetProfileIdentifiers)
return {
dataSource,
symbol
};
})
} }
}); });
} }
@ -115,12 +111,7 @@ export class MarketDataService {
return this.prismaService.marketData.count({ return this.prismaService.marketData.count({
where: { where: {
date: dateQuery, date: dateQuery,
OR: assetProfileIdentifiers.map(({ dataSource, symbol }) => { OR: this.getAssetProfileIdentifiersCondition(assetProfileIdentifiers)
return {
dataSource,
symbol
};
})
} }
}); });
} }
@ -260,4 +251,25 @@ export class MarketDataService {
return this.prismaService.$transaction(upsertPromises); return this.prismaService.$transaction(upsertPromises);
} }
// One condition per data source with all its symbols, since a condition per
// asset profile identifier is slow to evaluate for many asset profiles
private getAssetProfileIdentifiersCondition(
aAssetProfileIdentifiers: AssetProfileIdentifier[]
): Prisma.MarketDataWhereInput[] {
return Object.entries(
groupBy(aAssetProfileIdentifiers, ({ dataSource }) => {
return dataSource;
})
).map(([dataSource, assetProfileIdentifiers]) => {
return {
dataSource: dataSource as DataSource,
symbol: {
in: assetProfileIdentifiers.map(({ symbol }) => {
return symbol;
})
}
};
});
}
} }

Loading…
Cancel
Save