passenger oriented geo filter

This commit is contained in:
sbriat 2023-09-18 16:41:46 +02:00
parent 067854b697
commit 4e118603f3
7 changed files with 303 additions and 60 deletions

View File

@ -3,5 +3,7 @@ import { Filter } from './filter.abstract';
export class PassengerOrientedGeoFilter extends Filter {
filter = async (candidates: CandidateEntity[]): Promise<CandidateEntity[]> =>
candidates;
candidates.filter((candidate: CandidateEntity) =>
candidate.isDetourValid(),
);
}

View File

@ -60,6 +60,12 @@ export class PassengerOrientedSelector extends Selector {
adsRole.role == Role.PASSENGER
? (adEntity.getProps().driverDuration as number)
: (this.query.driverRoute?.duration as number),
spacetimeDetourRatio: {
maxDistanceDetourRatio: this.query
.maxDetourDistanceRatio as number,
maxDurationDetourRatio: this.query
.maxDetourDurationRatio as number,
},
}),
),
)

View File

@ -10,15 +10,34 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
return new CandidateEntity({ id: create.id, props });
};
setCarpoolPath = (waySteps: WayStepProps[]): void => {
setCarpoolPath = (waySteps: WayStepProps[]): CandidateEntity => {
this.props.carpoolSteps = waySteps;
return this;
};
setMetrics = (distance: number, duration: number): void => {
setMetrics = (distance: number, duration: number): CandidateEntity => {
this.props.distance = distance;
this.props.duration = duration;
return this;
};
isDetourValid = (): boolean =>
this._validateDistanceDetour() && this._validateDurationDetour();
private _validateDurationDetour = (): boolean =>
this.props.duration
? this.props.duration <=
this.props.driverDuration *
(1 + this.props.spacetimeDetourRatio.maxDurationDetourRatio)
: false;
private _validateDistanceDetour = (): boolean =>
this.props.distance
? this.props.distance <=
this.props.driverDistance *
(1 + this.props.spacetimeDetourRatio.maxDistanceDetourRatio)
: false;
validate(): void {
// entity business rules validation to protect it's invariant before saving entity to a database
}

View File

@ -12,6 +12,7 @@ export interface CandidateProps {
carpoolSteps?: WayStepProps[]; // carpool path for the crew (driver + passenger)
distance?: number;
duration?: number;
spacetimeDetourRatio: SpacetimeDetourRatio;
}
// Properties that are needed for a Candidate creation
@ -22,6 +23,7 @@ export interface CreateCandidateProps {
driverDuration: number;
driverWaypoints: PointProps[];
passengerWaypoints: PointProps[];
spacetimeDetourRatio: SpacetimeDetourRatio;
}
export enum Target {
@ -30,3 +32,17 @@ export enum Target {
FINISH = 'FINISH',
NEUTRAL = 'NEUTRAL',
}
export abstract class Validator {
abstract validate(): boolean;
}
export type SpacetimeMetric = {
distance: number;
duration: number;
};
export type SpacetimeDetourRatio = {
maxDistanceDetourRatio: number;
maxDurationDetourRatio: number;
};

View File

@ -0,0 +1,207 @@
import { Role } from '@modules/ad/core/domain/ad.types';
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
import { Target } from '@modules/ad/core/domain/candidate.types';
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
describe('Candidate entity', () => {
it('should create a new candidate entity', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
});
expect(candidateEntity.id.length).toBe(36);
});
it('should set a candidate entity carpool path', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
driverWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}).setCarpoolPath([
{
point: new Point({
lat: 48.689445,
lon: 6.17651,
}),
actors: [
new Actor({
role: Role.DRIVER,
target: Target.START,
}),
new Actor({
role: Role.PASSENGER,
target: Target.START,
}),
],
},
{
point: new Point({
lat: 48.8566,
lon: 2.3522,
}),
actors: [
new Actor({
role: Role.DRIVER,
target: Target.FINISH,
}),
new Actor({
role: Role.PASSENGER,
target: Target.FINISH,
}),
],
},
]);
expect(candidateEntity.getProps().carpoolSteps).toHaveLength(2);
});
it('should create a new candidate entity with spacetime metrics', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}).setMetrics(352688, 14587);
expect(candidateEntity.getProps().distance).toBe(352688);
expect(candidateEntity.getProps().duration).toBe(14587);
});
it('should not validate a candidate entity with exceeding distance detour', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.849445,
lon: 6.68651,
},
{
lat: 47.18746,
lon: 2.89742,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}).setMetrics(458690, 13980);
expect(candidateEntity.isDetourValid()).toBeFalsy();
});
it('should not validate a candidate entity with exceeding duration detour', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.849445,
lon: 6.68651,
},
{
lat: 47.18746,
lon: 2.89742,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}).setMetrics(352368, 18314);
expect(candidateEntity.isDetourValid()).toBeFalsy();
});
});

View File

@ -71,6 +71,10 @@ const candidates: CandidateEntity[] = [
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}),
CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
@ -97,6 +101,10 @@ const candidates: CandidateEntity[] = [
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
}),
];

View File

@ -45,67 +45,52 @@ const matchQuery = new MatchQuery(
},
);
const candidates: CandidateEntity[] = [
CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
driverWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
}),
];
const candidate: CandidateEntity = CandidateEntity.create({
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
role: Role.DRIVER,
driverWaypoints: [
{
lat: 48.678454,
lon: 6.189745,
},
{
lat: 48.84877,
lon: 2.398457,
},
],
passengerWaypoints: [
{
lat: 48.689445,
lon: 6.17651,
},
{
lat: 48.8566,
lon: 2.3522,
},
],
driverDistance: 350145,
driverDuration: 13548,
spacetimeDetourRatio: {
maxDistanceDetourRatio: 0.3,
maxDurationDetourRatio: 0.3,
},
});
describe('Passenger oriented geo filter', () => {
it('should filter candidates', async () => {
it('should not filter valid candidates', async () => {
const passengerOrientedGeoFilter: PassengerOrientedGeoFilter =
new PassengerOrientedGeoFilter(matchQuery);
candidate.isDetourValid = () => true;
const filteredCandidates: CandidateEntity[] =
await passengerOrientedGeoFilter.filter(candidates);
expect(filteredCandidates.length).toBe(2);
await passengerOrientedGeoFilter.filter([candidate]);
expect(filteredCandidates.length).toBe(1);
});
it('should filter invalid candidates', async () => {
const passengerOrientedGeoFilter: PassengerOrientedGeoFilter =
new PassengerOrientedGeoFilter(matchQuery);
candidate.isDetourValid = () => false;
const filteredCandidates: CandidateEntity[] =
await passengerOrientedGeoFilter.filter([candidate]);
expect(filteredCandidates.length).toBe(0);
});
});