图数据库入门教程(十三)怎么坐地铁最快?
两站之间最短路径
gremlin提供了repeat..until语法来循环执行一个逻辑,直到满足某个条件。通过repeat..until来进行最短路径查询非常适合。
下面语句来查询西单地铁站到东单地铁站的路径,并限制前10个
g.V().has("station","name","西单").repeat(out().simplePath()).until(has("name","东单")).path().by('name').limit(10)
//输出
[
path[西单, 天安门西, 天安门东, 王府井, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 天安门西, 天安门东, 王府井, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 北京站, 建国门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 崇文门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 王府井, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 灵境胡同, 西四, 平安里, 北海北, 南锣鼓巷, 东四, 灯市口, 东单]
]
同时,我们也可以用emit代替until来对结果进行过滤
g.V().has("station","name","西单").repeat(out().simplePath()).emit(has("name","东单")).path().by('name').limit(10)
//输出
[
path[西单, 天安门西, 天安门东, 王府井, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 天安门西, 天安门东, 王府井, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 北京站, 建国门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 崇文门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 王府井, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 灵境胡同, 西四, 平安里, 北海北, 南锣鼓巷, 东四, 灯市口, 东单]
]
这里使用simplePath是为了让遍历不走回头路。我们也可以吧until放到前面
g.V().has("station","name","西单").until(has("name","东单")).repeat(out().simplePath()).path().by('name').limit(10)
//输出
[
path[西单, 天安门西, 天安门东, 王府井, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 天安门西, 天安门东, 王府井, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 北京站, 建国门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 崇文门, 东单],
path[西单, 宣武门, 菜市口, 虎坊桥, 珠市口, 前门, 王府井, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 复兴门, 长椿街, 宣武门, 和平门, 前门, 王府井, 东单],
path[西单, 灵境胡同, 西四, 平安里, 北海北, 南锣鼓巷, 东四, 灯市口, 东单]
]
从西单出发,坐5站能到东单的路线
g.V().has("station","name","西单").repeat(out().simplePath()).times(5).has("name","东单").path().by('name')
//输出
[
path[西单, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 王府井, 东单]
]
times用来指定repeat中操作执行的次数。
从西单出发,坐5站以内能到东单的路线
g.V().has("station","name","西单").repeat(out().simplePath()).times(5).emit().has("name","东单").path().by('name')
//输出
[
path[西单, 天安门西, 天安门东, 王府井, 东单],
path[西单, 宣武门, 和平门, 前门, 崇文门, 东单],
path[西单, 宣武门, 和平门, 前门, 王府井, 东单]
]
emit的作用是包括指定次数以内的路径结果,代表"最多五站"的含义。
西单-东单,20站以内有多少种坐法
g.V().has("station","name","西单").repeat(out().simplePath()).times(20).emit().has("name","东单").count()
//输出
[1141]
查询两站间路径,并按照路径长度排序
g.V().has("station","name","西单").repeat(outE().inV().simplePath()).emit(has("name","东单")).project('path','total').by(path().by('name').by('dist')).by(path().unfold().values('dist').sum()).limit(5).order().by(select('total'))
//输出
[
{path=path[西单, 1217, 天安门西, 925, 天安门东, 852, 王府井, 774, 东单], total=3768},
{path=path[西单, 815, 宣武门, 851, 和平门, 1171, 前门, 1634, 崇文门, 822, 东单], total=5293},
{path=path[西单, 815, 宣武门, 851, 和平门, 1171, 前门, 1857, 王府井, 774, 东单], total=5468},
{path=path[西单, 1217, 天安门西, 925, 天安门东, 852, 王府井, 1857, 前门, 1634, 崇文门, 822, 东单], total=7307},
{path=path[西单, 815, 宣武门, 851, 和平门, 1171, 前门, 1634, 崇文门, 1023, 北京站, 946, 建国门, 1230, 东单], total=7670}
]
怎么理解这个语句?主要是by(path().unfold().values('dist').sum())这句。
我们先看看西单到东单的一个路径
g.V().has("station","name","西单").repeat(outE().inV().simplePath()).emit(has("name","东单")).path().limit(1).next()
//输出
{
path[
v[323672],
e[7nrf-6xqw-74l-7gnc][323672-route->348168],
v[348168],
e[7ls1-7gnc-74l-65fc][348168-route->286968],
v[286968],
e[7okf-65fc-74l-68l4][286968-route->291064],
v[291064],
e[7pcv-68l4-74l-6bqw][291064-route->295160],
v[295160]
]
}
这里的unfold(),是把path数组展开,然后通过values('dist')提取它们的距离集合,然后对集合求sum,赋值给total。