Using a graph database approach we can use the infiniteGraph graph database and the DO Query Language. We create a weight calculator and then use it in the query which would look like the following:
CREATE WEIGHT CALCULATOR shortestRoute {
minimum: 0,
default: 0,
edges: {
()-[r:Road]->(): r.distance
}
};
Match m = max weight 8.0 shortestRoute
((:Town {name == 'A'})-[*..10]->(t:Town))
GROUP BY t.name
RETURN t.name as Name;
In this query we specify the "max weight" of 8.0 and which WEIGHT CALCUALTOR that we want to use. Then we specify the starting Town {name == 'A'} and the number of degrees out we want to go [*..10]. Then we specify the end-point (t:Town) with no predicate, which is a node of type Town with the label 't'. We group by t.name and return t.name as NAME.
The graph used in this query is:

And the query results are as follows:
DO> Match m = max weight 8.0 shortestRoute ((:Town {name == 'A'})-[*..10]->(t:Town)) GROUP BY t.name RETURN t.name as Name;
{
_Projection
{
Name:'B'
},
_Projection
{
Name:'D'
},
_Projection
{
Name:'E'
},
_Projection
{
Name:'F'
}
}
The setup (schema and sample data) is as follows:
UPDATE SCHEMA {
CREATE CLASS Town {
name : STRING,
roadsIn : List { Element: Reference { EdgeClass: Road, EdgeAttribute: from }, CollectionTypeName: TreeListOfReferences },
roadsOut : List { Element: Reference { EdgeClass: Road, EdgeAttribute: to }, CollectionTypeName: TreeListOfReferences }
}
CREATE CLASS Road {
name : String,
from : Reference { referenced: Town, Inverse: roadsOut },
to : Reference { referenced: Town, Inverse: roadsIn },
distance : REAL { Storage: B32 },
avgTravelTime : REAL { Storage: B32 },
stopLightCount : INTEGER { Encoding: Signed, Storage: B16 }
}
};
let townA = create Town { name: "A" };
let townB = create Town { name: "B" };
let townC = create Town { name: "C" };
let townD = create Town { name: "D" };
let townE = create Town { name: "E" };
let townF = create Town { name: "F" };
let townG = create Town { name: "G" };
let townH = create Town { name: "H" };
let ab = create Road { name: "AB", distance: 4.0, stopLightCount: 1, from: $townA, to: $townB };
let bc = create Road { name: "BC", distance: 5.0, stopLightCount: 2, from: $townB, to: $townC };
let cd = create Road { name: "CD", distance: 6.0, stopLightCount: 3, from: $townC, to: $townD };
let cH = create Road { name: "CH", distance: 10.0, stopLightCount: 0, from: $townC, to: $townH };
let ad = create Road { name: "AD", distance: 3.0, stopLightCount: 0, from: $townA, to: $townD };
let ae = create Road { name: "AE", distance: 4.0, stopLightCount: 3, from: $townA, to: $townE };
let ed = create Road { name: "ED", distance: 2.0, stopLightCount: 1, from: $townE, to: $townD };
let ef = create Road { name: "EF", distance: 4.0, stopLightCount: 7, from: $townE, to: $townF };
let fg = create Road { name: "FG", distance: 3.0, stopLightCount: 0, from: $townF, to: $townG };
let dg = create Road { name: "DG", distance: 8.0, stopLightCount: 6, from: $townD, to: $townG };
let dH = create Road { name: "DH", distance: 8.0, stopLightCount: 9, from: $townD, to: $townH };
let gH = create Road { name: "GH", distance: 8.0, stopLightCount: 4, from: $townG, to: $townH };
The query does fast/early pruning of possible result paths so it's loading only the data that it needs to in order to determine the results.