Partial implementation of: #47 The 2022.2.1 release currently live is based off of the TimetableAPI-Upgrade branch. Partial PIS code matching is now implemented in cases where x number of stops need skipping at the start of a route. Reviewed-on: #64
131 lines
3.9 KiB
TypeScript
131 lines
3.9 KiB
TypeScript
// Finds PIS Codes using DB Lookups
|
|
|
|
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";
|
|
|
|
export const supported = ["GW", "UK"];
|
|
|
|
async function findPisByOrigDest(start: string, end: string) {
|
|
logger.debug(
|
|
`pisServices.findPisByOrigDest: Searching for Orig: ${start}, Dest: ${end}`
|
|
);
|
|
const firstCrs = clean.cleanApiEndpointTxt(start.toLowerCase());
|
|
const lastCrs = clean.cleanApiEndpointTxt(end.toLowerCase());
|
|
const query = {
|
|
stops: {
|
|
$all: [
|
|
{ $elemMatch: { $eq: firstCrs } },
|
|
{ $elemMatch: { $eq: lastCrs } },
|
|
],
|
|
},
|
|
$expr: {
|
|
$and: [
|
|
{ $eq: [{ $arrayElemAt: ["$stops", -1] }, lastCrs] },
|
|
{ $eq: [{ $arrayElemAt: ["$stops", 0] }, firstCrs] },
|
|
],
|
|
},
|
|
};
|
|
const search = await db.query("pis", query);
|
|
// Check for results, if none then try partial match
|
|
return search;
|
|
}
|
|
|
|
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 = {
|
|
code: parseInt(cleanCode),
|
|
};
|
|
const search = db.query("pis", query);
|
|
return await search;
|
|
}
|
|
|
|
// 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 {
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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];
|
|
}
|
|
|
|
// 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.toString(),
|
|
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";
|
|
}
|
|
// Additional substitutions can be applied here
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
supported,
|
|
findPisByOrigDest,
|
|
findPisByCode,
|
|
findByTiplocArray,
|
|
};
|