查询

Ultipa 提供了丰富的查询功能,通过 UQL,用户可以在没有编程基础的前提下,也能自由的探索任何的图数据。UQL 支持路径查询,K 邻查询,元数据(点,边)查询,并可以结合"filter"表达式进行过滤,通过 alias(别名)和 return 子句来定制所需的返回内容。本章将会介绍每种查询语句的使用方式以及 return 子句的使用。

查询机制

用户可以通过 UQL 把结构化的查询语句输送给 Ultipa 服务,Ultipa 服务会自动解析,优化 UQL,并将其分配给高性能图计算引擎进行图查询,然后返回给用户所需数据。

本节会从整体介绍 UQL 查询的机制,目的在于给用户一个大体的认识,如果在阅读过程中存在疑惑,请不用担心,请带着这些疑惑继续阅读后面章节,在后面的章节中,会对每一部分做详细介绍。

UQL 的格式分为三个部分,分别为 [命令] [参数] [返回],其中[返回]是可选项,比如对 Ultipa 属性的增删操作当中,就不需要定义返回内容。

我们通过 下图 来分析,在一次 ab 路径查询当中,这个三部分分别是什么样的:

如果不理解下面两个示例的具体含义,不用担心,可以直接进入下一节,本节旨在描述UQL的组装结构。

其中 ab() 是[命令],src(12).dest(21).depth(5).limit(5) 是[参数], select(name)定义了[返回]

又如 图 2 所示,:

这是一个模板匹配查询,其中 t(a) 代表Command[命令], n(12).e().n(b{age:20}).limit(10)是 Params [参数], 而 return(a, b.name) 为 [返回],其中的 a、b 是 alias(别名)。

对于return & select的使用详情可以参阅后面的《return & select 子句》章节。

为了方便大家快速的掌握UQL,本文还会准备大量的示例供用户参考。

点查询

点查询,即在 Ultipa 服务中,对 node 进行查询。这部分非常像传统数据库中的表查询操作。用户可以给定一些条件,或者 id 来获取需要的 nodes 数据。

点查询的 [命令] 为 find().nodes()

点查询的 [参数] 与 [返回] 为:

名称 类型 规范 描述
limit int >0, -1 返回的数量限制,-1: 不限制
select []string 逗号分隔 点所包含的 property

注: 其中,过滤条件设置于 [命令] 的参数当中。

示例 1. 查询 id 为 12 的 node,

find().nodes(12);

示例 2. 查询 id 为 12, 21, 32 的 node, 并返回他们的全部属性值,

find().nodes([12,21,32]).select(*)

示例 3. 查询 age 在 18 到 35 岁之间的 node, 结果包含他们的名字 name,返回 3 条结果

find()
  .nodes({ age: { $bt: [18, 35] } })
  .limit(3)
  .select(name);

如 示例 3 在 Ultipa-CLI 中的效果:

边查询

边查询,即在 Ultipa 服务中,对 edge 进行查询。这部分和点查询类似。用户可以给定一组条件,或者 id 来获取需要的 edges 数据。

边查询的 [命令] 为 find().edges()

边查询的 [参数] 与 [返回] 为:

名称 类型 规范 描述
limit int >0, -1 返回的数量限制, -1:不限制
select []string 逗号分隔 点所包含的 property

示例:

  1. 查询 id 为 12 的 edge,
find().edges(12);
  1. 查询 _from_id 为 12 的边, 并返回他们的名字 name,限制返回 10 条结果
find()
  .edges({ _from_id: 12 })
  .limit(3)
  .select(name);

看下 示例 2 在 Ultipa-cli 中的效果:

AB 路径查询

AB 路径查询, 即在 Ultipa 图系统当中按照指定的深度去查询一个 node 到另一个 node 之间的路径,深度可以是 1 - N。当然,我们也可以将查询深度设置为最短。

AB 路径查询的 [命令] 为 ab()

[参数] 和 [返回] 如下表所示:

