Add support for partial PIS matches by skipping first n stops
Signed-off-by: Fred Boniface <fred@fjla.uk>
This commit is contained in:
parent
820c8b0dd4
commit
e01cdd2f7e
@ -33,7 +33,7 @@
|
||||
"zlib": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@owlboard/ts-types": "^0.1.0",
|
||||
"@owlboard/ts-types": "^0.1.2",
|
||||
"@types/jest": "^29.5.3",
|
||||
"eslint": "^8.39.0",
|
||||
"jest": "^29.6.2",
|
||||
|
@ -48,13 +48,13 @@ async function random(req, res, next) {
|
||||
}
|
||||
|
||||
async function testingTiplocArray(req, res, next) {
|
||||
let array = JSON.parse(req.params.array)
|
||||
return pis.findByTiplocArray(array)
|
||||
let array = JSON.parse(req.params.array);
|
||||
res.json(await pis.findByTiplocArray(array));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
byStartEndCRS,
|
||||
byCode,
|
||||
random,
|
||||
testingTiplocArray
|
||||
testingTiplocArray,
|
||||
};
|
||||
|
@ -5,9 +5,9 @@ module.exports = async function authCheck(req, res, next) {
|
||||
//log.out("authMiddlewares: Checking authentication", "dbug");
|
||||
logger.logger.debug("Auth check starting");
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
logger.logger.warn("DEVELOPMENT MODE - AUTHENTICATION DISABLED")
|
||||
logger.logger.warn("DEVELOPMENT MODE - AUTHENTICATION DISABLED");
|
||||
res.isAuthed = true;
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
var uuid = req.headers.uuid;
|
||||
|
@ -3,8 +3,15 @@
|
||||
const db = require("../services/dbAccess.services");
|
||||
const clean = require("../utils/sanitizer.utils");
|
||||
|
||||
import type { OB_Pis_SimpleObject } from "@owlboard/ts-types";
|
||||
|
||||
import { logger } from "../utils/logger.utils";
|
||||
import { queryAggregate } from "./dbAccess.services";
|
||||
import {
|
||||
getPartialEndTiplocMatchPipeline,
|
||||
getFullTiplocMatchPipeline,
|
||||
} from "../utils/pis.utils";
|
||||
import { Document } from "mongodb";
|
||||
|
||||
const supported = ["GW", "UK"];
|
||||
|
||||
@ -33,7 +40,9 @@ async function findPisByOrigDest(start: string, end: string) {
|
||||
return search;
|
||||
}
|
||||
|
||||
async function findPisByCode(code: string) {
|
||||
async function findPisByCode(
|
||||
code: string
|
||||
): Promise<OB_Pis_SimpleObject | null> {
|
||||
logger.debug(`pisServices.findPisByCode: Searching for PIS code: ${code}`);
|
||||
const cleanCode = clean.removeNonNumeric(code);
|
||||
const query = {
|
||||
@ -43,75 +52,73 @@ async function findPisByCode(code: string) {
|
||||
return await search;
|
||||
}
|
||||
|
||||
export async function findByTiplocArray(tiplocArray: string[]) {
|
||||
// If any of the TIPLOCS in an array is RDG4AB - Replace it with READING.
|
||||
// ^^ Double check those though before doing it.
|
||||
const query = {
|
||||
tiplocs: tiplocArray,
|
||||
};
|
||||
const res = await db.query("pis", query);
|
||||
// If res is empty then try to find partial match
|
||||
if (res.length) {
|
||||
return res;
|
||||
// Queries the database for PIS codes that match the given TIPLOC array
|
||||
export async function findByTiplocArray(
|
||||
tiplocArray: string[]
|
||||
): Promise<OB_Pis_SimpleObject | null> {
|
||||
// Firstly fix errant TIPLOCS such as RDNG4AB which will never return a result
|
||||
// currently only works with RDNG4AB - checks for presence of RDNG4AB first to
|
||||
// avoid iterating over every array ever searched for.
|
||||
if (tiplocArray.includes("RDNG4AB")) {
|
||||
fixErrantTiplocs(tiplocArray);
|
||||
}
|
||||
|
||||
// PERFORMANCE NOTE:
|
||||
// The majority of queries will return a full match,
|
||||
// this means that a more performant pipeline is used
|
||||
// to find a full match and only then are more
|
||||
// complicated pipelines used to find partial matches
|
||||
// if the first pipeline returns nothing.
|
||||
try {
|
||||
const exactMatch = await findExactMatchByTiploc(tiplocArray);
|
||||
if (exactMatch) {
|
||||
return convertDocument(exactMatch, "none");
|
||||
} else {
|
||||
let partial = await findPartialMatchByTiploc(tiplocArray);
|
||||
return partial;
|
||||
const partialEnd = await findPartialEndMatchByTiploc(tiplocArray);
|
||||
if (partialEnd) {
|
||||
return convertDocument(partialEnd, "first");
|
||||
} else {
|
||||
// Here, I should search for a partialStart match. For now return null.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err, "Error in findByTiplocArray");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function findPartialMatchByTiploc(tiplocArray: string[]) {
|
||||
// Do some magic here, similar to findPisByOrigDest but
|
||||
// ensuring that the stops in the array match the last
|
||||
// x number of items in the array.
|
||||
// Uses a pipeline to find an exact match
|
||||
async function findExactMatchByTiploc(array: string[]): Promise<Document> {
|
||||
const pipeline = getFullTiplocMatchPipeline(array);
|
||||
const res = await queryAggregate("pis", pipeline);
|
||||
return res[0];
|
||||
}
|
||||
|
||||
const pipeline = [
|
||||
{
|
||||
$addFields: {
|
||||
reversedTiplocs: {
|
||||
$reverseArray: "$tiplocs",
|
||||
},
|
||||
reversedQueryArray: {
|
||||
$reverseArray: tiplocArray,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$gt: [
|
||||
{
|
||||
$indexOfArray: [
|
||||
"$reversedTiplocs",
|
||||
{
|
||||
$arrayElemAt: ["$reversedQueryArray", 0],
|
||||
// Uses a pipeline to find a partial match - only supporting
|
||||
// codes ending with the correct stops for now.
|
||||
async function findPartialEndMatchByTiploc(array: string[]): Promise<Document> {
|
||||
const pipeline = getPartialEndTiplocMatchPipeline(array);
|
||||
const res = await queryAggregate("pis", pipeline);
|
||||
return res[0];
|
||||
}
|
||||
|
||||
function convertDocument(doc: Document, skipType: string): OB_Pis_SimpleObject {
|
||||
return {
|
||||
code: doc.code,
|
||||
toc: doc.toc,
|
||||
skipCount: doc.skipStops,
|
||||
skipType: skipType,
|
||||
};
|
||||
}
|
||||
|
||||
// Changes any instance of 'RDNG4AB' to 'RDNGSTN'
|
||||
function fixErrantTiplocs(input: string[]): void {
|
||||
input.forEach((value, index, array) => {
|
||||
if (value === "RDNG4AB") {
|
||||
array[index] = "RDNGSTN";
|
||||
}
|
||||
],
|
||||
},
|
||||
-1,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
code: 1,
|
||||
skipStops: {
|
||||
$subtract: [
|
||||
{
|
||||
$size: "$tiplocs",
|
||||
},
|
||||
{
|
||||
$size: tiplocArray,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const res = await queryAggregate('pis', pipeline)
|
||||
console.log(JSON.stringify(res))
|
||||
return res
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
102
src/utils/pis.utils.ts
Normal file
102
src/utils/pis.utils.ts
Normal file
@ -0,0 +1,102 @@
|
||||
export function getPartialEndTiplocMatchPipeline(query: string[]) {
|
||||
return [
|
||||
{
|
||||
$match: {
|
||||
tiplocs: {
|
||||
$all: query,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
reversedTiplocs: {
|
||||
$reverseArray: "$tiplocs",
|
||||
},
|
||||
query: {
|
||||
$literal: query,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
reversedQuery: {
|
||||
$reverseArray: "$query",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$eq: [
|
||||
{
|
||||
$slice: [
|
||||
"$reversedTiplocs",
|
||||
0,
|
||||
{
|
||||
$size: "$reversedQuery",
|
||||
},
|
||||
],
|
||||
},
|
||||
"$reversedQuery",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
skipStops: {
|
||||
$subtract: [
|
||||
{
|
||||
$size: "$tiplocs",
|
||||
},
|
||||
{
|
||||
$size: "$reversedQuery",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
skipStops: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
$limit: 1,
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
code: 1,
|
||||
skipStops: 1,
|
||||
toc: 1,
|
||||
_id: 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getFullTiplocMatchPipeline(query: string[]) {
|
||||
return [
|
||||
{
|
||||
$match: {
|
||||
tiplocs: query,
|
||||
},
|
||||
},
|
||||
{
|
||||
$limit: 1,
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
skipStops: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
code: 1,
|
||||
toc: 1,
|
||||
skipStops: 1,
|
||||
_id: 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue
Block a user