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"
|
"zlib": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@owlboard/ts-types": "^0.1.0",
|
"@owlboard/ts-types": "^0.1.2",
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.3",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"jest": "^29.6.2",
|
"jest": "^29.6.2",
|
||||||
|
@ -48,13 +48,13 @@ async function random(req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testingTiplocArray(req, res, next) {
|
async function testingTiplocArray(req, res, next) {
|
||||||
let array = JSON.parse(req.params.array)
|
let array = JSON.parse(req.params.array);
|
||||||
return pis.findByTiplocArray(array)
|
res.json(await pis.findByTiplocArray(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
byStartEndCRS,
|
byStartEndCRS,
|
||||||
byCode,
|
byCode,
|
||||||
random,
|
random,
|
||||||
testingTiplocArray
|
testingTiplocArray,
|
||||||
};
|
};
|
||||||
|
@ -5,9 +5,9 @@ module.exports = async function authCheck(req, res, next) {
|
|||||||
//log.out("authMiddlewares: Checking authentication", "dbug");
|
//log.out("authMiddlewares: Checking authentication", "dbug");
|
||||||
logger.logger.debug("Auth check starting");
|
logger.logger.debug("Auth check starting");
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
logger.logger.warn("DEVELOPMENT MODE - AUTHENTICATION DISABLED")
|
logger.logger.warn("DEVELOPMENT MODE - AUTHENTICATION DISABLED");
|
||||||
res.isAuthed = true;
|
res.isAuthed = true;
|
||||||
return next()
|
return next();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var uuid = req.headers.uuid;
|
var uuid = req.headers.uuid;
|
||||||
|
@ -3,8 +3,15 @@
|
|||||||
const db = require("../services/dbAccess.services");
|
const db = require("../services/dbAccess.services");
|
||||||
const clean = require("../utils/sanitizer.utils");
|
const clean = require("../utils/sanitizer.utils");
|
||||||
|
|
||||||
|
import type { OB_Pis_SimpleObject } from "@owlboard/ts-types";
|
||||||
|
|
||||||
import { logger } from "../utils/logger.utils";
|
import { logger } from "../utils/logger.utils";
|
||||||
import { queryAggregate } from "./dbAccess.services";
|
import { queryAggregate } from "./dbAccess.services";
|
||||||
|
import {
|
||||||
|
getPartialEndTiplocMatchPipeline,
|
||||||
|
getFullTiplocMatchPipeline,
|
||||||
|
} from "../utils/pis.utils";
|
||||||
|
import { Document } from "mongodb";
|
||||||
|
|
||||||
const supported = ["GW", "UK"];
|
const supported = ["GW", "UK"];
|
||||||
|
|
||||||
@ -33,7 +40,9 @@ async function findPisByOrigDest(start: string, end: string) {
|
|||||||
return search;
|
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}`);
|
logger.debug(`pisServices.findPisByCode: Searching for PIS code: ${code}`);
|
||||||
const cleanCode = clean.removeNonNumeric(code);
|
const cleanCode = clean.removeNonNumeric(code);
|
||||||
const query = {
|
const query = {
|
||||||
@ -43,75 +52,73 @@ async function findPisByCode(code: string) {
|
|||||||
return await search;
|
return await search;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findByTiplocArray(tiplocArray: string[]) {
|
// Queries the database for PIS codes that match the given TIPLOC array
|
||||||
// If any of the TIPLOCS in an array is RDG4AB - Replace it with READING.
|
export async function findByTiplocArray(
|
||||||
// ^^ Double check those though before doing it.
|
tiplocArray: string[]
|
||||||
const query = {
|
): Promise<OB_Pis_SimpleObject | null> {
|
||||||
tiplocs: tiplocArray,
|
// 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
|
||||||
const res = await db.query("pis", query);
|
// avoid iterating over every array ever searched for.
|
||||||
// If res is empty then try to find partial match
|
if (tiplocArray.includes("RDNG4AB")) {
|
||||||
if (res.length) {
|
fixErrantTiplocs(tiplocArray);
|
||||||
return res;
|
}
|
||||||
} else {
|
|
||||||
let partial = await findPartialMatchByTiploc(tiplocArray);
|
// PERFORMANCE NOTE:
|
||||||
return partial;
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findPartialMatchByTiploc(tiplocArray: string[]) {
|
// Uses a pipeline to find an exact match
|
||||||
// Do some magic here, similar to findPisByOrigDest but
|
async function findExactMatchByTiploc(array: string[]): Promise<Document> {
|
||||||
// ensuring that the stops in the array match the last
|
const pipeline = getFullTiplocMatchPipeline(array);
|
||||||
// x number of items in the array.
|
const res = await queryAggregate("pis", pipeline);
|
||||||
|
return res[0];
|
||||||
|
}
|
||||||
|
|
||||||
const pipeline = [
|
// Uses a pipeline to find a partial match - only supporting
|
||||||
{
|
// codes ending with the correct stops for now.
|
||||||
$addFields: {
|
async function findPartialEndMatchByTiploc(array: string[]): Promise<Document> {
|
||||||
reversedTiplocs: {
|
const pipeline = getPartialEndTiplocMatchPipeline(array);
|
||||||
$reverseArray: "$tiplocs",
|
const res = await queryAggregate("pis", pipeline);
|
||||||
},
|
return res[0];
|
||||||
reversedQueryArray: {
|
}
|
||||||
$reverseArray: tiplocArray,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
$expr: {
|
|
||||||
$gt: [
|
|
||||||
{
|
|
||||||
$indexOfArray: [
|
|
||||||
"$reversedTiplocs",
|
|
||||||
{
|
|
||||||
$arrayElemAt: ["$reversedQueryArray", 0],
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
code: 1,
|
|
||||||
skipStops: {
|
|
||||||
$subtract: [
|
|
||||||
{
|
|
||||||
$size: "$tiplocs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$size: tiplocArray,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const res = await queryAggregate('pis', pipeline)
|
function convertDocument(doc: Document, skipType: string): OB_Pis_SimpleObject {
|
||||||
console.log(JSON.stringify(res))
|
return {
|
||||||
return res
|
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";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
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