1.1. 在图中解决问题需要转换思维方式,从遍历图的角度思考问题
1.2. skeleton(骨架版本)
1.2.1. 不含任何代码的应用程序骨架,只有为方法预留的桩(stub)。
1.2.2. 适合想自己编写代码的读者
1.3. commented(注释版本)
1.3.1. 项目所需的所有代码都已经写好,但被注释掉了
1.3.2. 适合不想敲代码但又想了解代码的读者
1.4. completed(已完成版本)
1.4.1. 完整的、可用的应用程序版本
1.4.2. 适合只想检视最终产品的读者
2. 开始项目2.1. 建立基于图的应用程序和建立其他基于数据的应用程序类似,都需要选择工具、建立项目、获取正确的驱动程序以及准备数据库服务器这些过程
2.2. 选择工具:开发语言和数据库
2.2.1. 使用什么开发语言
2.2.1.1. 使用Java作为开发语言
2.2.1.2. 常年位居排行榜前茅的编程语言Java(版本号为8)和常用的构建工具Maven(版本号为3.5)
2.2.1.2.1. 是使用图数据库时最常用的一对搭档
2.2.2. 基于什么数据库
2.2.2.1. 使用Gremlin Server作为数据库实现
2.2.2.2. 使用Apache TinkerPop引用实现,称为Gremlin Server
2.3. 建立软件项目
2.3.1. pom.xml文件
2.4. 选择合适的数据库驱动
2.4.1. 开发应用程序的一个首要任务就是指定一个合适的驱动程序来连接数据库
2.4.2. 只要使用数据库,不管是关系数据库还是图数据库,任何应用程序都需要驱动程序
2.4.3. 使用Apache TinkerPop Gremlin Driver来连接Gremlin Server
2.4.4. 对于C#项目,使用像NuGet这样的工具增加对Apache TinkerPop Gremlin.Net驱动程序的依赖
2.5. 准备数据库服务器实例
2.5.1. 使用Gremlin Server,可以在不预先定义图的模式或数据模型的情况下开始写代码
3. 连接数据库3.1. 连接图数据库包括配置合适的网络客户端和为TinkerPop数据库设置GraphTraversalSource
3.2. 建立数据库配置对象,称为Cluster
3.2.1. 在支持TinkerPop的图数据库中,这叫作集群配置(cluster configuration)
3.2.2. 该配置指定了应用程序与图数据库服务器连接的服务器和端口
3.2.3. 对于绝大多数图数据库服务器,每个实例仅支持一个图
3.2.3.1. 由于只有一个图,就不需要指定使用哪个
3.2.4. 有几个图数据库的服务器支持多个图
3.2.4.1. 在这些数据库中,必须使用官方提供的驱动程序
3.2.4.2. 需要额外的配置参数,如图的名字
3.3. 建立GraphTraversalSource
3.3.1. 远程/分离式或嵌入式/API
3.3.2. “分离式”的意思是图数据库不与驱动程序和应用程序在相同的处理和内存空间
3.3.3. TinkerPop能以两种模式运行
3.3.4. 嵌入模式
3.3.4.1. 图在应用程序的内存和处理空间中操作,而不是作为外部资源被访问
3.3.4.2. 这种配置需要添加额外的库到项目中
3.3.5. 网络模式
3.3.5.1. 数据库作为外部资源操作,通过网络连接
3.3.5.2. 市场上的所有图数据库都支持这种方式
4. 获取数据4.1. 使用Gremlin语言变体
4.1.1. GLV是TinkerPop独有的特性,提供TinkerPop的特定语言实现
4.1.1.1. 如果你熟悉.NET,GLV看起来就像在其生态系统中流行的语言集成查询(Language Integrated Query,LINQ)组件
4.1.1.2. 使用GLV得到的好处能抵消把字符串转换成本地语言的额外工作
4.1.1.3. 强烈建议你尽可能使用GLV风格的遍历
4.1.2. Gremlin之所以被创建,就是为了被嵌入多种编程语言中,并使用该语言的构造来表示遍历
Cluster cluster = Cluster.open();Client client = cluster.connect();ResultSet results = client.submit("g.V().hasLabel('person')");
4.1.3.1. 字符串提交、Groovy脚本或脚本提交
4.1.3.2. 字符串的序列化和反序列化会产生额外的开销,而且这种开销可能很大
4.1.3.3. 使用字符串拼接导致你的代码面临SQL注入(应该叫“Gremlin注入”)攻击的风险,除非持续使用参数化
4.1.3.4. 无法使用现代IDE的代码验证和自动补全能力
4.1.3.5. ResultSet的结果还是必须被强制转换成类型对象,但通过GLV,返回结果是自动强类型的
4.1.4. Apache TinkerPop提供了多种Gremlin语言变体(GLV)
4.1.4.1. 使用GLV可以利用IDE的代码补全功能和强类型结果
4.1.5. 把遍历转换成应用程序可用的代码,往往就像把每一个Gremlin操作替换成同名的方法调用一样简单
4.2. 获取一个顶点
4.2.1. // 这返回属性的列表
List properties = g.V(). has("person", "first_name", name). valueMap().toList();
4.3. 增加终点操作
4.3.1. 在为应用程序编写遍历时,必须以一个终点操作作为遍历的结束
4.3.2. return g.V().count().next();
GraphTraversal t = g.V();t = t.count();return (Long)t.next();
4.3.4. hasNext():返回一个布尔值,其中true代表有结果,false代表没有结果
4.3.5. tryNext():一个由hasNext()和next()操作组合而成的便捷方法,在有结果的情况下执行遍历
4.3.6. toList():以Java List的形式返回遍历的结果
4.3.7. toSet():以Java Set的形式返回遍历的结果
4.3.8. 终点操作在编写应用程序代码时是必需的
4.3.8.1. 应用的驱动程序不会自动添加它们
4.4. 在应用程序中创建Java方法
5. 添加、修改和删除数据5.1. 添加顶点
5.1.1. g.addV('person').property('first_name', 'Dave')
5.1.2. 在代码中使用GLV比采用JDBC或Groovy脚本的方式简单得多
5.1.3. 由于Gremlin是在JVM上开发出来的,至少在Java里,其语法是一样的
5.1.4. 每一个GLV都试图提供目标语言的原生开发体验
5.1.5. Gremlin遍历
5.1.5.1. g.addV('person').property('first_name', 'Dave')
5.1.6. Java语句
5.1.6.1. g.addV("person").property("first_name", name).next();
5.1.7. 归功于Groovy,Gremlin中的字符串既可以使用单引号,又可以使用双引号
5.1.8. 使用Java GLV时,Java必须使用双引号来引住字符串
5.1.9. 使用GLV的一个主要好处:返回的是不需要额外代码处理的通用对象
5.2. 添加边
g.addE('friends'). from( V().has('person', 'first_name', 'Dave') ). to( V().has('person', 'first_name', 'Josh') )
Edge newEdge = g.addE("friends"). from(V().has("person", "first_name", fromName)). to(V().has("person", "first_name", toName)). next();
Edge newEdge = g.addE("friends"). from(__.V().has("person", "first_name", fromName)). to(__.V().has("person", "first_name", toName)). next();
5.2.3.1. 带有两个下划线的元素叫作匿名遍历
5.2.3.2. 是Gremlin语言的一个特性,在其他图语言中没有直接的对照物
5.2.3.3. to()操作是一个调节器
5.3. 修改属性
5.3.1. g.V().has('person', 'first_name', 'Dav').property('first_name', 'Dave')
5.4. 删除元素
5.4.1. g.V().has('person', 'first_name', 'Dave').drop().iterate()
5.4.2. sideEffect(traversal):将被提供的遍历作为额外的过程进行处理,不影响传递给下一个操作的结果
5.4.3. store(alias):保存由alias指定的遍历集合的结果
5.4.4. cap(alias):输出由alias指定的结果集合
6. 转换清单和路径遍历6.1. 获取结果的清单
g.V().has('person', 'first_name', 'Ted'). out('friends').dedup(). values('first_name').next()
List<Object> friends = g.V().has("person", "first_name", name). out("friends").dedup(). values("first_name"). toList();
6.2. 实现递归遍历
g.V().has('person', 'first_name', 'Ted'). repeat( out('friends') ).times(2).dedup().values().next()
6.3. 实现路径
g.V().has('person', 'first_name', 'Ted'). until(has('person', 'first_name', 'Denise')). repeat( both('friends').simplePath() ).path().next()
List<Path> friends = g.V().has("person", "first_name", fromName). until(has("person", "first_name", toName)). repeat( both("friends").simplePath() ).path().toList();
6.3.3. path()返回的不仅是一个顶点或边,还有所有的中间操作
6.3.4. 以使用List<Path>而不是更通用的List<Object>
6.3.4.1. Path类是TinkerPop Java驱动中特有的类,它包含对象的有序列表;每个对象代表路径中的一个顶点或边