概述
路径模板n()...n()将点模板和边模板按顺序串联,形成特定路径结构。使用路径模板可以从图中获取与描述的模式或结构匹配的路径。
点模板和边模板
点模板和边模板是路径模板的构建要素,共分为以下四种:
模板 |
名称 |
描述 | 别名类型 |
|---|---|---|---|
n() |
单点模板 | 代表路径中的单个点:![]() |
NODE |
e(),le(),re() |
单边模板 (方向:双向、左向、右向) |
代表路径中的单条边:![]() |
EDGE |
e()[<steps>],le()[<steps>],re()[<steps>] |
多边模板 (方向:双向、左向、右向) |
代表路径中的多条连续边:![]() [<steps>]格式(N≥0):
0时,当且仅当边模板前的点与边模板后的点重合时有效。此时边模板被忽略,两侧的两个点将被视作单个点 |
N/A |
e().nf()[<steps>],le().nf()[<steps>],re().nf()[<steps>] |
有中介点的多边模板 (方向:双向、左向、右向) |
代表路径中多条连续的边及其之间的点: ![]() [<steps>]格式与多边模板中相同 |
N/A |
过滤器包裹在{}中,作用在括号内的所有点边模板上,更加精确地定义相应点边的schema和属性。此外,路径模板中的首个单点模板n()允许直接引用别名。
构建路径模板
路径以点开始,以点结尾,其间点边交替出现。值得注意的是,路径也可以仅由单个点组成,不包含任何边。依照该规则,可以构建符合特定场景的路径模板。请参考以下示例。
查找由Kavi喜欢的用户推荐的书:

n({@user.name == "Kavi"}).re({@likes}).n({@user}).re({@recommends}).n({@books} as b)
return b.name
查找C34持有的账户到C135持有的账户间1到3步出向交易路径:

n({_id == "C34"}).re({@owns}).n({@account}).re({@transfers})[3].n({@account}).le({@owns}).n({_id == "C135"}) as p
return p{*}
查找C34持有的账户到C135持有的账户间的3步交易路径,其中中间账户的level属性值大于4:

n({_id == "C34"}).re({@owns}).n({@account}).e({@transfers}).nf({@account.level > 4})[:3].n({@account}).le({@owns}).n({_id == "C135"}) as p
return p{*}
查找3到5步的任务依赖环形路径:

n({@task} as t).re({@dependsOn})[3:5].n({_id == t._id}) as p
return p{*}
本条查询重复使用了路径模板中的别名t,从而形成环形结构。
语法
- 语句别名:类型为
PATH - 方法可以在路径模板后串联使用:
方法 |
参数 |
描述 | 可选 |
别名类型 |
|---|---|---|---|---|
no_circle() |
/ | 剔除含有环路的路径。路径有重复点时意味着有环路出现 | 是 | N/A |
limit() |
<N> |
限制每个起点返回的路径数(N≥-1);-1返回所有路径 |
是 | N/A |
示例图集1

在一个空图集中,逐行运行以下语句,创建示例图集:
create().node_schema("country").node_schema("movie").node_schema("director").edge_schema("filmedIn").edge_schema("direct").edge_schema("bornIn")
create().node_property(@*, "name").edge_property(@direct, "year", int32).edge_property(@bornIn, "year", int32)
insert().into(@country).nodes([{_id:"C1", name:"France"}, {_id:"C2", name:"USA"}, {_id:"C3", name:"Canada"}])
insert().into(@movie).nodes([{_id:"M1", name:"Léon"}, {_id:"M2", name:"The Terminator"}, {_id:"M3", name:"Avatar"}])
insert().into(@director).nodes([{_id:"D1", name:"Luc Besson"}, {_id:"D2", name:"James Cameron"}])
insert().into(@filmedIn).edges([{_from:"M1", _to:"C1"}, {_from:"M1", _to:"C2"}, {_from:"M2", _to:"C2"}, {_from:"M3", _to:"C2"}])
insert().into(@direct).edges([{_from: "D1", _to: "M1", year: 1994}, {_from: "D2", _to: "M2", year: 1984}, {_from: "D2", _to: "M3", year: 2009}])
insert().into(@bornIn).edges([{_from: "D1", _to: "C1", year: 1959}, {_from: "D2", _to: "C3", year: 1954}])
找点
可以在单点模板n()中声明别名。
查找点@movie:
n({@movie} as m)
return m.name
结果:
| m.name |
|---|
| The Terminator |
| Léon |
| Avatar |
查找电影Léon拍摄所在国家:
n({@movie.name == "Léon"}).e({@filmedIn}).n(as c)
return c.name
结果:
| c.name |
|---|
| France |
| USA |
找边
可以在单边模板e()中声明别名。
查找电影The Terminator的导演时间:
n({@movie.name == "The Terminator"}).e({@direct} as d).n()
return d.year
结果:
| d.year |
|---|
| 1984 |
查找固定长度路径
查找描述所有在USA拍摄的电影及其导演的路径:
n({@country.name == "USA"}).le().n({@movie}).e().n({@director}) as p
return p{*}
结果:p

