@accounter/server
Version:
Accounter GraphQL server
65 lines • 3.12 kB
JavaScript
/**
* Queue Match Evaluator Provider
*
* Lazily evaluates match suggestions for a batch of unmatched charges, powering
* the charge matching review screen queue. Purely read-only and analytical —
* no DB mutations happen here.
*/
import { __decorate, __metadata } from "tslib";
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { ChargesMatcherProvider } from './charges-matcher.provider.js';
let QueueMatchEvaluatorProvider = class QueueMatchEvaluatorProvider {
chargesMatcherProvider;
constructor(chargesMatcherProvider) {
this.chargesMatcherProvider = chargesMatcherProvider;
}
/**
* Calculate match suggestions for a batch of unmatched charges on the fly.
*
* Delegates to `ChargesMatcherProvider.findMatchesForCharges`, which builds the
* candidate pool once for the whole batch (instead of once per charge). A charge
* whose evaluation fails (e.g. it turns out to be already matched) is returned
* with empty suggestions rather than failing the whole batch.
*
* @param baseCharges - Unmatched charges to evaluate, in queue order
* @returns One entry per input charge, preserving input order
*/
async evaluateMatchesForCharges(baseCharges) {
if (baseCharges.length === 0) {
return [];
}
const matchesByChargeId = await this.chargesMatcherProvider.findMatchesForCharges(baseCharges.map(baseCharge => baseCharge.id));
return baseCharges.map(baseCharge => ({
id: baseCharge.id,
baseCharge,
// Copy before sorting so we never mutate an array owned by another provider
suggestions: [...(matchesByChargeId.get(baseCharge.id) ?? [])].sort((a, b) => b.confidenceScore - a.confidenceScore),
}));
}
/**
* DataLoader for lazily resolving a single charge's match suggestions.
*
* Backs the `ChargeWithSuggestions.suggestions` field resolver so the queue can
* return base charges immediately and stream suggestions in via `@defer`. All
* suggestions requested within the operation are coalesced into a single
* `findMatchesForCharges` call, so the shared candidate pool is still built once.
*/
suggestionsByChargeIdLoader = new DataLoader(chargeIds => this.batchLoadSuggestions(chargeIds), { name: 'suggestionsByChargeIdLoader' });
async batchLoadSuggestions(chargeIds) {
const matchesByChargeId = await this.chargesMatcherProvider.findMatchesForCharges([
...chargeIds,
]);
return chargeIds.map(chargeId =>
// Copy before sorting so we never mutate an array owned by another provider
[...(matchesByChargeId.get(chargeId) ?? [])].sort((a, b) => b.confidenceScore - a.confidenceScore));
}
};
QueueMatchEvaluatorProvider = __decorate([
Injectable({
scope: Scope.Operation,
}),
__metadata("design:paramtypes", [ChargesMatcherProvider])
], QueueMatchEvaluatorProvider);
export { QueueMatchEvaluatorProvider };
//# sourceMappingURL=queue-match-evaluator.provider.js.map