名称 类型 规范 描述
src int >0, 必填 起始 node 的 ID
dest int >0, 必填 结束 node 的 ID
depth int Step Range 查询的深度
depth(N) : 第几层
depth(N:M): N-M 层
limit int >0, -1 返回的条数限制,-1:无限制
select []string 逗号分隔 路径点,边所包含的 property
select_node_properties []string 逗号分隔 路径点所包含的 property
select_edge_properties []string 逗号分隔 路径边所包含的 property
shortest string 边属性 返回最短路,属性值将会作为最短路的衡量标准,不设置视为每条边权重都为 1
node_filter filter Ultipa 过滤器 对所有点进行过滤(除起始点)
edge_filter filter Ultipa 过滤器 对所有边进行过滤
path_ascend string Edge Property 查找符合该属性升序排列的路径
path_descend string Edge Property 查找符合该属性降序排列的路径
direction string left,right 设定边的过滤方向
turbo bool(true) / 启动加速模式
osrc string 不可使用 src 设置起始 node 原始 ID
odest string 不可使用 dest 设置结束 node 原始 ID
no_circle / / 避免环路

示例:

  1. 查找 node ( _id = 12) 到 node (_id = 21) 之间 深度小于等于 3 的路径,返回 5 条结果。
ab()
  .src(12)
  .dest(21)
  .depth(:3)
  .limit(5);
  1. 与示例 1 相同,增加点和边的返回属性 name
ab()
  .src(12)
  .dest(21)
  .depth(:3)
  .limit(5)
  .select(name);
  1. 与示例 1 相同,返回点的属性 name 和 age,边的属性 name, 深度改为 3 层
ab()
  .src(12)
  .dest(21)
  .depth(3)
  .limit(5)
  .select_node_properties(name, age)
  .select_edge_properties(name);
  1. 与示例 1 相同,返回 5 步以内的最短路径
ab()
  .src(12)
  .dest(21)
  .depth(:5)
  .shortest()
  .limit(5);
  1. 增加过滤器,只返回边的 name 为 Like 的路径,路径长度在 3-5 层
ab()
  .src(12)
  .dest(21)
  .depth((3: 5))
  .edge_filter({ name: "Like" })
  .shortest()
  .limit(5);
  1. 通过边的属性 distance 来查询最短路径
ab()
  .src(12)
  .dest(21)
  .shortest(distance)
  .limit(5);
  1. 增加过滤器,只返回边的 rank 值在 20 到 30 之间的路径
ab()
  .src(12)
  .dest(21)
  .depth(:5)
  .edge_filter({ rank: { $bt: [20, 30] } })
  .limit(5);

提示: 过滤器的详细使用请参考《过滤器》章节。

  1. 使用 turbo 模式,加快查询速度
ab()
  .src(12)
  .dest(21)
  .depth(5)
  .turbo()
  .limit(5);
  1. 查询边的属性 time 符合升序排序的路径。
ab()
  .src(12)
  .dest(21)
  .path_ascend(time)
  .limit(5)
  .depth(5);

注:turbo()是Ultipa图计算的一种路径查询加速模式,特别适用于有超级节点的路径查询场景。超级节点或称为热点(hot-spot node)指的是出度(1度邻居)数量很大的顶点,例如那些有10万+相邻顶点的点被称作超级节点。使用turbo()模式在很多情况下可以获得50%-300%的性能提升。

K 邻查询

K 邻查询,查找从某个 node 出发最短 K 步能到达的邻居 nodes,并返回统计数据(数量,和邻居 nodes 集合)。例如,可以通过 K 邻查询计算某个 node 的第 3 步邻居的数量, 并返回全部或者部分结果。

注:市面上的很多图系统为了提高搜索速度, 采用了 DFS 的方法来实现 K-hop 查询, 这样计算的返回结果是 不正确并且大量重复的!

K 邻查询的 [命令] 为 khop()

[参数] 和 [返回] 如下表所示:

名称 类型 规范 描述
src int >0, 必填 起始 node 的 ID
depth int Step Range 查询的深度设置,
depth(N) : 第几层
depth(N:M): N-M 层
limit int >0, 0, -1 限制搜索数量 0:
仅返回全量统计数字
-1 :返回所有点信息以及返回的记录条数
select []string 逗号分隔 路径点,边所包含的 property
select_node_properties []string 逗号分隔 路径点所包含的 property
select_edge_properties []string 逗号分隔 路径边所包含的 property
node_filter filter Ultipa 过滤器 对所有点进行过滤(除起始点)
edge_filter filter Ultipa 过滤器 对所有边进行过滤
direction string left, right 设置过滤方向
osrc string 不可使用 src 起始 node 原始 ID

示例:

  1. 查找 node (id = 12) 的 第 1 步邻居,并返回 10 条结果与总计
khop()
  .src(12)
  .depth(1)
  .limit(10);
  1. 查找 node (_id = 12) 的 第 2 步邻居,返回 10 条结果和总计,携带属性 name
khop()
  .src(12)
  .depth(2)
  .limit(10)
  .select(name);
  1. 查找 node (id = 12) 的 第 2 步邻居,返回 10 条结果与总计,所有的 edge 符合条件 rank=20
khop()
  .src(12)
  .depth(2)
  .edge_filter({ rank: 20 })
  .limit(10);
  1. 查找 node 原始 ID 为 u_1234 的 2 - 3 步邻居,返回 10 条结果与总计,并携带属性 name
khop()
  .osrc("u_1234")
  .depth((2: 3))
  .limit(10)
  .select(name);

为了保证数据的清晰,K 邻查询将只返回点的数据,以及统计信息。

在 Ultipa-CLI 中运行的结果如 图 5 所示, 总共找到 node(id=12)的 1106 个 2 步邻居。

模板查询

模板查询,即在图系统当中,按照一定的匹配规则查询一张子图,并返回部分或者全部数据, 用户可以规定从哪些起始 node 出发,经过怎样的边和点,每一步如何过滤和筛选,最终达到一个定义好的结束 node。

通过模板查询,用户可以灵活的根据业务需求,组装子图的抽取模式。在模板查询中我们将会提到一个新的概念,alias (别名),下文中会有详细介绍。

模板查询的 [命令] 为 t(),t 代表 template(模板)的 缩写

模板查询的 [参数] 以及 [返回] 稍微有些不同,这部分将会分小节来描述使用方式。

下面先列举模板所支持的参数

名称 类型 规范 描述
limit int -1 或 > 0 返回的数量限制,-1:无限制
n 点模板 / 详见点模板章节
e 边模板 / 详见边模板章节
le 入边(左向)模板 / 筛选入方向的边
re 出边(右向)模板 / 筛选出方向的边
no_circle / / 路径中去掉环路

我们先来看一个比较完整的例子:

t(a1).n(n1{age:20}).e({rank:[20,30]})[3:7].n(n2).limit(5)
.return(a1, n1, n2._id, n2.name)

这句话告诉 Ultipa 服务执行以下任务:

  1. 执行一个模板路径查询, 标记 路径的 alias 为 a1,

  2. 起始 node 为属性 age=20 的点,标记这些 nodes 的 alias 为 n1,

  3. 经过深度为 3-7 之间,每条边的 rank 属性值在 20,30 之间的 edge

  4. 到达结束节点,标记 alias 为 n2.

  5. 查询出符合条件的 5 条路径结果,

  6. 返回这五条路径结果中:

    a) a1 的路径信息,携带 node 和 edge 的 id 信息,

    b) n1 的所有信息,

    c) n2 的属性_id 和 name。

Alias 别名系统

别名系统仅存在于模板查询当中,用于表示某一个点,边或者路径,在其他的子句中,通过别名,可以方便的来进行二次操作。