查找电影Léon和The Terminator之间的2步路径:
n({@movie.name == "Léon"}).e()[2].n({@movie.name == "The Terminator"}) as p
return p{*}
结果:p

查找可变长度路径
查找Luc Besson和France之间的4步内路径:
n({name == "Luc Besson"}).e()[:4].n({name == "France"}) as p
return p{*}
结果:p

查找Luc Besson和France之间的4步内路径,且不经过电影Léon:
n({name == "Luc Besson"}).e().nf({name != "Léon"})[:4].n({name == "France"}) as p
return p{*}
结果:p

查找最短路径
查找Luc Besson和France之间4步内的最短路径:
n({name == "Luc Besson"}).e()[*:4].n({name == "France"}) as p
return p{*}
结果:p

剔除环路
查找Léon和USA之间的4步内路径:
n({name == "Léon"}).e()[:4].n({name == "USA"}) as p
return p{*}
结果:p

查找Léon和USA之间的4步内路径,并剔除所有环路:
n({name == "Léon"}).e()[:4].n({name == "USA"}).no_circle() as p
return p{*}
结果:p

限制查询数量
查找每个导演执导的一部电影:
n({@director} as d).e().n({@movie} as m).limit(1)
return table(d.name,m.name)
结果:
| d.name | m.name |
|---|---|
| James Cameron | The Terminator |
| Luc Besson | Léon |
使用OPTIONAL
本条查询中,路径模板语句执行三次,每次使用c中的一条记录。使用OPTIONAL前缀后,如果没有查询到数据,则返回null:
find().nodes({@country}) as c
optional n(c).e({@filmedIn}).n({@movie} as m)
return table(c.name, m.name)
结果:
| c.name | m.name |
|---|---|
| France | Léon |
| Canada | null |
| USA | Léon |
| USA | Avatar |
| USA | The Terminator |
若不使用OPTIONAL前缀,则没有关于Canada的结果返回:
find().nodes({@country}) as c
n(c).e({@filmedIn}).n({@movie} as m)
return table(c.name, m.name)
结果:
| c.name | m.name |
|---|---|
| France | Léon |
| USA | Léon |
| USA | Avatar |
| USA | The Terminator |
示例图集2

在一个空图集中,逐行运行以下语句,创建示例图集:
create().node_schema("customer").node_schema("account").edge_schema("owns").edge_schema("transfers")
create().node_property(@account, "level", uint32).edge_property(@transfers, "time", datetime)
insert().into(@customer).nodes([{_id:"C01"}])
insert().into(@account).nodes([{_id:"A01", level: 2}, {_id:"A02", level: 3}, {_id:"A03", level: 4}, {_id:"A04", level: 2}])
insert().into(@owns).edges([{_from:"C01", _to:"A01"}, {_from:"C01", _to:"A02"}])
insert().into(@transfers).edges([{_from:"A01", _to:"A03", time:"2023-03-01"}, {_from:"A01", _to:"A04", time:"2023-04-25"}, {_from:"A03", _to:"A04", time:"2023-03-27"}, {_from:"A04", _to:"A02", time:"2023-02-15"}])
包含0步路径
查找C01持有的账户与其他账户间的0到1步出向交易路径,其中其他账户的level属性值不小于3:
n({_id == "C01"}).e().n({@account}).re({@transfers})[0:1].n({@account.level >= 3}) as p
return p
结果:p

[0:1]表明遍历可以沿re({@transfers})关系执行0或1步。当应用0步时,re({@transfers})[0:1]将被忽略,其前后的点将被合并,路径模板简化为n({_id == "C01"}).e().n({@account.level >= 3})。
以下查询中,由于合并后的点n({@account.level < 3 && @account.level >= 3})不存在,所以0步不会生成任何结果:
n({_id == "C01"}).e().n({@account.level < 3}).re({@transfers})[0:1].n({@account.level >= 3}) as p
return p
结果:p

步间过滤
prev_n,prev_e
使用系统别名prev_n和prev_e可在路径模板中实现步间过滤,即在每一步引用前一个点或边。
查找账户之间的2步出向交易路径,其中time属性递增:
n().re({@transfers.time > prev_e.time})[2].n() as p
return p
结果:p

了解更多prev_e和prev_n的使用细节,请参阅系统别名。
复用别名
本条查询通过再次使用路径模板中声明的别名,可达成与上述相同的结果:
n().re({@transfers} as t1).n().re({@transfers.time > t1.time}).n() as p
return p
结果:p




