mirror of
https://gitlab.com/mobicoop/v3/service/matcher.git
synced 2026-01-01 08:22:41 +00:00
try to launch thousands of request in parallel
This commit is contained in:
@@ -2,13 +2,16 @@ import { Injectable } from '@nestjs/common';
|
||||
import { ICreateGeorouter } from '../../domain/interfaces/georouter-creator.interface';
|
||||
import { IGeorouter } from '../../domain/interfaces/georouter.interface';
|
||||
import { GraphhopperGeorouter } from './graphhopper-georouter';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
@Injectable()
|
||||
export class GeorouterCreator implements ICreateGeorouter {
|
||||
constructor(private readonly httpService: HttpService) {}
|
||||
|
||||
create(type: string, url: string): IGeorouter {
|
||||
switch (type) {
|
||||
case 'graphhopper':
|
||||
return new GraphhopperGeorouter(url);
|
||||
return new GraphhopperGeorouter(url, this.httpService);
|
||||
default:
|
||||
throw new Error('Unknown geocoder');
|
||||
}
|
||||
|
||||
@@ -1,15 +1,133 @@
|
||||
import { Route } from '../../domain/entities/route';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { NamedRoute } from '../../domain/entities/named-route';
|
||||
import { IGeorouter } from '../../domain/interfaces/georouter.interface';
|
||||
import { GeorouterSettings } from '../../domain/types/georouter-settings.type';
|
||||
import { Path } from '../../domain/types/path.type';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
catchError,
|
||||
defer,
|
||||
forkJoin,
|
||||
from,
|
||||
lastValueFrom,
|
||||
map,
|
||||
mergeAll,
|
||||
mergeMap,
|
||||
toArray,
|
||||
} from 'rxjs';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
@Injectable()
|
||||
export class GraphhopperGeorouter implements IGeorouter {
|
||||
_url: string;
|
||||
_urlArgs: Array<string>;
|
||||
_withTime: boolean;
|
||||
_withPoints: boolean;
|
||||
_withDistance: boolean;
|
||||
_paths: Array<Path>;
|
||||
_httpService: HttpService;
|
||||
|
||||
constructor(url: string) {
|
||||
constructor(url: string, httpService: HttpService) {
|
||||
this._url = url + '/route?';
|
||||
this._httpService = httpService;
|
||||
}
|
||||
|
||||
route(routesRequested: [], settings: GeorouterSettings): Route[] {
|
||||
throw new Error('Method not implemented.');
|
||||
async route(
|
||||
paths: Array<Path>,
|
||||
settings: GeorouterSettings,
|
||||
): Promise<Array<NamedRoute>> {
|
||||
this._setDefaultUrlArgs();
|
||||
this._setWithTime(settings.withTime);
|
||||
this._setWithPoints(settings.withPoints);
|
||||
this._setWithDistance(settings.withDistance);
|
||||
this._paths = paths;
|
||||
const routes = await this._getRoutes();
|
||||
console.log(routes.length);
|
||||
return routes;
|
||||
}
|
||||
|
||||
_setDefaultUrlArgs(): void {
|
||||
this._urlArgs = [
|
||||
'vehicle=car',
|
||||
'weighting=fastest',
|
||||
'points_encoded=false',
|
||||
];
|
||||
}
|
||||
|
||||
_setWithTime(withTime: boolean): void {
|
||||
this._withTime = withTime;
|
||||
if (withTime) {
|
||||
this._urlArgs.push('details=time');
|
||||
}
|
||||
}
|
||||
|
||||
_setWithPoints(withPoints: boolean): void {
|
||||
this._withPoints = withPoints;
|
||||
if (withPoints) {
|
||||
this._urlArgs.push('calc_points=false');
|
||||
}
|
||||
}
|
||||
|
||||
_setWithDistance(withDistance: boolean): void {
|
||||
this._withDistance = withDistance;
|
||||
if (withDistance) {
|
||||
this._urlArgs.push('instructions=true');
|
||||
} else {
|
||||
this._urlArgs.push('instructions=false');
|
||||
}
|
||||
}
|
||||
|
||||
async _getRoutes(): Promise<Array<NamedRoute>> {
|
||||
const routes = Promise.all(
|
||||
this._paths.map(async (path) => {
|
||||
const url: string = [
|
||||
this._getUrl(),
|
||||
'&point=',
|
||||
path.points
|
||||
.map((point) => [point.lat, point.lon].join())
|
||||
.join('&point='),
|
||||
].join('');
|
||||
const res = await lastValueFrom(
|
||||
this._httpService.get(url).pipe(
|
||||
map((res) => res.data.paths[0].distance),
|
||||
catchError((error: AxiosError) => {
|
||||
throw new Error(error.message);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return <NamedRoute>{
|
||||
key: path.key,
|
||||
route: res,
|
||||
};
|
||||
}),
|
||||
);
|
||||
return routes;
|
||||
// const date1 = new Date();
|
||||
// const urls = this._paths.map((path) =>
|
||||
// defer(() =>
|
||||
// this._httpService
|
||||
// .get(
|
||||
// [
|
||||
// this._getUrl(),
|
||||
// '&point=',
|
||||
// path.points
|
||||
// .map((point) => [point.lat, point.lon].join())
|
||||
// .join('&point='),
|
||||
// ].join(''),
|
||||
// )
|
||||
// .pipe(map((res) => res.data.paths[0].distance)),
|
||||
// ),
|
||||
// );
|
||||
// const observables = from(urls);
|
||||
// const routes = observables.pipe(mergeAll(7), toArray());
|
||||
// routes.subscribe(() => {
|
||||
// const date2 = new Date();
|
||||
// console.log(date2.getTime() - date1.getTime());
|
||||
// });
|
||||
// return [];
|
||||
}
|
||||
|
||||
_getUrl(): string {
|
||||
return [this._url, this._urlArgs.join('&')].join('');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { MatcherException } from '../../exceptions/matcher.exception';
|
||||
import { IRequestGeography } from '../interfaces/geography-request.interface';
|
||||
import { PointType } from '../types/geography.enum';
|
||||
import { Point } from '../types/point.type';
|
||||
import { Route } from './route';
|
||||
import { find } from 'geo-tz';
|
||||
import { Waypoint } from '../types/waypoint';
|
||||
import { Route } from './route';
|
||||
|
||||
export class Geography {
|
||||
_geographyRequest: IRequestGeography;
|
||||
|
||||
6
src/modules/matcher/domain/entities/named-route.ts
Normal file
6
src/modules/matcher/domain/entities/named-route.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Route } from './route';
|
||||
|
||||
export class NamedRoute {
|
||||
key: string;
|
||||
route: Route;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Route } from '../entities/route';
|
||||
import { NamedRoute } from '../entities/named-route';
|
||||
import { GeorouterSettings } from '../types/georouter-settings.type';
|
||||
import { Path } from '../types/path.type';
|
||||
|
||||
export interface IGeorouter {
|
||||
route(routesRequested: [], settings: GeorouterSettings): Array<Route>;
|
||||
route(
|
||||
paths: Array<Path>,
|
||||
settings: GeorouterSettings,
|
||||
): Promise<Array<NamedRoute>>;
|
||||
}
|
||||
|
||||
6
src/modules/matcher/domain/types/path.type.ts
Normal file
6
src/modules/matcher/domain/types/path.type.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Point } from './point.type';
|
||||
|
||||
export type Path = {
|
||||
key: string;
|
||||
points: Array<Point>;
|
||||
};
|
||||
@@ -17,21 +17,54 @@ export class MatchUseCase {
|
||||
|
||||
async execute(matchQuery: MatchQuery): Promise<ICollection<Match>> {
|
||||
try {
|
||||
const paths = [];
|
||||
for (let i = 0; i < 2000; i++) {
|
||||
paths.push({
|
||||
key: 'route' + i,
|
||||
points: [
|
||||
{
|
||||
lat: 48.110899,
|
||||
lon: -1.68365,
|
||||
},
|
||||
{
|
||||
lat: 48.131105,
|
||||
lon: -1.690067,
|
||||
},
|
||||
{
|
||||
lat: 48.56516,
|
||||
lon: -1.923553,
|
||||
},
|
||||
{
|
||||
lat: 48.622813,
|
||||
lon: -1.997177,
|
||||
},
|
||||
{
|
||||
lat: 48.67846,
|
||||
lon: -1.8554,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
const routes = await matchQuery.algorithmSettings.georouter.route(paths, {
|
||||
withDistance: true,
|
||||
withPoints: true,
|
||||
withTime: false,
|
||||
});
|
||||
const match = new Match();
|
||||
match.uuid = 'e23f9725-2c19-49a0-9ef6-17d8b9a5ec85';
|
||||
this._messager.publish('matcher.match', 'match !');
|
||||
// this._messager.publish('matcher.match', 'match !');
|
||||
return {
|
||||
data: [match],
|
||||
total: 1,
|
||||
};
|
||||
} catch (error) {
|
||||
this._messager.publish(
|
||||
'logging.matcher.match.crit',
|
||||
JSON.stringify({
|
||||
matchQuery,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
// this._messager.publish(
|
||||
// 'logging.matcher.match.crit',
|
||||
// JSON.stringify({
|
||||
// matchQuery,
|
||||
// error,
|
||||
// }),
|
||||
// );
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import { RedisClientOptions } from '@liaoliaots/nestjs-redis';
|
||||
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||
import { DefaultParamsProvider } from './adapters/secondaries/default-params.provider';
|
||||
import { GeorouterCreator } from './adapters/secondaries/georouter-creator';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
DatabaseModule,
|
||||
CqrsModule,
|
||||
HttpModule,
|
||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { GeorouterCreator } from '../../adapters/secondaries/georouter-creator';
|
||||
import { GraphhopperGeorouter } from '../../adapters/secondaries/graphhopper-georouter';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
const mockHttpService = jest.fn();
|
||||
|
||||
describe('Georouter creator', () => {
|
||||
let georouterCreator: GeorouterCreator;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
GeorouterCreator,
|
||||
{
|
||||
provide: HttpService,
|
||||
useValue: mockHttpService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
georouterCreator = module.get<GeorouterCreator>(GeorouterCreator);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
const georouterCreator: GeorouterCreator = new GeorouterCreator();
|
||||
expect(georouterCreator).toBeDefined();
|
||||
});
|
||||
it('should create a graphhopper georouter', () => {
|
||||
const georouterCreator: GeorouterCreator = new GeorouterCreator();
|
||||
const georouter = georouterCreator.create(
|
||||
'graphhopper',
|
||||
'http://localhost',
|
||||
@@ -15,7 +34,6 @@ describe('Georouter creator', () => {
|
||||
expect(georouter).toBeInstanceOf(GraphhopperGeorouter);
|
||||
});
|
||||
it('should throw an exception if georouter type is unknown', () => {
|
||||
const georouterCreator: GeorouterCreator = new GeorouterCreator();
|
||||
expect(() =>
|
||||
georouterCreator.create('unknown', 'http://localhost'),
|
||||
).toThrow();
|
||||
|
||||
@@ -1,22 +1,141 @@
|
||||
import { GraphhopperGeorouter } from '../../adapters/secondaries/graphhopper-georouter';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { NamedRoute } from '../../domain/entities/named-route';
|
||||
import { GeorouterCreator } from '../../adapters/secondaries/georouter-creator';
|
||||
import { IGeorouter } from '../../domain/interfaces/georouter.interface';
|
||||
import { of } from 'rxjs';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
const mockHttpService = {
|
||||
get: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
throw new AxiosError('Axios error !');
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
return of({
|
||||
status: 200,
|
||||
data: [new NamedRoute()],
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
describe('Graphhopper Georouter', () => {
|
||||
it('should be defined', () => {
|
||||
const graphhopperGeorouter: GraphhopperGeorouter = new GraphhopperGeorouter(
|
||||
let georouterCreator: GeorouterCreator;
|
||||
let graphhopperGeorouter: IGeorouter;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
GeorouterCreator,
|
||||
{
|
||||
provide: HttpService,
|
||||
useValue: mockHttpService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
georouterCreator = module.get<GeorouterCreator>(GeorouterCreator);
|
||||
graphhopperGeorouter = georouterCreator.create(
|
||||
'graphhopper',
|
||||
'http://localhost',
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(graphhopperGeorouter).toBeDefined();
|
||||
});
|
||||
it('should throw an exception when calling route', () => {
|
||||
const graphhopperGeorouter: GraphhopperGeorouter = new GraphhopperGeorouter(
|
||||
'http://localhost',
|
||||
);
|
||||
expect(() =>
|
||||
graphhopperGeorouter.route([], {
|
||||
withDistance: false,
|
||||
withPoints: false,
|
||||
withTime: false,
|
||||
}),
|
||||
).toThrow();
|
||||
|
||||
describe('route function', () => {
|
||||
it('should fail on axios error', async () => {
|
||||
await expect(
|
||||
graphhopperGeorouter.route(
|
||||
[
|
||||
{
|
||||
key: 'route1',
|
||||
points: [
|
||||
{
|
||||
lat: 49.440041,
|
||||
lon: 1.093912,
|
||||
},
|
||||
{
|
||||
lat: 50.630992,
|
||||
lon: 3.045432,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
withDistance: false,
|
||||
withPoints: false,
|
||||
withTime: false,
|
||||
},
|
||||
),
|
||||
).rejects.toBeInstanceOf(Error);
|
||||
});
|
||||
it('should create one route with all settings to false', async () => {
|
||||
const routes = await graphhopperGeorouter.route(
|
||||
[
|
||||
{
|
||||
key: 'route1',
|
||||
points: [
|
||||
{
|
||||
lat: 49.440041,
|
||||
lon: 1.093912,
|
||||
},
|
||||
{
|
||||
lat: 50.630992,
|
||||
lon: 3.045432,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
withDistance: false,
|
||||
withPoints: false,
|
||||
withTime: false,
|
||||
},
|
||||
);
|
||||
expect(routes).toHaveLength(1);
|
||||
});
|
||||
it('should create 2 routes with distance, points and time', async () => {
|
||||
const routes = await graphhopperGeorouter.route(
|
||||
[
|
||||
{
|
||||
key: 'route1',
|
||||
points: [
|
||||
{
|
||||
lat: 49.440041,
|
||||
lon: 1.093912,
|
||||
},
|
||||
{
|
||||
lat: 50.630992,
|
||||
lon: 3.045432,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'route2',
|
||||
points: [
|
||||
{
|
||||
lat: 49.440041,
|
||||
lon: 1.093912,
|
||||
},
|
||||
{
|
||||
lat: 50.630992,
|
||||
lon: 3.045432,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
withDistance: true,
|
||||
withPoints: true,
|
||||
withTime: true,
|
||||
},
|
||||
);
|
||||
expect(routes).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user