在使用 alias 时,需要遵循以下规范

  • 必须以字母或 "" 开头,内容必须为[a-z][a-z][0-9]或 ""组成
  • 长度不超过 64 个字符
  • 在一条 UQL 中,不能存在相同的 alias
  • 只能用于表示边,点,路径
  • 定义于 ultipa 过滤器之前,被应用于过滤器或 return 子句当中
  • 别名不可用于多步的边模板当中,详情经参阅后面的《边模板》小节
  • 不可以使用 Ultipa 系统中的模板保留字
    • 详见第一章中《语法规范》《保留字》部分

我们看下使用 alias 的例子:

t(a1).n(n1).e(e1{rank:33}).n(n2)

该示例分别定义了 alias : 路径 a1、点 n1, n2、 边 e1, 其中, e1 被定义在了边的 filter 之前。

用户也可以对返回值进行别名操作:

t(a1).n(n1).e(e1{rank:33}).n(n2).return(n2.name as userName)

注:return 中,路径不支持别名操作

点模板

点模板的参数使用方式为 n()

  • Alias: 别名,请参考 Alias 别名系统
  • Filter: Ultipa 过滤器,请参考下一章

边模板

边模板参数的使用方式为e(), 如图 7 所示:

  • Alias: 别名,请参考 Alias 别名系统
    • 注:若设置Steps, 如[3],[:3],[*:3] ,系统将不会接受此时的别名。
  • Filter: Ultipa 过滤器,请参考下一章
  • Steps: 即经过 N 步(steps),也可以理解为经过了 N 条边;若不指定,则为 1 步
    • [N1] 表示,经过 N1 步
    • [N1:N2] 表示 经过 N1 到 N2 步,N1 <= N2
    • [*:N2] 表示小于 N2 步的最短路径
      • 注:最短路径的使用,请参考下面小节 - 模板最短路查询
    • [:N2] 表示小于 N2 步,等同于 [1:N2]

边点组合模板

边模板支持了针对多步过滤的逻辑支持,若在多步当中对经过的点也要进行筛选,那么可以使用点边组合模板:

与边模板唯一的区别在于增加了 nf 的支持,即 node filter 的缩写。 指定 nf 中的过滤器即可对上图中 2 到 5 步中的点进行过滤。

点边组合模板中,若Steps,步数设置为1,是没有意义的。因为一步即经过一条边,也就是说并不会经过其他任何点, 因此不会触发nf点过滤器。

模板最短路查询

基于模板的最短路径查询,比普通的 ab 路径查询中的最短路查询更加灵活。 因最短路径查询对大量业务有至关重要的作用,因此本节将单独介绍最短路径模板 。

如图所示,最短路径模板与点边组合模板或者边模板无任何区别,仅仅是将 Steps 参数以 [*:N1]的形式组装书写。 * 在 Steps 部分表示最短路径,而 N1 代表的是路径的最大值,组装起来的含义即:

小于N1步的最短路径

示例 1:从 node(age =20)的顶点出发,查找与 node(age=30)的顶点的最短路径, 最长不超过 5 步:

t().n({age:20}).e()[*:5].n({age:30})

示例 2:从 node(age=20)的顶点出发,查找与 node({age=30})的顶点的最短路径,最长不超过 5 步,且中间经过的边的 type=works_for, 中间经过的点的 type=human。

t().n({age:20})
.e({type:"works_for"}).nf({type: "human"})[*:5].n({age:30})

示例 3:从公司 A 出发,通过投资最短路径查找股东,并返回这些股东所控制的公司

t()
.n({company_name: "a"})
.le({type:"invest"})[*:5]
.n({type: "human"}).re({type: "controll"}).n(c{type: "company"})
.return(c)

模板 K 邻查询

UQL 也支持模板式的 K 邻查询,仅需要将普通模板查询中的[命令] t(), 替换为 khop() 即可。

由于模板查询的特殊性,针对边模板中的Steps 仅支持 确定步数 K邻主要用于统计K步邻居信息,因此也不支持别名的使用。

示例 1:查询从 age=20 的点,经过两条边(即 k=2),到达 age=50 的点。返回 age=50 的点的个数,并抽样 100 条数据。

khop().n({age:20}).e()[2].n({age:50}).limit(100).select(*)

自动组网

