Skip to content

Commit b52041f

Browse files
committed
Support k shortest paths
1 parent 5439a04 commit b52041f

File tree

4 files changed

+180
-134
lines changed

4 files changed

+180
-134
lines changed

gql/parser_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ func TestParseQueryWithMultipleVar(t *testing.T) {
906906
func TestParseShortestPath(t *testing.T) {
907907
query := `
908908
{
909-
shortest(from:0x0a, to:0x0b) {
909+
shortest(from:0x0a, to:0x0b, numpaths: 3) {
910910
friends
911911
name
912912
}
@@ -918,6 +918,7 @@ func TestParseShortestPath(t *testing.T) {
918918
require.Equal(t, 1, len(res.Query))
919919
require.Equal(t, "0x0a", res.Query[0].Args["from"])
920920
require.Equal(t, "0x0b", res.Query[0].Args["to"])
921+
require.Equal(t, "3", res.Query[0].Args["numpaths"])
921922
}
922923

923924
func TestParseMultipleQueries(t *testing.T) {

query/query.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ type params struct {
145145
isGroupBy bool
146146
groupbyAttrs []gql.AttrLang
147147
uidCount string
148+
numPaths int
148149
}
149150

150151
// SubGraph is the way to represent data internally. It contains both the
@@ -739,6 +740,13 @@ func (args *params) fill(gq *gql.GraphQuery) error {
739740
}
740741
args.RecurseDepth = from
741742
}
743+
if v, ok := gq.Args["numpaths"]; ok && args.Alias == "shortest" {
744+
numPaths, err := strconv.ParseUint(v, 0, 64)
745+
if err != nil {
746+
return err
747+
}
748+
args.numPaths = int(numPaths)
749+
}
742750
if v, ok := gq.Args["from"]; ok && args.Alias == "shortest" {
743751
from, err := strconv.ParseUint(v, 0, 64)
744752
if err != nil {
@@ -1897,7 +1905,7 @@ func (sg *SubGraph) sortAndPaginateUsingVar(ctx context.Context) error {
18971905
// isValidArg checks if arg passed is valid keyword.
18981906
func isValidArg(a string) bool {
18991907
switch a {
1900-
case "from", "to", "orderasc", "orderdesc", "first", "offset", "after", "depth":
1908+
case "numpaths", "from", "to", "orderasc", "orderdesc", "first", "offset", "after", "depth":
19011909
return true
19021910
}
19031911
return false
@@ -2088,7 +2096,7 @@ func (req *QueryRequest) ProcessQuery(ctx context.Context) error {
20882096
return true
20892097
}
20902098

2091-
var shortestSg *SubGraph
2099+
var shortestSg []*SubGraph
20922100
for i := 0; i < len(req.Subgraphs) && numQueriesDone < len(req.Subgraphs); i++ {
20932101
errChan := make(chan error, len(req.Subgraphs))
20942102
var idxList []int
@@ -2165,8 +2173,8 @@ func (req *QueryRequest) ProcessQuery(ctx context.Context) error {
21652173
req.Latency.Processing += time.Since(execStart)
21662174

21672175
// If we had a shortestPath SG, append it to the result.
2168-
if shortestSg != nil {
2169-
req.Subgraphs = append(req.Subgraphs, shortestSg)
2176+
if len(shortestSg) != 0 {
2177+
req.Subgraphs = append(req.Subgraphs, shortestSg...)
21702178
}
21712179
return nil
21722180
}

query/query_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,53 @@ func TestShortestPath_NoPath(t *testing.T) {
16291629
js)
16301630
}
16311631

1632+
func TestKShortestPathWeighted(t *testing.T) {
1633+
populateGraph(t)
1634+
query := `
1635+
{
1636+
shortest(from: 1, to:1001, numpaths: 4) {
1637+
path @facets(weight)
1638+
}
1639+
}`
1640+
// We only get one path in this case as the facet is present only in one path.
1641+
js := processToFastJSON(t, query)
1642+
require.JSONEq(t,
1643+
`{"_path_":[{"_uid_":"0x1","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x1f","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e8","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e9"}]}]}]}]}`,
1644+
js)
1645+
}
1646+
1647+
func TestKShortestPathWeighted1(t *testing.T) {
1648+
populateGraph(t)
1649+
query := `
1650+
{
1651+
shortest(from: 1, to:1003, numpaths: 3) {
1652+
path @facets(weight)
1653+
}
1654+
}`
1655+
js := processToFastJSON(t, query)
1656+
require.JSONEq(t,
1657+
`{"_path_":[{"_uid_":"0x1","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x1f","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e8","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e9","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3ea","path":[{"@facets":{"_":{"weight":0.600000}},"_uid_":"0x3eb"}]}]}]}]}]},{"_uid_":"0x1","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x1f","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e8","path":[{"@facets":{"_":{"weight":0.700000}},"_uid_":"0x3ea","path":[{"@facets":{"_":{"weight":0.600000}},"_uid_":"0x3eb"}]}]}]}]},{"_uid_":"0x1","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x1f","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e8","path":[{"@facets":{"_":{"weight":0.100000}},"_uid_":"0x3e9","path":[{"@facets":{"_":{"weight":1.500000}},"_uid_":"0x3eb"}]}]}]}]}]}`,
1658+
js)
1659+
}
1660+
1661+
func TestTwoShortestPath(t *testing.T) {
1662+
populateGraph(t)
1663+
query := `
1664+
{
1665+
A as shortest(from: 1, to:1002, numpaths: 2) {
1666+
path
1667+
}
1668+
1669+
me(id: var( A)) {
1670+
name
1671+
}
1672+
}`
1673+
js := processToFastJSON(t, query)
1674+
require.JSONEq(t,
1675+
`{"_path_":[{"_uid_":"0x1","path":[{"_uid_":"0x1f","path":[{"_uid_":"0x3e8","path":[{"_uid_":"0x3ea"}]}]}]},{"_uid_":"0x1","path":[{"_uid_":"0x1f","path":[{"_uid_":"0x3e8","path":[{"_uid_":"0x3e9","path":[{"_uid_":"0x3ea"}]}]}]}]}],"me":[{"name":"Michonne"},{"name":"Andrea"},{"name":"Alice"},{"name":"Matt"}]}`,
1676+
js)
1677+
}
1678+
16321679
func TestShortestPath(t *testing.T) {
16331680
populateGraph(t)
16341681
query := `

0 commit comments

Comments
 (0)