get cached matching

This commit is contained in:
sbriat 2023-09-28 15:23:17 +02:00
parent 09efe313ba
commit b810bc86e6
10 changed files with 650 additions and 151 deletions

View File

@ -60,6 +60,44 @@ export class MatchQueryHandler implements IQueryHandler {
perPage: this._defaultParams.PER_PAGE,
})
.setDatesAndSchedule(this.datetimeTransformer);
let matchingEntity: MatchingEntity | undefined = await this._cachedMatching(
query.id,
);
if (!matchingEntity)
matchingEntity = (await this._createMatching(query)) as MatchingEntity;
const perPage: number = query.perPage as number;
const page: number = Paginator.pageNumber(
matchingEntity.getProps().matches.length,
perPage,
query.page as number,
);
return {
id: matchingEntity.id,
matches: Paginator.pageItems(
matchingEntity.getProps().matches,
page,
perPage,
),
total: matchingEntity.getProps().matches.length,
page,
perPage,
};
};
private _cachedMatching = async (
id?: string,
): Promise<MatchingEntity | undefined> => {
if (!id) return undefined;
try {
return await this.matchingRepository.get(id);
} catch (e: any) {
return undefined;
}
};
private _createMatching = async (
query: MatchQuery,
): Promise<MatchingEntity> => {
await query.setRoutes();
let algorithm: Algorithm;
@ -70,30 +108,8 @@ export class MatchQueryHandler implements IQueryHandler {
}
const matches: MatchEntity[] = await algorithm.match();
const perPage: number = query.perPage as number;
const page: number = Paginator.pageNumber(
matches.length,
perPage,
query.page as number,
);
// create Matching Entity for persistence
const matchingEntity: MatchingEntity = MatchingEntity.create({
matches: matches.map((matchEntity: MatchEntity) => ({
adId: matchEntity.getProps().adId,
role: matchEntity.getProps().role,
frequency: matchEntity.getProps().frequency,
distance: matchEntity.getProps().distance,
duration: matchEntity.getProps().duration,
initialDistance: matchEntity.getProps().initialDistance,
initialDuration: matchEntity.getProps().initialDuration,
distanceDetour: matchEntity.getProps().distanceDetour,
durationDetour: matchEntity.getProps().durationDetour,
distanceDetourPercentage:
matchEntity.getProps().distanceDetourPercentage,
durationDetourPercentage:
matchEntity.getProps().durationDetourPercentage,
journeys: matchEntity.getProps().journeys,
})),
const matchingEntity = MatchingEntity.create({
matches,
query: {
driver: query.driver as boolean,
passenger: query.passenger as boolean,
@ -120,13 +136,7 @@ export class MatchQueryHandler implements IQueryHandler {
},
});
await this.matchingRepository.save(matchingEntity);
return {
id: matchingEntity.id,
matches: Paginator.pageItems(matches, page, perPage),
total: matches.length,
page,
perPage,
};
return matchingEntity;
};
}

View File

@ -15,6 +15,7 @@ import { Point } from '@modules/ad/core/domain/value-objects/point.value-object'
import { Route } from '../../types/route.type';
export class MatchQuery extends QueryBase {
id?: string;
driver?: boolean;
passenger?: boolean;
readonly frequency: Frequency;
@ -43,6 +44,7 @@ export class MatchQuery extends QueryBase {
constructor(props: MatchRequestDto, routeProvider: RouteProviderPort) {
super();
this.id = props.id;
this.driver = props.driver;
this.passenger = props.passenger;
this.frequency = props.frequency;

View File

@ -1,14 +1,14 @@
import { MatchProps } from './match.types';
import { MatchEntity } from './match.entity';
import { MatchQueryProps } from './value-objects/match-query.value-object';
// All properties that a Matching has
export interface MatchingProps {
query: MatchQueryProps; // the query that induced the matches
matches: MatchProps[];
matches: MatchEntity[];
}
// Properties that are needed for a Matching creation
export interface CreateMatchingProps {
query: MatchQueryProps;
matches: MatchProps[];
matches: MatchEntity[];
}

View File

@ -7,6 +7,7 @@ import {
IsISO8601,
IsInt,
IsOptional,
IsUUID,
Max,
Min,
ValidateNested,
@ -21,6 +22,10 @@ import { Frequency } from '@modules/ad/core/domain/ad.types';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
export class MatchRequestDto {
@IsUUID()
@IsOptional()
id?: string;
@IsOptional()
@IsBoolean()
driver?: boolean;

View File

@ -7,25 +7,26 @@ service MatcherService {
}
message MatchRequest {
bool driver = 1;
bool passenger = 2;
Frequency frequency = 3;
string fromDate = 4;
string toDate = 5;
repeated ScheduleItem schedule = 6;
bool strict = 7;
repeated Waypoint waypoints = 8;
AlgorithmType algorithmType = 9;
int32 remoteness = 10;
bool useProportion = 11;
int32 proportion = 12;
bool useAzimuth = 13;
int32 azimuthMargin = 14;
float maxDetourDistanceRatio = 15;
float maxDetourDurationRatio = 16;
int32 identifier = 22;
optional int32 page = 23;
optional int32 perPage = 24;
string id = 1;
bool driver = 2;
bool passenger = 3;
Frequency frequency = 4;
string fromDate = 5;
string toDate = 6;
repeated ScheduleItem schedule = 7;
bool strict = 8;
repeated Waypoint waypoints = 9;
AlgorithmType algorithmType = 10;
int32 remoteness = 11;
bool useProportion = 12;
int32 proportion = 13;
bool useAzimuth = 14;
int32 azimuthMargin = 15;
float maxDetourDistanceRatio = 16;
float maxDetourDurationRatio = 17;
int32 identifier = 18;
optional int32 page = 19;
optional int32 perPage = 20;
}
message ScheduleItem {

View File

@ -15,22 +15,32 @@ export class MatchMapper {
private readonly outputDatetimeTransformer: DateTimeTransformerPort,
) {}
toResponse = (match: MatchEntity): MatchResponseDto => ({
...new ResponseBase(match),
adId: match.getProps().adId,
role: match.getProps().role,
frequency: match.getProps().frequency,
distance: match.getProps().distance,
duration: match.getProps().duration,
initialDistance: match.getProps().initialDistance,
initialDuration: match.getProps().initialDuration,
distanceDetour: match.getProps().distanceDetour,
durationDetour: match.getProps().durationDetour,
distanceDetourPercentage: match.getProps().distanceDetourPercentage,
durationDetourPercentage: match.getProps().durationDetourPercentage,
journeys: match.getProps().journeys.map((journey: Journey) => ({
day: new Date(
this.outputDatetimeTransformer.fromDate(
toResponse = (match: MatchEntity): MatchResponseDto => {
return {
...new ResponseBase(match),
adId: match.getProps().adId,
role: match.getProps().role,
frequency: match.getProps().frequency,
distance: match.getProps().distance,
duration: match.getProps().duration,
initialDistance: match.getProps().initialDistance,
initialDuration: match.getProps().initialDuration,
distanceDetour: match.getProps().distanceDetour,
durationDetour: match.getProps().durationDetour,
distanceDetourPercentage: match.getProps().distanceDetourPercentage,
durationDetourPercentage: match.getProps().durationDetourPercentage,
journeys: match.getProps().journeys.map((journey: Journey) => ({
day: new Date(
this.outputDatetimeTransformer.fromDate(
{
date: journey.firstDate.toISOString().split('T')[0],
time: journey.firstDriverDepartureTime(),
coordinates: journey.driverOrigin(),
},
match.getProps().frequency,
),
).getDay(),
firstDate: this.outputDatetimeTransformer.fromDate(
{
date: journey.firstDate.toISOString().split('T')[0],
time: journey.firstDriverDepartureTime(),
@ -38,41 +48,33 @@ export class MatchMapper {
},
match.getProps().frequency,
),
).getDay(),
firstDate: this.outputDatetimeTransformer.fromDate(
{
date: journey.firstDate.toISOString().split('T')[0],
time: journey.firstDriverDepartureTime(),
coordinates: journey.driverOrigin(),
},
match.getProps().frequency,
),
lastDate: this.outputDatetimeTransformer.fromDate(
{
date: journey.lastDate.toISOString().split('T')[0],
time: journey.firstDriverDepartureTime(),
coordinates: journey.driverOrigin(),
},
match.getProps().frequency,
),
steps: journey.journeyItems.map((journeyItem: JourneyItem) => ({
duration: journeyItem.duration,
distance: journeyItem.distance as number,
lon: journeyItem.lon,
lat: journeyItem.lat,
time: this.outputDatetimeTransformer.time(
lastDate: this.outputDatetimeTransformer.fromDate(
{
date: journey.firstDate.toISOString().split('T')[0],
time: journeyItem.driverTime(),
date: journey.lastDate.toISOString().split('T')[0],
time: journey.firstDriverDepartureTime(),
coordinates: journey.driverOrigin(),
},
match.getProps().frequency,
),
actors: journeyItem.actorTimes.map((actorTime: ActorTime) => ({
role: actorTime.role,
target: actorTime.target,
steps: journey.journeyItems.map((journeyItem: JourneyItem) => ({
duration: journeyItem.duration,
distance: journeyItem.distance as number,
lon: journeyItem.lon,
lat: journeyItem.lat,
time: this.outputDatetimeTransformer.time(
{
date: journey.firstDate.toISOString().split('T')[0],
time: journeyItem.driverTime(),
coordinates: journey.driverOrigin(),
},
match.getProps().frequency,
),
actors: journeyItem.actorTimes.map((actorTime: ActorTime) => ({
role: actorTime.role,
target: actorTime.target,
})),
})),
})),
})),
});
};
};
}

View File

@ -1,13 +1,234 @@
import { Injectable } from '@nestjs/common';
import { Mapper } from '@mobicoop/ddd-library';
import { MatchingEntity } from './core/domain/matching.entity';
import { Frequency, Role } from './core/domain/ad.types';
import { MatchEntity } from './core/domain/match.entity';
import { Target } from './core/domain/candidate.types';
import { Waypoint } from './core/application/types/waypoint.type';
import { ScheduleItem } from './core/application/types/schedule-item.type';
import { Journey } from './core/domain/value-objects/journey.value-object';
import { JourneyItem } from './core/domain/value-objects/journey-item.value-object';
import { ActorTime } from './core/domain/value-objects/actor-time.value-object';
@Injectable()
export class MatchingMapper
implements Mapper<MatchingEntity, string, string, undefined>
{
toPersistence = (entity: MatchingEntity): string => JSON.stringify(entity);
toPersistence = (entity: MatchingEntity): string =>
JSON.stringify(<PersistedMatching>{
id: entity.id,
createdAt: entity.createdAt.toISOString(),
updatedAt: entity.updatedAt.toISOString(),
matches: entity.getProps().matches.map((match: MatchEntity) => ({
adId: match.getProps().adId,
role: match.getProps().role,
frequency: match.getProps().frequency,
distance: match.getProps().distance,
duration: match.getProps().duration,
initialDistance: match.getProps().initialDistance,
initialDuration: match.getProps().initialDuration,
distanceDetour: match.getProps().distanceDetour,
durationDetour: match.getProps().durationDetour,
distanceDetourPercentage: match.getProps().distanceDetourPercentage,
durationDetourPercentage: match.getProps().durationDetourPercentage,
journeys: match.getProps().journeys.map((journey: Journey) => ({
firstDate: journey.firstDate.toISOString(),
lastDate: journey.lastDate.toISOString(),
journeyItems: journey.journeyItems.map(
(journeyItem: JourneyItem) => ({
lon: journeyItem.lon,
lat: journeyItem.lat,
duration: journeyItem.duration,
distance: journeyItem.distance,
actorTimes: journeyItem.actorTimes.map(
(actorTime: ActorTime) => ({
role: actorTime.role,
target: actorTime.target,
firstDatetime: actorTime.firstDatetime.toISOString(),
firstMinDatetime: actorTime.firstMinDatetime.toISOString(),
firstMaxDatetime: actorTime.firstMaxDatetime.toISOString(),
lastDatetime: actorTime.lastDatetime.toISOString(),
lastMinDatetime: actorTime.lastMinDatetime.toISOString(),
lastMaxDatetime: actorTime.lastMaxDatetime.toISOString(),
}),
),
}),
),
})),
})),
query: {
driver: entity.getProps().query.driver,
passenger: entity.getProps().query.passenger,
frequency: entity.getProps().query.frequency,
fromDate: entity.getProps().query.fromDate,
toDate: entity.getProps().query.toDate,
schedule: entity
.getProps()
.query.schedule.map((scheduleItem: ScheduleItem) => ({
day: scheduleItem.day,
time: scheduleItem.time,
margin: scheduleItem.margin,
})),
seatsProposed: entity.getProps().query.seatsProposed,
seatsRequested: entity.getProps().query.seatsRequested,
strict: entity.getProps().query.strict,
waypoints: entity
.getProps()
.query.waypoints.map((waypoint: Waypoint) => ({
lon: waypoint.lon,
lat: waypoint.lat,
position: waypoint.position,
houseNumber: waypoint.houseNumber,
street: waypoint.street,
postalCode: waypoint.postalCode,
locality: waypoint.locality,
country: waypoint.country,
})),
algorithmType: entity.getProps().query.algorithmType,
remoteness: entity.getProps().query.remoteness,
useProportion: entity.getProps().query.useProportion,
proportion: entity.getProps().query.proportion,
useAzimuth: entity.getProps().query.useAzimuth,
azimuthMargin: entity.getProps().query.azimuthMargin,
maxDetourDistanceRatio: entity.getProps().query.maxDetourDistanceRatio,
maxDetourDurationRatio: entity.getProps().query.maxDetourDurationRatio,
},
});
toDomain = (record: string): MatchingEntity =>
new MatchingEntity(JSON.parse(record));
toDomain = (record: string): MatchingEntity => {
const parsedRecord: PersistedMatching = JSON.parse(record);
const matchingEntity: MatchingEntity = new MatchingEntity({
id: parsedRecord.id,
createdAt: new Date(parsedRecord.createdAt),
updatedAt: new Date(parsedRecord.updatedAt),
props: {
query: parsedRecord.query,
matches: parsedRecord.matches.map((match: PersistedMatch) =>
MatchEntity.create({
adId: match.adId,
role: match.role,
frequency: match.frequency,
distance: match.distance,
duration: match.duration,
initialDistance: match.initialDistance,
initialDuration: match.initialDuration,
journeys: match.journeys.map(
(journey: PersistedJourney) =>
new Journey({
firstDate: new Date(journey.firstDate),
lastDate: new Date(journey.lastDate),
journeyItems: journey.journeyItems.map(
(journeyItem: PersistedJourneyItem) =>
new JourneyItem({
lon: journeyItem.lon,
lat: journeyItem.lat,
duration: journeyItem.duration,
distance: journeyItem.distance,
actorTimes: journeyItem.actorTimes.map(
(actorTime: PersistedActorTime) =>
new ActorTime({
role: actorTime.role,
target: actorTime.target,
firstDatetime: new Date(actorTime.firstDatetime),
firstMinDatetime: new Date(
actorTime.firstMinDatetime,
),
firstMaxDatetime: new Date(
actorTime.firstMaxDatetime,
),
lastDatetime: new Date(actorTime.lastDatetime),
lastMinDatetime: new Date(
actorTime.lastMinDatetime,
),
lastMaxDatetime: new Date(
actorTime.lastMaxDatetime,
),
}),
),
}),
),
}),
),
}),
),
},
});
return matchingEntity;
};
}
type PersistedMatching = {
id: string;
createdAt: string;
updatedAt: string;
matches: PersistedMatch[];
query: {
driver: boolean;
passenger: boolean;
frequency: Frequency;
fromDate: string;
toDate: string;
schedule: {
day: number;
time: string;
margin: number;
}[];
seatsProposed: number;
seatsRequested: number;
strict: boolean;
waypoints: {
houseNumber: string;
street: string;
postalCode: string;
locality: string;
lon: number;
lat: number;
country: string;
position: number;
}[];
algorithmType: string;
remoteness: number;
useProportion: boolean;
proportion: number;
useAzimuth: boolean;
azimuthMargin: number;
maxDetourDistanceRatio: number;
maxDetourDurationRatio: number;
};
};
type PersistedMatch = {
adId: string;
role: Role;
frequency: Frequency;
distance: number;
duration: number;
initialDistance: number;
initialDuration: number;
journeys: PersistedJourney[];
};
type PersistedJourney = {
firstDate: string;
lastDate: string;
journeyItems: PersistedJourneyItem[];
};
type PersistedJourneyItem = {
lon: number;
lat: number;
duration: number;
distance: number;
actorTimes: PersistedActorTime[];
};
type PersistedActorTime = {
role: Role;
target: Target;
firstDatetime: string;
firstMinDatetime: string;
firstMaxDatetime: string;
lastDatetime: string;
lastMinDatetime: string;
lastMaxDatetime: string;
};

View File

@ -16,6 +16,8 @@ import {
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { Target } from '@modules/ad/core/domain/candidate.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
import { Test, TestingModule } from '@nestjs/testing';
@ -76,7 +78,156 @@ const mockAdRepository = {
};
const mockMatchingRepository: MatchingRepositoryPort = {
get: jest.fn(),
get: jest
.fn()
.mockImplementationOnce(
() =>
new MatchingEntity({
id: 'a3b10efb-121e-4d08-9198-9f57afdb5e2d',
createdAt: new Date('2023-08-20T09:48:00Z'),
updatedAt: new Date('2023-08-20T09:48:00Z'),
props: {
matches: [
new MatchEntity({
id: '4bd4e90b-ffba-4f5f-b904-48ad0667a1d7',
createdAt: new Date('2023-08-30T08:45:00Z'),
updatedAt: new Date('2023-08-30T08:45:00Z'),
props: {
adId: 'dd937edf-1264-4868-b073-d1952abe30b1',
role: Role.DRIVER,
frequency: Frequency.PUNCTUAL,
distance: 356041,
duration: 12647,
initialDistance: 348745,
initialDuration: 12105,
distanceDetour: 7296,
durationDetour: 542,
distanceDetourPercentage: 4.1,
durationDetourPercentage: 3.8,
journeys: [
{
firstDate: new Date('2023-08-28'),
lastDate: new Date('2023-08-28'),
journeyItems: [
{
lon: 6.389745,
lat: 48.32644,
duration: 0,
distance: 0,
actorTimes: [
{
role: Role.DRIVER,
target: Target.START,
firstDatetime: new Date('2023-08-28T07:00:00Z'),
firstMinDatetime: new Date(
'2023-08-28T06:45:00Z',
),
firstMaxDatetime: new Date(
'2023-08-28T07:15:00Z',
),
lastDatetime: new Date('2023-08-28T07:00:00Z'),
lastMinDatetime: new Date('2023-08-28T06:45:00Z'),
lastMaxDatetime: new Date('2023-08-28T07:15:00Z'),
},
{
role: Role.PASSENGER,
target: Target.START,
firstDatetime: new Date('2023-08-28T07:00:00Z'),
firstMinDatetime: new Date(
'2023-08-28T06:45:00Z',
),
firstMaxDatetime: new Date(
'2023-08-28T07:15:00Z',
),
lastDatetime: new Date('2023-08-28T07:00:00Z'),
lastMinDatetime: new Date('2023-08-28T06:45:00Z'),
lastMaxDatetime: new Date('2023-08-28T07:15:00Z'),
},
],
},
{
lon: 6.984567,
lat: 48.021548,
distance: 356041,
duration: 12647,
actorTimes: [
{
role: Role.DRIVER,
target: Target.FINISH,
firstDatetime: new Date('2023-08-28T07:00:00Z'),
firstMinDatetime: new Date(
'2023-08-28T06:45:00Z',
),
firstMaxDatetime: new Date(
'2023-08-28T07:15:00Z',
),
lastDatetime: new Date('2023-08-28T07:00:00Z'),
lastMinDatetime: new Date('2023-08-28T06:45:00Z'),
lastMaxDatetime: new Date('2023-08-28T07:15:00Z'),
},
{
role: Role.PASSENGER,
target: Target.FINISH,
firstDatetime: new Date('2023-08-28T07:00:00Z'),
firstMinDatetime: new Date(
'2023-08-28T06:45:00Z',
),
firstMaxDatetime: new Date(
'2023-08-28T07:15:00Z',
),
lastDatetime: new Date('2023-08-28T07:00:00Z'),
lastMinDatetime: new Date('2023-08-28T06:45:00Z'),
lastMaxDatetime: new Date('2023-08-28T07:15:00Z'),
},
],
},
],
},
],
},
}),
],
query: {
driver: false,
passenger: true,
frequency: Frequency.PUNCTUAL,
fromDate: '2023-08-28',
toDate: '2023-08-28',
schedule: [
{
day: 1,
time: '06:40',
margin: 900,
},
],
seatsProposed: 3,
seatsRequested: 1,
strict: true,
waypoints: [
{
lon: 6.389745,
lat: 48.32644,
},
{
lon: 6.984567,
lat: 48.021548,
},
],
algorithmType: 'PASSENGER_ORIENTED',
remoteness: 15000,
useProportion: true,
proportion: 0.3,
useAzimuth: true,
azimuthMargin: 10,
maxDetourDistanceRatio: 0.3,
maxDetourDurationRatio: 0.3,
},
},
}),
)
.mockImplementationOnce(() => {
throw new Error();
}),
save: jest.fn(),
};
@ -151,6 +302,10 @@ describe('Match Query Handler', () => {
matchQueryHandler = module.get<MatchQueryHandler>(MatchQueryHandler);
});
afterEach(async () => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(matchQueryHandler).toBeDefined();
});
@ -183,4 +338,64 @@ describe('Match Query Handler', () => {
expect(matching.id).toHaveLength(36);
expect(MatchingEntity.create).toHaveBeenCalledTimes(1);
});
it('should return a valid saved Matching', async () => {
jest.spyOn(MatchingEntity, 'create');
const matchQuery = new MatchQuery(
{
id: 'a3b10efb-121e-4d08-9198-9f57afdb5e2d',
algorithmType: AlgorithmType.PASSENGER_ORIENTED,
driver: false,
passenger: true,
frequency: Frequency.PUNCTUAL,
fromDate: '2023-08-28',
toDate: '2023-08-28',
schedule: [
{
time: '07:05',
day: 1,
margin: 900,
},
],
strict: false,
waypoints: [originWaypoint, destinationWaypoint],
},
mockRouteProvider,
);
const matching: MatchingResult = await matchQueryHandler.execute(
matchQuery,
);
expect(matching.id).toBe('a3b10efb-121e-4d08-9198-9f57afdb5e2d');
expect(MatchingEntity.create).toHaveBeenCalledTimes(0);
});
it('should return a new matching if saved Matching is not found', async () => {
jest.spyOn(MatchingEntity, 'create');
const matchQuery = new MatchQuery(
{
id: 'a3b10efb-121e-4d08-9198-9f57afdb5e2d',
algorithmType: AlgorithmType.PASSENGER_ORIENTED,
driver: false,
passenger: true,
frequency: Frequency.PUNCTUAL,
fromDate: '2023-08-28',
toDate: '2023-08-28',
schedule: [
{
time: '07:05',
day: 1,
margin: 900,
},
],
strict: false,
waypoints: [originWaypoint, destinationWaypoint],
},
mockRouteProvider,
);
const matching: MatchingResult = await matchQueryHandler.execute(
matchQuery,
);
expect(matching.id).toHaveLength(36);
expect(MatchingEntity.create).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,6 +1,7 @@
import { getRedisToken } from '@liaoliaots/nestjs-redis';
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { Target } from '@modules/ad/core/domain/candidate.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
import { MatchingNotFoundException } from '@modules/ad/core/domain/matching.errors';
import { MatchingRepository } from '@modules/ad/infrastructure/matching.repository';
@ -52,7 +53,7 @@ const matchingEntity: MatchingEntity = new MatchingEntity({
updatedAt: new Date(),
props: {
matches: [
{
MatchEntity.create({
adId: 'dd937edf-1264-4868-b073-d1952abe30b1',
role: Role.DRIVER,
frequency: Frequency.PUNCTUAL,
@ -60,10 +61,6 @@ const matchingEntity: MatchingEntity = new MatchingEntity({
duration: 12647,
initialDistance: 348745,
initialDuration: 12105,
distanceDetour: 7296,
durationDetour: 542,
distanceDetourPercentage: 4.1,
durationDetourPercentage: 3.8,
journeys: [
{
firstDate: new Date('2023-09-01'),
@ -91,7 +88,7 @@ const matchingEntity: MatchingEntity = new MatchingEntity({
},
],
// ...
},
}),
],
query: {
driver: false,

View File

@ -1,5 +1,6 @@
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { Target } from '@modules/ad/core/domain/candidate.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
import { MatchingMapper } from '@modules/ad/matching.mapper';
import { Test } from '@nestjs/testing';
@ -25,46 +26,88 @@ describe('Matching Mapper', () => {
updatedAt: new Date('2023-08-20T09:48:00Z'),
props: {
matches: [
{
adId: 'dd937edf-1264-4868-b073-d1952abe30b1',
role: Role.DRIVER,
frequency: Frequency.PUNCTUAL,
distance: 356041,
duration: 12647,
initialDistance: 348745,
initialDuration: 12105,
distanceDetour: 7296,
durationDetour: 542,
distanceDetourPercentage: 4.1,
durationDetourPercentage: 3.8,
journeys: [
{
firstDate: new Date('2023-09-01'),
lastDate: new Date('2023-09-01'),
journeyItems: [
{
lon: 6.35484,
lat: 48.26587,
duration: 0,
distance: 0,
actorTimes: [
{
role: Role.DRIVER,
target: Target.START,
firstDatetime: new Date('2023-09-01T07:00:00Z'),
firstMinDatetime: new Date('2023-09-01T06:45:00Z'),
firstMaxDatetime: new Date('2023-09-01T07:15:00Z'),
lastDatetime: new Date('2023-09-01T07:00:00Z'),
lastMinDatetime: new Date('2023-09-01T06:45:00Z'),
lastMaxDatetime: new Date('2023-09-01T07:15:00Z'),
},
],
},
],
},
],
// ...
},
new MatchEntity({
id: '4bd4e90b-ffba-4f5f-b904-48ad0667a1d7',
createdAt: new Date('2023-08-30T08:45:00Z'),
updatedAt: new Date('2023-08-30T08:45:00Z'),
props: {
adId: 'dd937edf-1264-4868-b073-d1952abe30b1',
role: Role.DRIVER,
frequency: Frequency.PUNCTUAL,
distance: 356041,
duration: 12647,
initialDistance: 348745,
initialDuration: 12105,
distanceDetour: 7296,
durationDetour: 542,
distanceDetourPercentage: 4.1,
durationDetourPercentage: 3.8,
journeys: [
{
firstDate: new Date('2023-09-01'),
lastDate: new Date('2023-09-01'),
journeyItems: [
{
lon: 6.389745,
lat: 48.32644,
duration: 0,
distance: 0,
actorTimes: [
{
role: Role.DRIVER,
target: Target.START,
firstDatetime: new Date('2023-09-01T07:00:00Z'),
firstMinDatetime: new Date('2023-09-01T06:45:00Z'),
firstMaxDatetime: new Date('2023-09-01T07:15:00Z'),
lastDatetime: new Date('2023-09-01T07:00:00Z'),
lastMinDatetime: new Date('2023-09-01T06:45:00Z'),
lastMaxDatetime: new Date('2023-09-01T07:15:00Z'),
},
{
role: Role.PASSENGER,
target: Target.START,
firstDatetime: new Date('2023-09-01T07:00:00Z'),
firstMinDatetime: new Date('2023-09-01T06:45:00Z'),
firstMaxDatetime: new Date('2023-09-01T07:15:00Z'),
lastDatetime: new Date('2023-09-01T07:00:00Z'),
lastMinDatetime: new Date('2023-09-01T06:45:00Z'),
lastMaxDatetime: new Date('2023-09-01T07:15:00Z'),
},
],
},
{
lon: 6.984567,
lat: 48.021548,
distance: 356041,
duration: 12647,
actorTimes: [
{
role: Role.DRIVER,
target: Target.FINISH,
firstDatetime: new Date('2023-09-01T07:00:00Z'),
firstMinDatetime: new Date('2023-09-01T06:45:00Z'),
firstMaxDatetime: new Date('2023-09-01T07:15:00Z'),
lastDatetime: new Date('2023-09-01T07:00:00Z'),
lastMinDatetime: new Date('2023-09-01T06:45:00Z'),
lastMaxDatetime: new Date('2023-09-01T07:15:00Z'),
},
{
role: Role.PASSENGER,
target: Target.FINISH,
firstDatetime: new Date('2023-09-01T07:00:00Z'),
firstMinDatetime: new Date('2023-09-01T06:45:00Z'),
firstMaxDatetime: new Date('2023-09-01T07:15:00Z'),
lastDatetime: new Date('2023-09-01T07:00:00Z'),
lastMinDatetime: new Date('2023-09-01T06:45:00Z'),
lastMaxDatetime: new Date('2023-09-01T07:15:00Z'),
},
],
},
],
},
],
},
}),
],
query: {
driver: false,
@ -105,14 +148,17 @@ describe('Matching Mapper', () => {
});
const mapped: string = matchingMapper.toPersistence(matchingEntity);
expect(mapped).toBe(
'{"_id":"644a7cb3-6436-4db5-850d-b4c7421d4b97","_createdAt":"2023-08-20T09:48:00.000Z","_updatedAt":"2023-08-20T09:48:00.000Z","props":{"matches":[{"adId":"dd937edf-1264-4868-b073-d1952abe30b1","role":"DRIVER","frequency":"PUNCTUAL","distance":356041,"duration":12647,"initialDistance":348745,"initialDuration":12105,"distanceDetour":7296,"durationDetour":542,"distanceDetourPercentage":4.1,"durationDetourPercentage":3.8,"journeys":[{"firstDate":"2023-09-01T00:00:00.000Z","lastDate":"2023-09-01T00:00:00.000Z","journeyItems":[{"lon":6.35484,"lat":48.26587,"duration":0,"distance":0,"actorTimes":[{"role":"DRIVER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]}]}]}],"query":{"driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-09-01","toDate":"2023-09-01","schedule":[{"day":5,"time":"06:40","margin":900}],"seatsProposed":3,"seatsRequested":1,"strict":true,"waypoints":[{"lon":6.389745,"lat":48.32644},{"lon":6.984567,"lat":48.021548}],"algorithmType":"PASSENGER_ORIENTED","remoteness":15000,"useProportion":true,"proportion":0.3,"useAzimuth":true,"azimuthMargin":10,"maxDetourDistanceRatio":0.3,"maxDetourDurationRatio":0.3}},"_domainEvents":[]}',
'{"id":"644a7cb3-6436-4db5-850d-b4c7421d4b97","createdAt":"2023-08-20T09:48:00.000Z","updatedAt":"2023-08-20T09:48:00.000Z","matches":[{"adId":"dd937edf-1264-4868-b073-d1952abe30b1","role":"DRIVER","frequency":"PUNCTUAL","distance":356041,"duration":12647,"initialDistance":348745,"initialDuration":12105,"distanceDetour":7296,"durationDetour":542,"distanceDetourPercentage":4.1,"durationDetourPercentage":3.8,"journeys":[{"firstDate":"2023-09-01T00:00:00.000Z","lastDate":"2023-09-01T00:00:00.000Z","journeyItems":[{"lon":6.389745,"lat":48.32644,"duration":0,"distance":0,"actorTimes":[{"role":"DRIVER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"},{"role":"PASSENGER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]},{"lon":6.984567,"lat":48.021548,"duration":12647,"distance":356041,"actorTimes":[{"role":"DRIVER","target":"FINISH","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"},{"role":"PASSENGER","target":"FINISH","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]}]}]}],"query":{"driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-09-01","toDate":"2023-09-01","schedule":[{"day":5,"time":"06:40","margin":900}],"seatsProposed":3,"seatsRequested":1,"strict":true,"waypoints":[{"lon":6.389745,"lat":48.32644},{"lon":6.984567,"lat":48.021548}],"algorithmType":"PASSENGER_ORIENTED","remoteness":15000,"useProportion":true,"proportion":0.3,"useAzimuth":true,"azimuthMargin":10,"maxDetourDistanceRatio":0.3,"maxDetourDurationRatio":0.3}}',
);
});
it('should map persisted string to domain entity', async () => {
const matchingEntity: MatchingEntity = matchingMapper.toDomain(
'{"_id":"644a7cb3-6436-4db5-850d-b4c7421d4b97","_createdAt":"2023-08-20T09:48:00.000Z","_updatedAt":"2023-08-20T09:48:00.000Z","props":{"matches":[{"adId":"dd937edf-1264-4868-b073-d1952abe30b1","role":"DRIVER","frequency":"PUNCTUAL","distance":356041,"duration":12647,"initialDistance":348745,"initialDuration":12105,"distanceDetour":7296,"durationDetour":542,"distanceDetourPercentage":4.1,"durationDetourPercentage":3.8,"journeys":[{"firstDate":"2023-09-01T00:00:00.000Z","lastDate":"2023-09-01T00:00:00.000Z","journeyItems":[{"lon":6.35484,"lat":48.26587,"duration":0,"distance":0,"actorTimes":[{"role":"DRIVER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]}]}]}],"query":{"driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-09-01","toDate":"2023-09-01","schedule":[{"day":5,"time":"06:40","margin":900}],"seatsProposed":3,"seatsRequested":1,"strict":true,"waypoints":[{"lon":6.389745,"lat":48.32644},{"lon":6.984567,"lat":48.021548}],"algorithmType":"PASSENGER_ORIENTED","remoteness":15000,"useProportion":true,"proportion":0.3,"useAzimuth":true,"azimuthMargin":10,"maxDetourDistanceRatio":0.3,"maxDetourDurationRatio":0.3}},"_domainEvents":[]}',
'{"id":"644a7cb3-6436-4db5-850d-b4c7421d4b97","createdAt":"2023-08-20T09:48:00.000Z","updatedAt":"2023-08-20T09:48:00.000Z","matches":[{"adId":"dd937edf-1264-4868-b073-d1952abe30b1","role":"DRIVER","frequency":"PUNCTUAL","distance":356041,"duration":12647,"initialDistance":348745,"initialDuration":12105,"distanceDetour":7296,"durationDetour":542,"distanceDetourPercentage":4.1,"durationDetourPercentage":3.8,"journeys":[{"firstDate":"2023-09-01T00:00:00.000Z","lastDate":"2023-09-01T00:00:00.000Z","journeyItems":[{"lon":6.389745,"lat":48.32644,"duration":0,"distance":0,"actorTimes":[{"role":"DRIVER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"},{"role":"PASSENGER","target":"START","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]},{"lon":6.984567,"lat":48.021548,"duration":12647,"distance":356041,"actorTimes":[{"role":"DRIVER","target":"FINISH","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"},{"role":"PASSENGER","target":"FINISH","firstDatetime":"2023-09-01T07:00:00.000Z","firstMinDatetime":"2023-09-01T06:45:00.000Z","firstMaxDatetime":"2023-09-01T07:15:00.000Z","lastDatetime":"2023-09-01T07:00:00.000Z","lastMinDatetime":"2023-09-01T06:45:00.000Z","lastMaxDatetime":"2023-09-01T07:15:00.000Z"}]}]}]}],"query":{"driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-09-01","toDate":"2023-09-01","schedule":[{"day":5,"time":"06:40","margin":900}],"seatsProposed":3,"seatsRequested":1,"strict":true,"waypoints":[{"lon":6.389745,"lat":48.32644},{"lon":6.984567,"lat":48.021548}],"algorithmType":"PASSENGER_ORIENTED","remoteness":15000,"useProportion":true,"proportion":0.3,"useAzimuth":true,"azimuthMargin":10,"maxDetourDistanceRatio":0.3,"maxDetourDurationRatio":0.3}}',
);
expect(matchingEntity.getProps().query.fromDate).toBe('2023-09-01');
expect(matchingEntity.getProps().matches[0].getProps().adId).toBe(
'dd937edf-1264-4868-b073-d1952abe30b1',
);
});
});