图的魅力在于在纷繁复杂的网络当中,可以快速直观的检索出用户所关注的点之间的关联关系,通过 UQL 可以轻松的将所关注的点自动组成一张网络。

自动组网分为两种模式:

  1. N 个点,自动两两组网

  2. N 个点到 M 个点的自动组网

自动组网 [命令] 为 autoNet()

两种自动组网共享相同的 [参数] 如下:

名称 类型 规范 描述
srcs []int >0. 必填 起始 nodes 的 ID
dests []int >0 结束 nodes 的 ID,若设置此值,将会触发模式 2
depth int Step Range 查询的深度
depth(N) : 第几层
depth(N:M): N-M 层
limit int >0, -1 返回的条数限制, -1:无限制
select []string 逗号分隔 路径点,边所包含的 property
select_node_properties []string 逗号分隔 路径点所包含的 property
select_edge_properties []string 逗号分隔 路径边所包含的 property
shortest / / 返回最短路
node_filter filter Ultipa 过滤器 对所有点进行过滤(除起始点)
edge_filter filter Ultipa 过滤器 对所有边进行过滤
turbo / / 启动加速模式
no_circle / / 两点路径避免环路

示例 1:将 nodes( id = [12,21,30,40]) 的四个点组成一张网,每 2 个点之间返回 2 条路径,每条路径的深度设为 3。

autoNet()
  .srcs(12, 21, 30, 40)
  .depth(3)
  .limit(2);

注意,在组网语句中,limit()限定的是任意2个点之间的路径数量,而不是 全部路径的数量,因此上面的语句最多可能返回(2 * 4*3/2)=12条路径。

示例 2:将 node( id = 12) 与 nodes ( id = [12,270336,18432] ) 组成网,每 2 个点之间返回 2 条路径,且深度设为 5 以内.

autoNet()
  .srcs(12)
  .dests(12, 270336, 18432)
  .depth(:5)
  .limit(1);

在 Ultipa-Manager 当中,执行效果如下:

自动展开

从一个点逐层展开更多数据,展开的方式为: BFS(广度优先)。

自动展开的 [命令] 为:spread()

自动展开的 [参数] 如下:

名称 类型 规范 描述
src int >0. 必填 起始 nodes 的 ID
osrc string 不与 src 参数共存 设置原始 ID
depth int >0, 必填 展开的最大深度
limit int >0, -1 返回点的数量,-1:无限制
select []string 逗号分隔 路径点,边所包含的 property
select_node_properties []string 逗号分隔 路径点所包含的 property
select_edge_properties []string 逗号分隔 路径边所包含的 property
node_filter filter 过滤器 对所有点进行过滤(除起始点)
edge_filter filter 过滤器 对所有边进行过滤
direction left | right / 展开方向

示例 1:展开 node(id=12),返回 10 个点,深度限制为 2,并携带属性值 name

spread()
  .src(12)
  .depth(2)
  .limit(10)
  .select(name);

示例 2:展开 node(id=12),返回 10 个点,深度限制为 5,并携带属性值 name, 过滤条件为持股方向

spread()
  .src(12)
  .edge_filter({ type: "Invest" })
  .direction(right)
  .depth(5)
  .limit(10)
  .select(name);

自动展开在很多场景中都有广泛应用,尤其是在层层递归计算,或者逐层链路逻辑的时候。

return & select 子句

在上一节当中,我们讲述了模板的使用,每一个模板最后都会跟随一个 return 子句,本节会详细介绍如何使用 return 和 select 子句。

return 子句中使用 alias 定义返回内容,也可以使用一些 数学函数 进行 聚合计算,本节末尾会讲述聚合运算函数的使用。

select 子句只定义需要返回内容所携带属性的名称,如果返回数据既包含 nodes 又包含 edges,如路径结果。那么 select 将会同时作用于 nodes 和 edges。

UQL 同时提供了 select_node_properties 和 select_edge_properties 来精准控制需要返回的属性列表。

注: 在模板查询当中,select和return不建议同时使用。

Return 子句

