diff --git a/src/components/PathVisualiser.astro b/src/components/PathVisualiser.astro new file mode 100644 index 0000000..c16ead3 --- /dev/null +++ b/src/components/PathVisualiser.astro @@ -0,0 +1,114 @@ +--- +import type { Path } from 'neo4j-driver'; +import type { Edge, Node } from 'vis-network'; + +import Graph from '@/components/Graph.astro'; + +import { db } from '@/db'; +import type { Driver } from '@/models/driver'; + +interface Props { + source: string; + dest: string; +} + +const { source, dest } = Astro.props; + +const pluralise = (word: string, num: number) => + num + ' ' + word + (num > 1 ? 's' : ''); + +let paramsNotEqual = source !== dest; +let numPaths = 0; +let degsOfSeparation = 0; + +let nodes: Node[] = []; +let edges: Edge[] = []; + +if (paramsNotEqual) { + const { records } = await db.executeQuery( + `MATCH (n:Driver) + WHERE + (n.forename + " " + n.surname) = $source + OR (n.forename + " " + n.surname) = $dest + WITH collect(n) AS nodes + UNWIND nodes AS s + UNWIND nodes AS d + WITH * + WHERE + (s.forename + " " + s.surname) = $source + AND (d.forename + " " + d.surname) = $dest + MATCH path = allShortestPaths((s)-[*..20]-(d)) + RETURN path`, + { source, dest } + ); + + numPaths = records.length; + degsOfSeparation = + records.length > 0 ? (records[0].get('path') as Path).length : 0; + + const pairsOfRelatedDrivers = records.flatMap((rec) => + (rec.get('path') as Path).segments.map( + ({ start, end }) => [start.properties, end.properties] as Driver[] + ) + ); + + nodes = [ + ...new Map( + pairsOfRelatedDrivers.flatMap((pair) => + pair.map(({ driverId, forename, surname }) => [ + driverId.toNumber(), + forename + ' ' + surname + ]) + ) + ).entries() + ].map(([id, label]) => ({ id, label })); + + edges = [ + ...new Set( + pairsOfRelatedDrivers.map( + ([from, to]) => from.driverId.toString() + '-' + to.driverId.toString() + ) + ).values() + ].map((pair) => { + const [from, to] = pair.split('-').map(Number); + + return { from, to }; + }); +} +--- + +

+ { + paramsNotEqual ? ( + nodes.length > 0 ? ( + degsOfSeparation == 1 ? ( + 'Colosally, these drivers were teammates!' + ) : ( + + Found {pluralise('path', numPaths)} with + {pluralise('degree', degsOfSeparation)} + of separation. + + ) + ) : ( + 'No paths were found!' + ) + ) : ( + "There's nothing wrong with the inputs, except they're identical." + ) + } +

+{ + nodes.length > 0 && ( +
+ +
+ ) +} + + diff --git a/src/models/driver.ts b/src/models/driver.ts new file mode 100644 index 0000000..58368e3 --- /dev/null +++ b/src/models/driver.ts @@ -0,0 +1,7 @@ +import type { Integer } from 'neo4j-driver'; + +export interface Driver { + driverId: Integer; + forename: string; + surname: string; +} diff --git a/src/pages/index.astro b/src/pages/index.astro index 6a1c6f9..625766e 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,21 +1,27 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro'; +import BaseLayout from '@/layouts/BaseLayout.astro'; import DriversList from '@/components/DriversList.astro'; +import PathVisualiser from '@/components/PathVisualiser.astro'; -Astro.response.headers.set('Cache-Control', 'public, max-age=604800'); +const source = (Astro.url.searchParams.get('source')! || '').trim(); +const dest = (Astro.url.searchParams.get('dest')! || '').trim(); + +// Cache the page for 2 weeks +Astro.response.headers.set('Cache-Control', 'public, max-age=1209600'); ---
-

Six Degrees of Formula One

+

Six Degrees of F1

Find out which driver is the sport's Kevin Bacon

Get all the shortest paths from/to:

-
+
+ { + source !== '' && dest !== '' && ( + + ) + }