1.Gremlin Server只将数据存储在内存中1.1.如果停止Gremlin Server,将丢失数据库里的所有数据2.概念2.1.遍历(动词)2.1.1.当在图数据库中导航时,从顶点到边或从边到顶点的移动过程2.1.2.类似于在关系数据库中的查询行为2.2.遍历(名词)2.2.1.要在图数据库中执行的一个或多个操作2.2.1.1.要么返回数据,要么进行更改2.2.2.在关系数据库中与之对应的是实际的SQL查询2.3.遍历源(traversal source)2.3.1.TinkerPop特有的概念2.3.2.表示遍历图操作的起点或基点2.3.3.通常用变量g表示,并且需要位于任何遍历的开头2.3.4.从遍历源开始遍历,通过每个分支发送一个遍历器来遍历图2.4.遍历器(traverser)2.4.1.与遍历执行特定分支相关联的计算过程2.4.2.遍历器维护相关图当前分支移动的所有元数据2.4.2.1.当前对象、循环信息、历史路径数据等2.4.3.唯一遍历器表示通过数据的每个分支2.4.4.可以被删除,也可以带着结果返回3.遍历图的过程3.1.找到起始顶点,确定要遍历的边,遍历该边,最后到达目标顶点完成遍历3.2.遍历图需要我们了解图的结构,我们任何时间在图中的位置,以及每个位置的相邻边、相邻顶点和可用属性3.3.遍历图数据库的重点是从一个元素遍历到另一个元素3.3.1.在清楚地陈述业务问题并彻底理解用例之后,应该会发现我们的逻辑模型和已识别出的相关模式元素有助于编写遍历3.4.通过多个并行进程遍历图3.4.1.每个并行进程都称为遍历器3.5.遍历是一系列操作3.5.1.遍历的每个操作都是从一个位置开始,并且(几乎总是)在不同的位置结束3.5.2.每个操作都从上一个操作结束的位置继续3.6.遍历需要知道我们在图中的位置3.6.1.在关系数据库中,SQL查询能够在查询的任意点连接任意两个表3.6.2.在图中,则只能使用图中当前位置旁边的边或顶点3.6.3.为了有效地在整个图中导航,必须跟踪我们在图数据模型结构中的位置3.6.3.1.最难掌握的技能3.7.边的方向很重要3.7.1.边的有向性是图数据库的一个关键能力,对于筛选或决定要遍历哪些边非常有用3.7.2.关系的这种方向性与关系数据库中不同,后者中的所有关系都是双向的3.7.3.在图数据库中,不仅要决定边的方向,还要确定我们希望如何遍历该边3.7.3.1.只遍历入边、出边,还是同时遍历两者3.8.遍历并不包含历史记录3.8.1.在图数据库中,从遍历返回的唯一值是结束顶点4.使用Gremlin编写遍历4.1.TinkerPop允许在Gremlin代码中使用任何模式,因此本身完全避免了模式定义的问题4.2.所有图查询语言都普遍需要理解筛选及边的方向性才能在图中移动4.3.一旦从关系数据库转移到图数据库里这种根据当前位置来考虑遍历的思考方式,我们就养成了利用数据中关系的必备思维习惯4.4.遍历API4.4.1.按照惯例以变量g开头:g =graph.traversal()4.5.内部API4.5.1.专为创建图数据库引擎的开发人员而设计4.5.2.图API4.5.2.1.就像关系数据库中可以通过C/C++、C#或Java等编程语言直接操纵位于SQL语言抽象之下的具体数据库文件的API4.5.3.它是一个接口,用于为Vertex(顶点)、Edge(边)、VertexProperty(顶点属性)和Property(属性)对象的集合定义容器对象4.5.4.它也是一种数据结构,不能提供有效的导航方式,只能提供在图中定位单个数据元素的最基本能力4.6.谁是Ted的朋友4.7.api
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.api
g.V().has('person', 'first_name', 'Ted'). out('friends'). out('friends'). values('first_name')==>Hank
6.3.api
g.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.api
g.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.api
g.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.api
g.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中回答相同类型问题的复杂性进行比较,你会开始注意到为什么图数据库擅长回答这类问题