aggregate
功能:(sideEffect)在遍历过程中,将输入objects聚合成一个list<object>,并保存在sideEffect。
备注:
单步不会影响输入objects,下一个单步仍然以objects作为输入。
单步生成的list<object>是只读的,在后续遍历的任意位置可以通过
cap()
取值。PS:不推荐通过
select()
取值。由于select()
对每个实体都会返回一个list<object>,多个实体就会返回多个一样的list<object>。
单步可以被
by()
修饰,用于指定聚合的字段值。单步会隐式地插入
barrier()
,使用store()
可以不阻塞地实现lazy聚合。aggregate()
、as()
、fold()
的差异?aggregate()
是将输入聚合为list并拷贝到sideEffect,下一个单步以objects作为输入,拷贝值对后续遍历来说是只读的。as()
是对输入打标,下一个单步以带标的objects作为输入,带标的objects对后续遍历来说是可修改的。fold()
是将输入聚合为list,下一个单步以list作为输入。
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").aggregate("x").by("name")
==> {"label":"person","age":29,"name":"marko","pk":"1"}
==> {"label":"person","age":32,"name":"josh","pk":"4"}
==> {"label":"person","age":27,"name":"vadas","pk":"2"}
==> {"label":"person","age":35,"name":"peter","pk":"6"}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").aggregate("x").by("name").cap("x")
==> ["josh","marko","peter","vadas"]
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").aggregate("x").by("name").filter("age>=32").cap("x")
==> ["josh","marko","peter","vadas"]
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").aggregate("x").by("name").outE("knows").aggregate("y").cap("x","y").dedup()
==> {"\"x\"":["josh","marko","peter","vadas"],"\"y\"":[{"label":"knows","pk":"1","sk":"2","weight":0.5},{"label":"knows","pk":"1","sk":"4","weight":1.0}]}
alias
功能:(sideEffect)为输入实体(点或边)定义虚拟字段。
格式:
alias("expr1:new1;expr2;new2;field1:new3")
备注:
单步支持将定义为表达式虚拟字段。
单步支持为原始字段定义虚拟字段,相当于为原始字段定义了别名。
重点:为原始字段定义别名后,后续遍历只能通过别名访问该字段。
不允许存在重复的字段名。
此单步为iGraph自研扩展的单步
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").alias("name:nick;age*2:double_age")
==> {"label":"person","age":29,"double_age":58,"nick":"marko","pk":"1"}
==> {"label":"person","age":32,"double_age":64,"nick":"josh","pk":"4"}
==> {"label":"person","age":27,"double_age":54,"nick":"vadas","pk":"2"}
==> {"label":"person","age":35,"double_age":70,"nick":"peter","pk":"6"}
// 定义别名后,用老名字访问字段将返回空结果
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").alias("name:nick").values("name")
==> []
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").alias("name:nick").values("nick")
==> ["marko","josh","vadas","peter"]
and
功能:(filter)返回“所有子遍历都产生结果”的输入。
备注:单步可以传入不定长个数的遍历。
// 找到年龄在[32,35)之间的人
g("thinkerpop").V("1;2;4;6").hasLabel("person").and(has("age",P.lt(35)),has("age",P.gte(32)))
==> {"label":"person","age":32,"name":"josh","pk":"4"}
// 找到年龄在大于28岁,并且朋友不少于2个的人
g("thinkerpop").V("1;2;4;6").hasLabel("person").and(has("age",P.gt(28)),outE("knows")).count().is(P.gte(2)))
==> {"label":"person","age":29,"name":"marko","pk":"1"}
as
功能:(修饰符)为输入objects打标,从而让后续单步和数据结构访问到。
备注:
单步可以为一批结果打多个标签。
单步生成带标objects,下一个单步以带标的objects作为输入,在后续遍历的任意位置可以通过
select()
取值。select()
获取多个标签值时会得到map<label,objects>
。select()
可被by()
修饰,指定返回标签的某个字段。
aggregate()
、as()
、fold()
的差异?aggregate()
是将输入聚合为list并拷贝到sideEffect,下一个单步以objects作为输入,拷贝值对后续遍历来说是只读的。as()
是对输入打标,下一个单步以带标的objects作为输入,带标的objects对后续遍历来说是可修改的。fold()
是将输入聚合为list,下一个单步以list作为输入。
结果集中的
"label":"person"
用于展示实体对应的图label,和as()
的打标值不是一个含义。
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").as("x").select("x")
==> {"label":"person","age":29,"name":"marko","pk":"1"}
==> {"label":"person","age":32,"name":"josh","pk":"4"}
==> {"label":"person","age":27,"name":"vadas","pk":"2"}
==> {"label":"person","age":35,"name":"peter","pk":"6"}
// filter单步以带标的objects作为输入,并且过滤了某些objects
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person")as("x").filter("age>=32").select("x")
==> {"label":"person","age":32,"name":"josh","pk":"4"}
==> {"label":"person","age":35,"name":"peter","pk":"6"}
// 单步可通过as()被多个标签关联
// select()获取多个标签值时会得到map<label,objects>
// select()被by()修饰,指定获取label的哪个字段
g("thinkerpop").V("1;2").hasLabel("person").as("x","y","z").select("x","y","z").by("pk").by("name").by("age")
==> {"\"x\"":"1","\"y\"":"marko","\"z\"":29}
==> {"\"x\"":"2","\"y\"":"vadas","\"z\"":27}
barrier
功能:(map)阻塞。
barrier()
前的所有单步都执行完后,才开始执行barrier()
后的单步。备注:
单步默认合并相同的objects。
合并方法:将实体的bulk值相加,实体的sack值按规则合并。
目的:如果输入有十个bulk=1的顶点A,
进行
barrier().outE()
。barrier()
返回一个bulk=10的顶点A。outE()
需要计算一次,得到一个bulk=10的结果。进行
outE()
,需要计算十次,得到十个相同的bulk=1的结果。
用户一般感受不到合并操作的存在。因为返回最终结果时,bulk>1的实体会按照bulk值成倍地被展开,展开时不会修改bulk值。
通过
bulk()
可以获取实体的bulk值通过
local(count())
可以计算单个object的数量,即获取相同object的数量
barrier(Barrier.nodedup)
只阻塞,不合并相同objects。aggregate()
、cap()
、count()
、dedup()
、distinct()
、fold()
、group()
、groupCount()
、limit()
、max()
、mean()
、min()
、order()
、range()
、sample()
、sum()
、tail()
等单步会隐式插入barrier()
// barrier将相同的objects合并为一个object,并且bulk值被设置为重复的数量
// 一般情况下,用户感受不到合并操作的存在,因为在输出结果前,objects会按照bulk值展开
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier()
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
// 通过bulk()获取实体的bulk值
// 返回结果前展开,一个bulk=3的软件3变成三个bulk=3的软件3
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier().bulk()
// 通过local(count())计算单个object的数量,即获取相同object的数量
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier().local(count())
==> [3,1]
// barrier(Barrier.nodedup) 只阻塞,不会合并相同objects。
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier(Barrier.nodedup)
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
// 不合并时,实体的bulk值都为1
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier(Barrier.nodedup).bulk()
==> [1,1,1,1]
branch
功能:(branch)分支查询。
备注:
需要和
option()
联合使用。子遍历的计算结果满足第一个条件,则进入第一个分支;否则,继续比较第二个条件。
单步可以传入不定长个数的遍历。
和
choose()
功能类似
// if (name=marko) {输出age}
// else {输出name}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").branch(values("name")).option("marko",values("age")).option(none,values("name"))
==> [29,"josh","vadas","peter"]
bulk
功能:(map)返回实体的bulk值
备注:
实体的bulk值初始为1。
barrier()
合并相同结果时,bulk值会相加。用户一般感受不到合并操作的存在。因为返回最终结果时,bulk>1的实体会按照bulk值成倍地被展开,展开时不会修改bulk值。
dedup()重置bulk值为1。
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV()
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
// barrier()合并三个bulk=1的软件3为一个bulk=3的软件3
// 返回结果前展开,一个bulk=3的软件3变成三个bulk=3的软件3
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier().bulk()
==> 3
==> 3
==> 3
==> 1
// barrier()合并三个bulk=1的软件3为一个bulk=3的软件3
// dedup()重置一个bulk=3的软件3为一个bulk=1的软件3
// 返回结果前展开,四个顶点变成了二个
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().barrier().dedup().bulk()
==> 1
==> 1
by
功能:(修饰符)为其他单步提供遍历、函数、比较器等参数。最通用的形式是
step().by()...by()
。备注:下面的这些单步操作都支持
by()
。每个语义详见单步的章节aggregate()
:通过by()
指定聚合的字段值。cyclicPath()
:通过by()
指定有环路径上需要记录的字段dedup()
:通过by()
指定去重时需要比较的字段值distinct()
:通过by()
指定打散规则group()
:通过by()
指定分组规则和组内存放的值groupCount()
:通过by()
指定分组的规则order()
:通过by()
指定排序规则以及升序/降序/乱序path()
:通过by()
指定路径上需要记录的字段project()
:通过by()
指定映射的value值sample()
:通过by()
指定某个字段的值作为采样权重,权重越高采样到的概率越大select()
:通过by()
指定获取标签的某个字段simplePath()
:按照by()
来过滤保留简单路径(非循环路径即成为简单路径)
cap
功能:(map)返回sideEffect的存储值
备注:
单步支持读取sideEffect的多个存储值,会得到
map<key,sideEffect>
。单步会隐式地插入
barrier()
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().groupCount("x").by("name").cap("x")
==> {"lop":3,"ripple":1}
// 读取sideEffect的多个存储值,会得到map<key,sideEffect>
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").groupCount("x").by("pk").groupCount("y").by("weight").cap("x","y")
==> {"\"x\"":{"\"1\"":1,"\"4\"":2,"\"6\"":1},"\"y\"":{"0.2":1,"0.4":2,"1.0":1}}
choose
功能:(branch)分支查询。
备注:
子遍历的计算结果满足第一个条件,则进入第一个分支;否则,继续比较第二个条件。
单步可以传入不定长个数的遍历。
与
branch()
功能类似
// if (name=marko) {输出age}
// else {输出name}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").choose(has("name","marko"),values("age"), values("name"))
==> 29
==> "josh"
==> "vadas"
==> "peter"
// if (age<=30) {输出朋友关系}
// else {输出创造关系)
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").choose(values("age").is(P.lte(30)),__.outE("knows"),__.outE("created"))
==> {"label":"knows","pk":"1","sk":"4","weight":1.0}
==> {"label":"knows","pk":"1","sk":"2","weight":0.5}
==> {"label":"created","pk":"4","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"5","weight":1.0}
==> {"label":"created","pk":"6","sk":"3","weight":0.2}
// if (age=29) {输出朋友关系}
// else if (age=27) {输出创造关系}
// else {输出自己}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").choose(values("age")).option(29,__.outE("knows")).option(32,__.outE("created")).option(none,identity())
==> {"label":"knows","pk":"1","sk":"4","weight":1.0}
==> {"label":"knows","pk":"1","sk":"2","weight":0.5}
==> {"label":"created","pk":"4","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"5","weight":1.0}
==> {"label":"person","age":35,"nick":"peter","pk":"6"}
coalesce
功能:(branch)按顺序处理输入,返回首个“至少能计算出一个元素”的输入的计算值
备注:单步可以传入不定长个数的遍历。
// 先计算出朋友关系,故输出朋友关系
g("thinkerpop").V("1").hasLabel("person").coalesce(outE("knows")outE("created"))
==> {"label":"knows","pk":"1","sk":"4","weight":1.0}
==> {"label":"knows","pk":"1","sk":"2","weight":0.5}
// 先计算出创造关系,故输出创造关系
g("thinkerpop").V("1").hasLabel("person").coalesce(outE("created"),outE("knows"))
==> {"label":"created","pk":"1","sk":"3","weight":0.4}
constant
功能:(map)返回用户自定义的“值”。
备注:
单步仅支持数值或者string。
单步常见于分支查询。
// if (age<=30) {输出“young man”}
// else {输出年龄)
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").choose(filter("age<30"),constant("young man"),values("age"))
==> "young man"
==> "32"
==> "young man"
==> "35"
// if (有年龄) {输出年龄}
// else {输出5}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").union(outE("knows").inV(),outE("created").inV()).coalesce(properties("age"), constant(5))
==> {"age":32}
==> {"age":27}
==> 5
==> 5
==> 5
==> 5
count
功能:(map)计算输入objects的数量
备注:
count(Scope.local)
支持对迭代器类型(list、set等)的输入求容器内元素个数单步会隐式地插入
barrier()
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").count()
==> 4
// 对fold()生成的list<object>,求容器内的元素个数
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").fold().count(Scope.local)
==> 4
cyclicPath
功能:(filter)返回有环的路径
备注:
单步可以被
by()
修饰,用于指定有环路径上需要记录的字段simplePath()
返回无环的路径
g("thinkerpop").V("1").hasLabel("person").out().out().cyclicPath().path()
==> 空结果(说明图thinkerpop不包含有向环)
dedup
功能:(filter)对输入进行去重。
备注:
如果遍历的bulk大于1,单步会将bulk重设成1
单步可以被
by()
修饰,用于指定去重时需要比较的字段值。字段值相同的objects,会随机保留一个。
单步支持对
as()
标签进行去重单步会隐式地插入
barrier()
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV()
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().dedup()
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
// 随机保留一个lang字段相同的objects
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").inV().dedup().by("lang")
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
// 按照x和y标签的值,进行去重。
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").as("x").inV().as("y").dedup("x","y")
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"lop","pk":"3"}
==> {"label":"software","lang":"java","name":"ripple","pk":"5"}
distinct
功能:(filter)打散。按照某种规则对结果进行hash,并从每个hash桶抽取固定数量的结果。
备注
单步支持
Distinct.round
指定抽取轮数,默认抽取1轮单步支持
Distinct.amount
指定每个桶每轮抽取的个数,默认每个桶每轮抽取1个
单步支持
Distinct.isReserved
指定附加未抽取到结果集末尾,默认丢弃剩余结果单步可以被
by()
修饰,用于指定打散规则单步会隐式地插入
barrier()
此单步为iGraph自研扩展的单步
// 按照pk分桶,默认抽取1轮,默认每个桶每轮抽取1个,默认丢弃未被抽取的结果
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").distinct().by("pk")
==> {"label":"created","pk":"6","sk":"3","weight":0.2}
==> {"label":"created","pk":"1","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"3","weight":0.4}
// 按照“pk”分桶,指定抽取2轮,指定每个桶每轮抽取1个,默认丢弃未被抽取的结果
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").distinct(Distinct.round,2,Distinct.amount,1).by("pk")
==> {"label":"created","pk":"6","sk":"3","weight":0.2}
==> {"label":"created","pk":"1","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"5","weight":1.0}
// 按照“朋友的个数”分桶,默认抽取1轮,默认每个桶每轮抽取1个,指定附加未被抽取的结果到结果集的末尾
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").distinct(Distinct.isReserved).by(__.outE("created").count())
==> {"label":"person","age":27,"name":"vadas","pk":"2"}
==> {"label":"person","age":32,"name":"josh","pk":"4"}
==> {"label":"person","age":29,"name":"marko","pk":"1"}
==> {"label":"person","age":35,"name":"peter","pk":"6"}
E
功能:(实体)查询边的信息。
语法:不同的pkey字符串用
;
分割;pkey指定多个skey的格式为pk:sk1|sk2|...
备注:
图访问方式:通过
hasLabel()
指定要访问的边对应的图label
g("thinkerpop").E("1:3;4:3|5;6").hasLabel("created")
==> {"label":"created","pk":"1","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"3","weight":0.4}
==> {"label":"created","pk":"4","sk":"5","weight":1.0}
==> {"label":"created","pk":"6","sk":"3","weight":0.2}
emit
功能:(修饰符)记录循环的中间结果。
备注:需要与
loop()
、repeat()
等结合使用。
fields
功能:(sideEffect)裁剪实体(点/边)的字段
格式:不同的字段名用
;
隔开备注:
pkey和skey字段不可被裁剪
单步有利于减少数据的传输量
此单步为iGraph自研扩展的单步
g("thinkerpop").E("1:3;4:3|5;6").hasLabel("created").fields("pk;sk")
==> {"label":"created","pk":"1","sk":"3"}
==> {"label":"created","pk":"4","sk":"3"}
==> {"label":"created","pk":"4","sk":"5"}
==> {"label":"created","pk":"6","sk":"3"}
filter
功能:(filter)过滤实体。满足过滤条件的实体被保留;否则,被丢弃。
备注:单步支持表达式或者子遍历作为过滤条件
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").filter("age<30")
==> {"label":"person","age":29,"name":"marko","pk":"1"}
==> {"label":"person","age":27,"name":"vadas","pk":"2"}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").filter(__.outE("knows"))
==> {"label":"person","age":29,"name":"marko","pk":"1"}
fold
功能:(map)将输入objects聚合成list<object>
备注:
aggregate()
、as()
、fold()
的差异?aggregate()
是将输入聚合为list并拷贝到sideEffect,下一个单步以objects作为输入,拷贝值对后续遍历来说是只读的。as()
是对输入打标,下一个单步以带标的objects作为输入,带标的objects对后续遍历来说是可修改的。fold()
是将输入聚合为list,下一个单步以list作为输入。
unfold()
是相反的操作,可展开list单步会隐式地插入
barrier()
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").values("name").fold()
==> ["marko","josh","vadas","peter"]
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").values("name").fold().unfold()
==> "marko"
==> "josh"
==> "vadas"
==> "peter"
group
功能:(map)将输入objects按照规则进行分组
备注:
单步可以被两个
by()
修饰先通过
by()
指定分组的规则,再通过by()
指定每组存放的值分组规则【必选填】,可以是表达式或者子遍历
存放的值【可选项】,可以是表达式或者子遍历的值,默认是objects本身。
单步会隐式地插入
barrier()
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").group().by(outE("knows".count())
==> {"0":[{"label":"person","age":32,"name":"josh","pk":"4"},{"label":"person","age":27,"name":"vadas","pk":"2"},{"label":"person","age":35,"name":"peter","pk":"6"}],"2":[{"label":"person","age":29,"name":"marko","pk":"1"}]}
// 按照label进行分组,每组保留结果中的name字段值
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").union(outE("knows").inV(),outE("created").inV()).group().by(T.label).by("name")
==> {"\"person\"":["josh","vadas"],"\"software\"":["lop","lop","ripple","lop"]}
常见用法
// 按照f1分组后,组内再按照weight降序排序,每组保留前2个结果
......group().by("f1").select(Column.values).unfold().flatMap(__.unfold().order().by("weight", decr).limit(2))
groupCount
功能:(sideEffect)将输入objects按照规则进行分组,并输出每组包含的objects个数
备注:
单步可以被
by()
修饰,用于指定分组的规则分组规则【可选项】,可以是表达式或者子遍历
单步支持将分组的结果拷贝到sideEffect,后续遍历再通过
cap()
取值。单步会隐式地插入
barrier()
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").groupCount().by("pk")
==> {"\"1\"":1,"\"4\"":2,"\"6\"":1}
g("thinkerpop").V("1;2;3;4;5;6").hasLabel("person").union(outE("knows").inV().outE("created").inV()).groupCount().by(T.label)
==> {"\"person\"":2,"\"software\"":4}
// 将分组的结果拷贝到sideEffect,后续遍历再通过cap()取值
g("thinkerpop").E("1;2;3;4;5;6").hasLabel("created").groupCount("x").by("pk").groupCount("y").by("weight").cap("x","y")
==> {"\"x\"":{"\"1\"":1,"\"4\"":2,"\"6\"":1},"\"y\"":{"0.2":1,"0.4":2,"1.0":1}}