g.V().has('person', 'first_name', 'Ted'). out('friends').values('first_name')==>Josh
4.7.1. g4.7.1.1. 表示图的遍历源4.7.1.2. 是所有遍历的基石4.7.1.3. 可以任意命名,但是TinkerPop图数据库在事务模式下的惯例是使用g4.7.1.4. Gremlin的关键概念:g != graph4.7.1.4.1. g指遍历源,而不是图4.7.2. V()操作4.7.2.1. 返回一个包含图中每个顶点的迭代器4.7.2.2. 两个全局图操作之一4.7.2.3. 另一个全局图操作是E()4.7.2.3.1. 返回一个包含图中每条边的迭代器4.7.2.3.2. 为了维护或基于数据完整性考虑时才使用4.7.2.4. 遍历的第二个操作始终是这两个操作之一4.7.2.5. 使用V()从顶点开始遍历是目前最常见的做法4.7.2.5.1. 在遍历中,几乎总是从V()开始4.7.2.6. 为事务操作编写的每次遍历几乎都是从一个或一组顶点开始的4.7.3. has()操作4.7.3.1. 筛选操作4.7.3.2. 它只经过满足以下筛选条件的顶点或边4.7.3.2.1. 匹配指定的标签(如果指定了)4.7.3.2.2. 具有与指定键-值对匹配的键-值对4.7.3.3. hasLabel(label):返回匹配指定标签类型的所有顶点或边4.7.3.4. has(key,value):返回匹配指定键-值对的所有顶点或边4.7.3.5. has(label,key,value):返回同时匹配标签类型和指定键-值对的所有顶点或边4.7.3.5.1. g.V().hasLabel('person').has('first_name', 'Ted')4.7.3.5.1.1. 等同4.7.3.6. 出于负载和性能的考虑必须尽快缩减起始遍历器的数量4.7.3.7. 起始位置越少通常意味着遍历图的总体工作量越少4.7.3.7.1. 在遍历的第一个操作中将可能的顶点筛选为具有一个或多个has()操作的小子集是很常见的4.7.4. out(label)操作4.7.4.1. 遍历操作4.7.4.2. 遍历所有出边到带有指定标签的相邻顶点(如果指定了标签)4.7.4.3. 如果没有指定标签,那么就会遍历所有出边4.7.4.4. 沿任一方向遍历关系的灵活性是图数据库的基本功能,但也可能是一把双刃剑4.7.4.5. 方向性会筛选我们的遍历,虽然既有助于可读性又有助于性能,但也有局限性4.7.4.6. 另一个常见的遍历操作是in(label),它将遍历所有入边到带有指定标签的相邻顶点(如果指定了标签)4.7.4.7. both(label)4.7.4.7.1. 沿着给定标签的边从一个顶点遍历到相邻顶点4.7.4.7.2. 同时在入和出两个方向遍历边4.7.5. values(keys...)操作4.7.5.1. 值操作检索属性4.7.5.2. 返回元素属性的值4.7.5.3. 如果元素有N个属性,那么输出将包含N行4.7.5.4. 如果指定了一个或多个键,则仅返回具有这些键的属性4.7.5.5. valueMap(keys...),它返回匹配这些键的属性(包括键和值)5. 递归遍历5.1. 循环遍历5.2. 处理需要连续多次执行遍历某些部分的问题5.2.1. 物料清单5.2.1.1. 标准物料清单由多个零件组成,每个零件又由多个零件组成,这些零件还是由多个零件组成5.2.2. 地图导航5.2.2.1. 给定地图上的两个位置,提供从起始位置到结束位置的街道和转弯的列表5.2.2.2. 尽管这两个位置是相连的,但是无法提前预测所需的转弯次数5.2.3. 任务依赖关系5.2.3.1. 对于每一个项,都可以将其链接到任何它依赖的工作项,也就是说在图中将这些项连接到它们的依赖项,以此类推5.3. 图数据库是为处理高度互连的数据而优化过的,因此图数据库的查询语言和底层数据结构也经过优化,能快速执行递归查询5.3.1. 在关系数据库中,这可能会通过递归CTE来处理,很难编码和维护6. 使用Gremlin编写递归遍历6.1. 为Ted找到“朋友的朋友”6.2. apig.V().has('person', 'first_name', 'Ted'). out('friends'). out('friends'). values('first_name')==>Hank
6.3. apig.V().has('person','first_name','Ted'). repeat( out() ).until(has('person','first_name','Dave')). values('first_name')
6.3.1. repeat(traversal)6.3.1.1. 重复循环遍历操作,直到接收到停止指示为止6.3.1.2. traversal参数表示要在循环中重复的一组Gremlin操作6.3.2. until(traversal)6.3.2.1. repeat()循环的修饰符6.3.2.2. traversal参数表示要为每次循环计算一遍的一组Gremlin操作6.3.2.3. 当traversal参数里的计算结果为true时,退出repeat()操作6.3.2.4. 对于不知道需要递归多少次的情况,使用until()操作6.3.2.5. until()操作允许持续循环,直到满足指定的条件为止6.3.2.6. 可能会产生性能问题,因为遍历会一直运行到满足条件为止6.3.2.7. 如果条件从未满足,则继续执行,直到耗尽图中所有可能的路径6.3.2.7.1. 无界遍历6.3.2.8. 建议使用times()操作指定最大迭代次数,或者使用timeLimit()操作指定时间限制6.3.3. 如果until()操作在repeat()操作之前,则循环作为while-do循环运行6.3.3.1. 在循环开始就检查6.3.3.2. 可能根本不执行6.3.3.3. apig.V().has('person', 'first_name', 'Ted'). until(has('person', 'first_name', 'Hank')). repeat( out('friends') ). values('first_name')==>Hank
6.3.4. 如果until()操作在repeat()操作之后,则循环作为do-while循环运行6.3.4.1. 在循环末尾才检查表达式6.3.4.2. 总是至少执行一次6.4. apig.V().has('person', 'first_name', 'Ted'). repeat( out('friends') ).times(2). values('first_name')==>Hank
6.4.1. times(integer)6.4.1.1. repeat()循环的修饰符6.4.1.2. integer参数表示要循环执行的次数6.5. apig.V().has('person', 'first_name', 'Ted'). until(has('person', 'first_name', 'Hank')). repeat( out('friends') ).emit(). values('first_name')==>Josh==>Hank==>Hank
6.5.1. emit()操作6.5.1.1. emit()操作通知repeat()操作在循环当前位置发送值到控制台6.5.1.2. emit()操作与until()操作类似,放在repeat()操作之前或之后会影响它的行为6.5.1.3. 如果emit()放置在repeat()之前,会包含起始顶点6.5.1.4. 如果emit()放置在repeat()之前,会包含起始顶点6.5.1.5. 仅仅更改emit()操作的位置也会修改递归循环的结果6.5.1.5.1. 灵活性是以增加复杂性为代价的6.6. 如果将在图中编写递归查询的简单性与在SQL中回答相同类型问题的复杂性进行比较,你会开始注意到为什么图数据库擅长回答这类问题