return 子句仅作用于模板搜索当中,用于各类数据结果,如点,边,路径,以及他们的属性。

路径

示例:查找从 node(age=20)的点出发,经过一条边达到另一个 node,返回 1 条路径

t(p).n(a{age:20}).e(b).n(c).limit(1).return(p)

在示例当中,t(p) 中的 p 即为路径的别名,这样在 return 当中可以使用 p 进行返回,该查询等同于查找所有符合特征为 node(age=20)的顶点的 1 步邻居,返回的结果为完整的路径(即:起点-边-终点)。

设定返回路径结果的 UQL 具体组装形式,如下图:

具体可以参考如下示例:

  1. 指定路径 p 中携带点或者边的一些属性:
t(p).n(a{age:20}).e(b).n(c).limit(1).return(p{name,age}{name,rank})

2.携带所有的点属性,以及边属性中的 name 和 rank:

t(p).n(a{age:20}).e(b).n(c).limit(1).return(p{*}{name, rank})

3.点和边都携带 name 属性:

t(p).n(a{age:20}).e(b).n(c).limit(1).return(p{name})

4.因此,如果返回所有的点属性和边属性的写法如下:

t(p).n(a{age:20}).e(b).n(c).limit(1).return(p{*})

与路径示例相同,改为返回别名为 ac 的点

t(p).n(a{age:20}).e(b).n(c).limit(1).return(a, c)

在 Ultipa-CLI 中 点结果的表现形式如 图 所示:

通过别名 b 来返回边的数据:

t(p).n(a{age:20}).e(b).n(c).limit(1).return(b)

在 Ultipa-CLI 中边的表现形式如 图 所示:

属性

用户可以定义 alias 的返回属性,把它当做一个字段来处理,比如下面这个示例:

t(p).n(a{age:20}).e(b).n(c).limit(1).return(c.name, c.age)

在示例当中指定了别名 c 的 name 和 age 属性。

在 Ultipa-CLI 中 点的表现形式如 图 所示:

Select 子句

select 支持大部分的搜索语句。如 nodes(), edges(), ab(), khop()等。

在有路径返回结果的查询语句中,UQL 支持 select_node_properties 和 select_edge_properties 两种定义方式。

select 的参数分为两种形态,

select(<property_name>,<property_name>,...)

// 和

select(*)

分别为指定属性列表,和指定全部属性。

示例 1:查询点(id = 12) 到点( id = 21),返回路径结果,并携带点和边的 name 属性

ab()
  .src(12)
  .dest(21)
  .depth(3)
  .limit(10)
  .select("name");

示例 2:查询点(id = 12) 到点( id = 21),返回路径结果,并携带点的 name 属性和边的 rank 属性

ab()
  .src(12)
  .dest(21)
  .depth(3)
  .limit(10)
  .select_node_properties("name")
  .select_edge_properties("rank");

示例 3:查询模板,Company -> Employee -> Customer, 并返回路径数据中的 name 属性

t()
  .n({ type: "Company" })
  .re()
  .n({ type: "Employee" })
  .re()
  .n({ type: "Customer" })
  .select("name")
  .limit(10);

select不可与路径别名同时使用:

t(p)
  .n({ type: "Company" })
  .re()
  .n({ type: "Employee" })
  .re()
  .n({ type: "Customer" })
  .select("name")
  .limit(10);

因指定了路径别名p, 且同时使用了select,此时系统会抛出异常。

聚合运算

UQL 支持丰富的聚合运算函数,通过聚合运算可以方便的对结果进行统计等二次处理操作。聚合运算函数会作用于 return 中的 alias 之上。

注:需要进行聚合运算的属性必须已经 LTE。

支持的聚合运算函数:

  • count 计数
  • count_distinct 去重后计数
  • sum 计算总和(只对整形有效)
  • min 最小值
  • max 最大值
  • collect 收集
  • order_by 排序

$count 运算

