Browse Source

Task/optimize performance of search in assistant (#7534)

* Improve performance of search in assistant by reusing cached portfolio snapshot

* Update changelog
pull/7533/head^2
Thomas Kaul 4 days ago
committed by GitHub
parent
commit
0b9df572ba
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      CHANGELOG.md
  2. 30
      apps/api/src/app/portfolio/portfolio.service.ts
  3. 1
      libs/common/src/lib/interfaces/portfolio-position.interface.ts

10
CHANGELOG.md

@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Optimized the performance of the search in the assistant by reusing the cached portfolio snapshot
### Fixed
- Fixed the fuzzy search for the holdings in the assistant
## 3.41.0 - 2026-08-03 ## 3.41.0 - 2026-08-03
### Added ### Added

30
apps/api/src/app/portfolio/portfolio.service.ts

@ -165,6 +165,10 @@ export class PortfolioService {
}; };
} }
const filtersWithoutSearchQueryFilter = filters?.filter(({ type }) => {
return type !== 'SEARCH_QUERY';
});
const [accounts, details, user] = await Promise.all([ const [accounts, details, user] = await Promise.all([
this.accountService.accounts({ this.accountService.accounts({
where, where,
@ -176,8 +180,8 @@ export class PortfolioService {
orderBy: { name: 'asc' } orderBy: { name: 'asc' }
}), }),
this.getDetails({ this.getDetails({
filters,
withExcludedAccounts, withExcludedAccounts,
filters: filtersWithoutSearchQueryFilter,
impersonationId: userId, impersonationId: userId,
userId: this.request.user.id userId: this.request.user.id
}), }),
@ -369,14 +373,6 @@ export class PortfolioService {
userId: string; userId: string;
}) { }) {
userId = await this.getUserId(impersonationId, userId); userId = await this.getUserId(impersonationId, userId);
const { holdings: holdingsMap } = await this.getDetails({
dateRange,
filters,
impersonationId,
userId
});
let holdings = Object.values(holdingsMap);
const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy( const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy(
filters, filters,
@ -385,9 +381,22 @@ export class PortfolioService {
} }
); );
const filtersWithoutSearchQueryFilter = filters?.filter(({ type }) => {
return type !== 'SEARCH_QUERY';
});
const { holdings: holdingsMap } = await this.getDetails({
dateRange,
impersonationId,
userId,
filters: filtersWithoutSearchQueryFilter
});
let holdings = Object.values(holdingsMap);
if (filterBySearchQuery) { if (filterBySearchQuery) {
const fuse = new Fuse(holdings, { const fuse = new Fuse(holdings, {
keys: ['isin', 'name', 'symbol'], keys: ['assetProfile.isin', 'assetProfile.name', 'assetProfile.symbol'],
threshold: 0.3 threshold: 0.3
}); });
@ -651,6 +660,7 @@ export class PortfolioService {
}; };
} }
), ),
isin: assetProfile.isin,
name: assetProfile.name, name: assetProfile.name,
sectors: assetProfile.sectors, sectors: assetProfile.sectors,
symbol: assetProfile.symbol, symbol: assetProfile.symbol,

1
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -15,6 +15,7 @@ export interface PortfolioPosition {
| 'currency' | 'currency'
| 'dataSource' | 'dataSource'
| 'holdings' | 'holdings'
| 'isin'
| 'name' | 'name'
| 'sectors' | 'sectors'
| 'symbol' | 'symbol'

Loading…
Cancel
Save