全部产品
Search
文档中心

:Steps A-G

更新时间:Feb 20, 2024

aggregate

agg

  • 功能:(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}}