1.1. 在图中查找数据是一项技能,但是高效地返回数据则是一种全新的挑战
1.2. 最好在数据库层尽可能多地对数据进行处理
1.2.1. 客户端应用程序处理用户交互就够忙的了
1.3. 图数据库不返回元素属性,因此必须显式请求这些属性
2. 值操作2.1. SELECT ROWID FROM person WHERE first_name = 'Ted';
2.1.1. g.V().has('person', 'first_name', 'Ted')==>v[4]
2.2. 大多数数据库要求指定要返回的特定属性
2.2.1. 在SELECT子句中使用通配符(*)是SQL中的一种常见做法
2.3. 在Gremlin中,使用values()和valueMap()等操作以所需的形式检索值
2.4. values()
2.4.1. 尽管采用了单词的复数形式,但是values()最常用于返回标量,特别是单个属性的值
2.4.2. 它仅返回属性的值部分,而没有键或标签,所以请求代码必须知道调用的是哪个属性
2.4.3. 使用带属性值的键可以更容易地处理结果
2.4.4. 采用复数形式因为values()被设计为处理一个或多个属性,并且能将其与很少使用的value()操作区分开
2.4.4.1. g.V().has('person', 'first_name', 'Hank').values()==> Hank
2.4.5. 通常更喜欢valueMap()操作
2.4.5.1. g.V().has('person', 'first_name', 'Hank').valueMap()==>{first_name=[Hank]}
2.4.6. 空values()操作
2.4.6.1. 使用空values()操作通常是个坏主意
2.4.6.2. 相当于运行使用通配符的SQL SELECT查询
2.5. 图数据库引擎处理遍历的方式与SQL引擎处理查询的方式之间存在一个关键的区别
2.5.1. 在图数据库中,只检索当前顶点或边的值
2.5.1.1. 图结果只包含了产品顶点的属性
2.5.1.2. 在图数据库中,遍历中任何操作的输出都是当前顶点或边的集
2.5.2. 在关系数据库中,所有联接表(joined table)的所有值都可以包含在结果中
2.5.2.1. SQL结果同时包含了关于订单和产品的信息
2.5.2.2. 在关系数据库中,join操作的输出结果是所有联接表的组合
3. 构建结果3.1. 在订单处理图中,获取订单/产品组合结果的操作
3.1.1. 找到图中的所有order顶点
3.1.2. 给这些顶点起一个标记为O的别名
3.1.3. 通过contains边遍历到product顶点
3.1.4. 给这些顶点起一个标记为P的别名
3.1.5. 从标记为O的元素返回所有属性,并从标记为P的元素返回所有属性
3.1.5.1. 当返回别名元素时,所有这些元素都必须有别名,而不仅限于中间遍历元素
3.2. 最好将代码分成小块编写,并尽早、经常地进行测试
3.2.1. 经常练习是指不断试错,直到得到预期的结果
3.3. 在遍历过程中使用as()给对元素起别名
3.3.1. as():为上一个操作的输出分配一个(或多个)标签,这些标签可以在同一遍历中被后面的操作访问
3.3.2. SELECT alias_name.* FROM table AS alias_name;
3.3.3. g.V().hasLabel('table').as('alias_name')
3.3.4. 给遍历的每个操作分配一个as()可能很诱人,但使用它是有代价的
3.3.5. 最佳做法是只给计划在后面遍历需要检索的操作起别名
3.3.6. 遍历中的别名允许在后面的操作中引用前面操作的结果,从而支持组合强大的遍历
3.4. select(string[]):选择先前遍历中的别名元素
3.4.1. 该操作总是回顾遍历中的先前操作以找到别名
3.5. by()调节器只作用于另一个操作的上下文
3.5.1. by(key):指定属性的键,以从相应的别名元素返回值
3.5.2. by(traversal):指定要对相应别名元素执行的遍历
3.5.3. 使用by()语句令人困惑的一个方面是,在select()语句里指定的每个别名元素都应该有一个对应的by()语句,以指示要对其执行的操作
3.5.4. by()操作的顺序还要对应于指定别名的顺序
3.5.5. 严格来说,by()语句的数量可能比引用的元素多,也可能比引用的元素少
4. 投射结果而不应用别名4.1. 与其在遍历中回溯以获取之前的结果,不如从当前元素向前投射结果
4.2. 投射结果与以前检索结果的方式不同,是一种简单而微妙的方式
4.3. 当投射结果时,则会创建新的结果,可能会延伸分支到尚未遍历的项
4.4. 选择是使用顶点、属性或其他遍历表达式来返回先前标记操作结果的过程
4.4.1. 选择总是回溯到遍历的之前部分
4.4.2. 选择使用select()操作,基于先前遍历的图元素创建结果集
4.4.3. 选择操作基于先前遍历的图元素创建结果集
4.5. 投射是使用顶点、属性或其他遍历表达式来创建从输入到当前操作的结果的过程
4.5.1. 投射总是向前移动,以输入的数据为起点
4.5.2. 投射使用project()操作,从图中当前位置发出分支并创建新对象
4.5.3. 投射操作从图的当前位置运行,并创建具有静态或计算属性的新对象
4.6. 选择操作和投射操作从多个顶点或边创建复杂的结果,从而允许组成复杂的结果结构
5. 对结果进行组织5.1. 使用order()、group()和groupCount()操作进行排序、分组或按组计数是转换结果的常用方法
5.2. limit()操作可以限制结果的数量,tail()操作用于返回最后X 条记录,range()操作允许对结果分页
5.3. 对图遍历返回的结果排序
5.3.1. order():将遍历到此点为止的所有对象收集到一个列表中,该列表根据附带的by()调节器排序
5.3.2. 默认按升序排列
g.V().hasLabel('person').values('first_name'). order(). by()==>Dave==>Denise==>Hank==>Jim==>Josh==>Kelly==>Paras==>Ted
5.3.3. 要按降序排列,则在by()操作中指定decr参数
g.V().hasLabel('person').values('first_name'). order(). by(decr)==>Ted==>Paras==>Kelly==>Josh==>Jim==>Hank==>Denise==>Dave
5.3.4. 随机地对数据排序,在by()操作中使用shuffle参数
g.V().hasLabel('person').values('first_name'). order(). by(shuffle)==>Dave==>Jim==>Ted==>Paras==>Kelly==>Hank==>Denise==>Josh
5.3.5. 首先,按值的降序排列,然后按键排序
5.3.5.1. 按键排序是一种打破僵局的好方法,可以确保得到相对确定的结果
5.4. 对图遍历返回的结果分组
5.4.1. group():根据指定的by()调节器对结果分组
5.4.1.1. 使用一个或两个by()调节器来对数据分组
5.4.1.2. 第一个by()调节器指定分组的键
5.4.1.3. 第二个by()调节器如果存在,将指定值;如果不存在,则将传入数据收集为与分组键相关联的值列表
g.V().has('person', 'first_name', 'Dave'). both(). both(). group(). by('first_name')==>{Denise=[v[19], v[19]], Ted=[v[4]], Hank=[v[6]], Paras=[v[17]], Josh=[v[2], v[2]], Dave=[v[0], v[0], v[0], v[0], v[0]], Kelly=[v[13]], Jim=[v[15]]}
5.4.2. groupCount():根据指定的by()调节器对结果分组和计数
5.4.2.1. 需要一个by()调节器来指定键
5.4.2.2. 值总是通过count()操作进行聚合
5.4.2.3. groupCount()操作只是一个小语法糖,最常用于group()操作——聚合计数
g.V().has('person', 'first_name', 'Dave'). both(). both(). group(). by('first_name'). by(count()). unfold()==>Denise=2==>Ted=1==>Hank=1==>Paras=1==>Josh=2==>Dave=5==>Kelly=1==>Jim=1
5.4.3. unfold():将一个可迭代或map结果的各个组成部分展开
g.V().has('person', 'first_name', 'Dave'). both(). both(). group(). by('first_name'). unfold()==>Denise=[v[19], v[19]]==>Ted=[v[4]]==>Hank=[v[6]]==>Paras=[v[17]]==>Josh=[v[2], v[2]]==>Dave=[v[0], v[0], v[0], v[0], v[0]]==>Kelly=[v[13]]==>Jim=[v[15]]
5.5. 限制结果记录的数量
5.5.1. 对结果进行组织的最后一个主题是返回数据的子集
5.5.2. 通常用于最小化结果数量或分页功能
5.5.3. limit(number):返回数据集的前number个结果
g.V().hasLabel('person').values('first_name'). order(). by(). limit(3)==>Dave==>Denise==>Hank
5.5.4. tail(number):返回数据集的后number个结果
g.V().hasLabel('person').values('first_name'). order(). by(). tail(3)==>Kelly==>Paras==>Ted
5.5.5. range(startNumber, endNumber):返回数据集中从第startNumber个(包含第startNumber个,从0算起)到第endNumber个(不包含第endNumber个)结果
g.V().hasLabel('person').values('first_name'). order(). by(). range(0, 3)==>Dave==>Denise==>Hank