导读
原文500 Lines or Less Dagoba: An In-Memory Graph Database中语言比较精炼,在讲解代码的时候并不是按照“先定义再引用”的顺序来展示的,而是倾向于把代码“语义化”来理解。所以会出现前面引用到的函数,后面还才会实现的情况。
再由于原作者使用了很多JavaScript语言的弱类型和动态解释的特性,导致难以像强类型和编译型语言一样通过语言本身的约束来推理代码意图,因为约束太弱导致可能性太多了。
所以这里重新整理一下,按照先提纲后细节的顺序,从整体设计讲起。
类图
这里的类图中的数据类型并不能做到完全准确,因为JavaScript是弱类型的语言,所以一个变量可以多次存放不同类型的值,而原实现里确实也这么做了。
图的存储结构
Dagoba里实现的图是有向图,顶点是具备_out
,_in
出入边的。
由于原文用的是原生JS,也不是ES6的具备class的版本,所以“面向对象”的特性是通过原型+工厂方法实现的。这里为了便于理解,把它的用“原型+工厂模式”实现的概念,还原成类。
从上面可以看到,图的数据结构简单,只包含了顶点列表vertices
,和边列表edges
。另外,vertexIndex
作为一个映射是用来检索根据ID检索顶点的。在后面的持久化中,我们可以看到,实际上只会对顶点列表和边列表进行持久化,不会对其他属性,包括vertexIndex
这个ID-顶点的映射进行持久化。
而Vertex
顶点的属性则是:id
唯一标识,_out
出边列表,_in
入边列表。
Edge
边的属性则是:_in
入方向顶点,_out
出方向顶点。
查询类
查询类Query
持有属性如下:
graph
,要查询的图state
,查询时,执行步骤对应的状态(为了实现Pipetype流式处理和惰性计算)program
,查询时按优先级执行的步骤gremlin
,潜在的查询结果(命名来自Gremlin图遍历语言)
Program
对应的属性则是:
pipetype
,要执行的管道类型的名称args
,要执行的管道步骤的参数(保存参数用于惰性计算)
Gremlin
潜在的查询结果对应的属性:
result
,查询执行步骤对应的结果vertex
,潜在查询结果所属的顶点state
,查询执行步骤对应的状态
State
执行步骤对应的状态的属性:
vertices
,匹配该查询步骤的顶点列表edges
,查询步骤的gremlin
对应的vertex
的入边或出边列表gremlin
,当前查询步骤的潜在查询结果taken
,管道类型take
的计数器,记录已经获取了多少个结果as
,管道步骤结果的标签,用于查询步骤结果等操作。