计数运算,可以从 path、元数据,property 三方面进行计数,计数结果是一致的:

  1. 对路径进行计数,即满足模板的路径总数
  2. 对 node 或 edge 元数据计数,即在所有满足模板路径的基础上,对 node 或者 edge 的 alias 的个数进行统计
  3. 对 property 计算,即所有满足模板路径的基础上,对 node 或者 edge 的 alias 所对应的属性值的个数进行统计

示例 1: 计算路径数量,点(a)的数量,边(b)的数量,以及点(c)属性 name 的数量

t(p).n(a{age:20}).e(b).n(c).limit(1).return({$count: p, $count: a, $count: b, $count: c.name})

$count_distinct 运算

与 $count 类似的,是对去重后的元数据,property 进行计数。

示例 1:计算点(a)的数量,并且计算边(b)的数量

t(p).n(a{age:20}).e(b).n(c).limit(1)
.return({$count_distinct: a, $count_distinct: b})

示例 2:计算点(c)属性 name 的数量

t(p).n(a{age:20}).e(b).n(c).limit(1).return({$count_distinct: c.name})

$sum 运算

求和运算,对结果中,数字类型的 property 进行求和。 若对非数字类型 property 进行 sum 运算会产生错误。

示例:计算公司( id =12 ) 的员工的工资总和

t(p).n(12).le({type:"works_for"}).n(c{type: "human"})
.return({$sum: c.revenue})

$max 运算

求最大值运算,对结果中,数字类型的 property 查找最大值,若对非数字进行 max 运算,会产生错误。

示例:查找公司 ( id =12) 工资最高的员工

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.return({$max: c.salary})

注:上面的例子中边的类型为"works for",边的箭头方向可以 是从n(c)指向n(12),Ultipa在上述操作中会对边的方向进行双向过滤计算(即:忽略边的方向)。

$min 运算

求最小值运算,对结果中,数字类型的 property 查找最小值,若对非数字进行 min 运算,会产生错误。

示例: 查找公司( id =12) 员工中工资最低的员工

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.return({$min: c.salary})

$avg 运算

求平均值运算,对结果中,数字类型的 property 求平均值,若对非数字进行 avg 运算,会产生错误。

示例: 计算公司( id =12) 员工中的平均值

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.return({\$avg: c.salary})

$collect 运算

collect 运算,对结果中的 property 进行收集去重操作,将会返回一个去重之后的列表集合。

示例:查看公司 (id = 12) 的员工都来自哪些省份:

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.return({$collect: c.province})

order_by 排序

order_by 运算, 依据结果中 alias 的属性进行升序(ASC)或降序(DESC)的路径排序,支持多属性排序,(只支持数字类型的属性)

示例:查询 公司( id =12) 的员工,返回薪水最高的 10 名员工, 且年龄按升序排列:

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.limit(10).order_by({c.salary: -1, c.age: 1}).return(c)

注:order_by建议放在最后,方便理解和阅读

默认order_by将会对所有可能搜到的路径或者结果排序,若深层搜索路径数量过大(上亿条路),为了保证可能时间内,或系统承载范围内返回可以接受的结果,可以通过设置order_by的第二个参数来进行限制:

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.limit(10).order_by({c.salary: -1, c.age: 1}, 2000).return(c)

group_by 分组

Group by 支持对点,边,点边的某个属性进行成组操作。不支持传入路径别名。

group_by 运算,依据结果中 alias 的属性进行分组逻辑。

示例:查询 公司 12 的员工,返回男生和女生的数量

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.limit(2).return({\$count_distinct: c}).group_by(c.gender)

示例:查询 公司 12 的员工,返回男生和女生的数量 ,按照统计数量排序

t(p).n(12).e({type:"works_for"}).n(c{type: "employee"})
.limit(-1)
.return({\$count_distinct: c} as genderCount)
.group_by(c.gender).order_by(genderCount)
  • group_by 将会触发不同数据组装逻辑:
    1. return 中只可使用 $max $min $sum $count_distinct $avg 聚合函数
    2. limit 限制将控制 group by 之后的 group 数量
    3. order_by 只能对 return 中的别名进行排序