想象一下,你拿到了一张神秘的美食城市地图。这座城市的路口是“小店”,道路是“巷子”,而你接下来要做的,不是直接输入“我要找最辣的火锅”,而是拿着地图,一步步走街串巷:“从这个路口出发,向右转走到第二个路口,把所有卖甜品的店记下来,再顺着它们旁边的巷子看看有没有评分超高的隐藏摊位”。这种“一步步走、边走边记”的找店方式,就是图数据库世界里鼎鼎大名的 Gremlin 遍历语言。 在学习图数据库时,传统 SQL 的思维往往是“告诉我结果长什么样”,而 Gremlin 的思维则是“告诉我你打算怎么走”。它像一位经验丰富的城市向导,不坐直升机俯瞰全局,而是带着你脚踏实地,一条巷子一条巷子地穿行。整个过程由一连串“步骤”组成,每走一步,手里的清单就被过滤或扩充一次。这种“过程式”的风格,让复杂的关系查询变得直观而灵活。本文会带你用“美食探店”的视角,从零到一理解 Gremlin 的核心概念、常用步骤,以及它与 Cypher 的差异,让你在图数据库的选型与实战中多一件趁手兵器。
对于 AI 从业者而言,理解 Gremlin 意味着看懂图数据库查询的底层“行走逻辑”,在多跳关系分析、知识图谱构建与图计算引擎选型中拥有更清晰的决策依据。
可视化演示
动态演示Gremlin图查询的核心机制,通过开始/执行/监控/完成四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Gremlin图查询:一Gre 一、Gremlin的核心概念与 二、链式步骤组合与图查询管道 核心机制可视化 Gremlin图查询整体架构与关键组件
基础词汇
在开始逛这座“美食城市”之前,我们先认识地图上的基本元素。如果你手上拿到一张手绘美食地图,你首先会注意到两种东西:一种是画着店名的小圆圈或小房子,比如“老王麻辣烫”“阿婆糖水铺”;另一种是连接这些小房子的弯弯曲曲的线路,比如“从糖水铺出发,穿过小巷走到烧烤摊”。在图数据库的世界里,这些小房子被称为 顶点(Vertex),而那些线路被称为 边(Edge)。顶点和边合在一起,就构成了一张“图”。你可以把顶点想象成“店”,把边想象成“巷子”,而这条巷子还常常带着方向:有些是单行道,有些是双行道。
除了位置和连接方式,每家店门口还会挂着招牌和菜单——店名、营业时间、招牌菜、人均消费等。在图数据库里,这些信息被称为 属性(Property)。属性附着在顶点或边上,就像店面招牌上写着“本店招牌:螺蛳粉,辣度五颗星”。同时,每类店或每条巷子还可以有“标签(Label)”,比如“火锅店”“甜品店”“骑楼老街”。标签的作用类似于文件夹分类,帮你快速锁定某一大类目标。
有了这些基础概念,我们便可以开始“遍历”了。遍历(Traversal) 是指在图上从一个或多个起点出发,沿着边不断移动,同时收集沿途信息的过程。就像你从自己家出发,沿着巷子走到一家甜品店,再从那家甜品店旁边的巷子拐进一条老街——整个过程就是你的一次遍历。Gremlin 这门语言,本质上就是用来描述这种“从哪出发、怎么走、要记什么”的步骤序列。
遍历运行机制
现在你手里攥着地图,准备出发。但问题来了:如果你是“图数据库里的遍历”,你一次只会派一个人出门,还是派出一大群“分身”同时出门?Gremlin 的答案很有趣:它会让一个叫 遍历器(Traverser) 的家伙代表你出发。这个遍历器有两个特点:第一,它知道自己当前身处哪个顶点或边;第二,它随身携带一个“小背包”,里面可以存放一些临时笔记,比如“目前走了几步”“当前累计评分是多少”。在 Gremlin 中,这被称为 sack,我们暂时可以把它理解为“随身笔记本”。
遍历的过程就像“接力传递”。想象你派出 10 个分身,每个分身从不同的“店门口”出发。第一步指令是“沿着出巷走到下一家店”,那么每个分身都会分别走进自己当前所在店的每一条出巷,抵达新的店面。分身数可能会变多(一家店连着 3 条巷子,1 个分身就变 3 个),也可能会变少(两条巷子通向同一家店,3 个分身合并成 2 个)。每一轮新的指令,都作用在当前所有分身上,让它们各自前进、过滤或记录。Gremlin 把这一串分身的集合称为“遍历器流”。
Gremlin 还有一个“偷懒”的好习惯——惰性求值。就像你逛街时不会每走一步都拍一张照、记一大段笔记,而是等到有人问“你今天逛了哪些店”时才翻出记录开始整理。Gremlin 的每个步骤也不会立刻把全图数据都算完,而是“走一步看一步”。你写 g.V().out().out() 时,它并不会先把全图所有顶点都取出来再逐步过滤,而是从第一个顶点开始,一条巷子一条巷子地实时流动。这样在面对超大图时,它的内存占用和响应速度都更加可控。
正因为遍历器流的存在,Gremlin 才能实现“逐步变换”的灵活语义。每一步都接收一个遍历器流,吐出一个新的遍历器流。这就像美食探店时,每个步骤都在调整你手里的名单:第一步“找出所有火锅店”,名单可能是 20 家;第二步“只看评分 4.5 以上的”,名单缩到 5 家;第三步“只要营业到凌晨的”,最后只剩 2 家。名单在流动,而你在每个节点上只做一件简单的事。
- 遍历器集合用集合记号表示当前所有正在逛街的“分身”数量。
- 步骤作用箭头表示一个 Gremlin 步骤(如
out())被应用到当前遍历器流上。 - 新遍历器流箭头右边是步骤执行后产生的新“分身”集合,数量可能变化。
常用遍历步骤
这一节我们来认识 Gremlin 中最常用的几个“动作指令”。它们就像你在探店路上最常用的几个操作:往哪个方向走、看什么信息、按什么条件过滤、怎么分组计数。
首先是最基础的“走路”指令。out() 表示“从当前店沿着出巷走到下一家店”,in() 表示“沿着入巷倒着走回上一家店”,both() 则表示“不管方向,顺着所有巷子走”。如果你只关心某种类型的巷子,还可以传入标签参数,比如 out('friend') 表示“只走标记为 friend 的巷子”。就像你说“只走通往评分 4 星以上店家的巷子”。
接着是“查看信息”的指令。values('name') 用来读取当前顶点的 name 属性,相当于你走到店门口,抬头看招牌上的店名。has('cuisine', 'hotpot') 则是一个过滤指令:它检查当前顶点是否满足“cuisine 属性等于 hotpot”,不满足的分身会直接消失,就像你走到一家店门口,发现它不是火锅店,便扭头就走,连门都不进。
where 是一个更强大的过滤工具,它允许你指定复杂的条件。举例来说,where(out('friend').count().is(gt(2))) 的意思是“只保留那些朋友数量大于 2 的顶点”。这种“条件式筛选”在检查图结构关系时特别有用,比如筛选“被超过 10 人关注的大V”。而 filter 则提供一个更通用的布尔判断入口,让你用 lambda 或嵌套遍历自定义任意保留条件。
最后是统计类步骤。group() 可以把当前遍历器流按某个规则分组,比如“按照菜系分组,看看每个菜系有多少家店”。count() 则用于计数,比如计算“从这家店出发,沿着朋友巷子一共可以走到几位朋友”。如果你想做进一步的聚合,还可以配合 by() 指定分组的键或值的计算方式。
有了这些常用步骤,你就能像搭积木一样组合出非常丰富的查询。比如“找出我关注的、且发表了长文章的人”可以写成 g.V().has('name','我').out('follows').has('postCount', gt(10))。每一步都像在给名单做一次手术:先锁定自己,再走出关注巷子,然后过滤发文数。所有步骤串起来,就是一条完整的“逛街路线”。
out('label') 就是把当前顶点沿指定标签的边映射到邻居顶点集合。- 输入当前顶点
V是当前所在的顶点集合。 - 映射到邻居集合箭头表示“走到邻居顶点”,
E_{label}是带指定标签的边集合。
过程式特性
现在我们触及 Gremlin 最核心的个性——过程式。想象你在向朋友描述如何找到一家隐藏的深夜食堂,你有两种说法。说法一(声明式):“我想去一家评分 4.8 以上的居酒屋。” 朋友听了,自己去查地图、规划路线。说法二(过程式):“你先走到地铁站,出 B 口右转,直走 200 米看到便利店再左转,巷子尽头就是那家居酒屋。” 你详细描述了每一步怎么走。Gremlin 采用的是第二种方式。
这种风格带来的第一个好处是链式步骤组合。你要做的事被拆解为一系列串起来的步骤,前一步的输出就是后一步的输入。g.V().hasLabel('person').out('friend').values('name') 可以一口气读成:“从所有人出发,挑出 person,沿 friend 边走出去,读出 name 属性。” 每一步只干一件事,合起来却可以表达非常复杂的行为。
第二个好处是命令式思维带来的确定性。你清楚地知道遍历器每一步会走到哪里、过滤掉什么、保留什么。优化与否由你掌控,而不是把决定权完全交给查询引擎。在需要精细控制查询路径的场景(比如多跳安全分析、图算法中的逐步扩散),这种“手把手”的掌控感非常有价值。
第三个好处是灵活组合。由于步骤与步骤之间通过“遍历器流”衔接,你可以把步骤像乐高积木一样自由拼装——同一个 out('friend') 步骤可以用在完全不同的查询里。甚至可以把一组步骤定义为“步骤片段”并在多处复用。这就像你把“走到便利店左转”这个动作录制成一个快捷指令,以后在任何探店路线上都能直接调用。
所以,过程式的本质不是“更啰嗦”,而是“更可控”。当你的分析路径非常明确时,过程式可以让你精准地指挥遍历器走每一步,不走多余的弯路。当然,代价是你要自己想清楚每一步该怎么走——这正是 Gremlin 老手和新手的分水岭。
- 查询变量
q代表整个 Gremlin 遍历查询。 - 步骤复合每个
s_i是一个遍历步骤,\circ表示步骤之间的复合。
与Cypher对比
在图的周边,还有一种非常流行的查询语言叫 Cypher,它来自 Neo4j 图数据库。如果说 Gremlin 是“我给你详细路线”,那 Cypher 就是“我给你终极目标”。Cypher 是典型的声明式语言,它把“要在图里找到什么模式”描述清楚,至于怎么走、走哪条索引,由 Neo4j 引擎自己决定。两者的对比,就像“告诉导航你要去哪”和“告诉司机每条路怎么开”的区别。
这种差异在表达层面体现得非常明显。在 Cypher 里,匹配一个“朋友的朋友”的写法非常直观:
它像是在描述一张照片:“我要找这样一幅画面:一个人 A,通过 FRIEND 箭头连着 B,B 再通过 FRIEND 箭头连着 C。” 而在 Gremlin 里,同样的意图要写成:
你看,Gremlin 强调的是“从人出发,走两步 FRIEND 边”——走路的动作是显式的。Cypher 的 MATCH 更像图模式匹配的“通配符”,你只需把结构写出来;Gremlin 的步骤则是一台“指令机器”,每一步都在做具体的遍历动作。
再来看优化方式。因为 Cypher 是声明式的,Neo4j 引擎会在后台对查询做优化:选择合适的索引、调整连接顺序、利用统计信息估算代价。而 Gremlin 因为是过程式的,大部分情况下是“你指哪它打哪”——你怎么写,它就怎么执行(当然某些实现也会做局部优化,但控制权主要在你手里)。这带来一个有趣的结果:在 Gremlin 中,你可能需要自己研究“哪条遍历路径更高效”,而在 Cypher 中,你更依赖引擎的优化能力。
那么问题来了:选谁?这取决于场景。如果你的团队熟悉图模式思维、希望快速开发应用,Cypher 的声明式体验更友好;如果你在做深度的图算法、需要精确控制遍历行为,或者你正在使用 JanusGraph、TinkerPop 等多引擎生态,Gremlin 的过程式能力就更有优势。两者并非谁取代谁,而是服务于不同思维方式的好工具。
最后值得注意,Cypher 和 Gremlin 的学习曲线有所不同。Cypher 因为接近自然语言的 MATCH ... WHERE ... RETURN,初学者容易上手;Gremlin 的链式步骤则更接近“编程中的函数式 pipeline”,概念一旦建立,组合能力反而更强。对 AI 从业者来说,多掌握一种思维模型,在处理复杂关系数据时,就多一把锋利的刀。
- Cypher 端
WHAT to find表示 Cypher 的核心是描述想找的目标模式。 - 对照符号
\longleftrightarrow表示两者在思维方式上的对比关系。 - Gremlin 端
HOW to traverse表示 Gremlin 的核心是描述遍历的每一步方式。
MATCH (a:Person)-[:FRIEND]->(b)-[:FRIEND]->(c) RETURN cg.V().hasLabel('Person').out('FRIEND').out('FRIEND')项目实战与生态
让我们从“理论战场”回到“现实世界”。Gremlin 并不是一门只活在论文里的语言,它背后有一个完整的工业生态。这个生态的起点叫 Apache TinkerPop——你可以把它理解为 Gremlin 的“语言标准委员会”。TinkerPop 为图数据库定义了统一的遍历计算框架,也就是 Gremlin 图遍历机(Gremlin Machine)。任何实现了 TinkerPop 接口的图数据库,都能直接支持 Gremlin 查询。这就像一个“美食联盟”——只要一家店加盟了这个联盟,就得遵守统一的卫生评级和招牌标准。
在 TinkerPop 生态里,Groovy 是 Gremlin 最常用的宿主语言之一。Groovy 是一种运行在 JVM 上的动态语言,它允许你用很简洁的语法调用 Gremlin 步骤。实际上,你写的很多 Gremlin 遍历,本质上是在 Groovy DSL(领域特定语言)里描述遍历器流。比如 g.V().hasLabel('person') 中的 g、V()、hasLabel() 等在通过 Groovy 引擎解析执行。这也是 Gremlin 控制台(TinkerPop 自带的 REPL 环境)的命令基础。
因为 TinkerPop 的存在,Gremlin 获得了一个巨大的优势:多引擎支持。Neo4j 有自己的 Cypher,但 Gremlin 并不是某一家公司独占的语言。JanusGraph、Apache Cassandra 生态中的 DSE Graph、微软的 Cosmos DB 等图数据库产品都支持 Gremlin。这就像“同一套地道小吃的做法,被多家餐厅认可并写进菜单”。对于企业来说,这意味着如果你选择了 Gremlin,将来在多个图数据库之间迁移时,查询逻辑的重写成本会显著降低。
在实际项目中,使用 Gremlin 的典型路径是:先用 Gremlin Console 或 Python 客户端(gremlinpython)做探索式分析,再把成熟的遍历逻辑嵌入到后端服务中。比如,在知识图谱项目中,你可能需要回答“某个疾病相关的所有基因里,与药物靶点直接相连的有哪些”——这类多跳查询用 Gremlin 表达非常自然。也可以用 Gremlin 实现实时的个性化推荐:从当前用户出发,沿 “购买” 边找到商品,再沿 “被购买” 边找到相似用户,最后沿 “购买” 边推荐新商品。
总结来看,Gremlin 的生命力来自“标准 + 生态”的双轮驱动。对于开发者而言,掌握 Gremlin 不仅仅是在学一门语言,更是在为未来可能接触的各种图引擎做准备。就像你学会了“看地图找路”,不管走到哪座城市,都能从容不迫地逛起来。
- 图数据库
G表示一个支持 TinkerPop 的图数据库实例。 - 标准接口
\xrightarrow{\text{TinkerPop 接口}}表示通过 TinkerPop 的标准接口进行访问。 - 多引擎集合花括号内列出支持 Gremlin 的多个引擎。
AI 应用场景
知识图谱多跳问答
阶段:知识图谱构建与查询
解决的问题:传统关系型数据库在处理“实体间多跳关系”时需要大量 JOIN 操作,查询效率低且代码复杂。AI 系统在构建知识图谱后,需要一种既能精确表达图遍历路径、又能跨引擎运行的查询方式。
具体案例:在医疗知识图谱项目中,想回答“哪些药物可以作用于与疾病 P 相关的蛋白靶点?”这个问题。用 Gremlin 可以写成:从疾病节点出发,沿“相关”边走到蛋白节点,再沿“靶向”边走到药物节点。整个过程像“按图索骥”一样清晰可控,且能被 TinkerPop 生态内的多个图数据库执行。这比用 SQL 做 5 次 JOIN 要直观得多。
实时图推荐系统
阶段:推荐系统在线推理
解决的问题:传统的协同过滤推荐基于用户-物品二部图矩阵计算,难以捕捉用户之间的多跳关系;Gremlin 的过程式遍历可以在毫秒级内完成“用户→物品→相似用户→新物品”的路径推导,且无需预先物化所有候选集。
具体案例:在一个移动阅读 App 中,当用户点击了一篇技术文章后,推荐服务通过 Gremlin 执行 g.V().has('user_id', uid).out('read').out('read_by').out('read').limit(50),迅速找出“读了相似文章的用户还在读什么”。每步都沿着“读取”边在图上行走,就像沿着巷子拜访一家家隐藏小店。
图数据仓库ETL校验
阶段:数据工程与质量校验
解决的问题:在构建大规模图数据仓库时,需要定期校验图的完整性,比如“是否存在孤立顶点”“某类边的数量是否符合预期”。这些校验逻辑用 Gremlin 写成可复用的遍历脚本,能自动发现问题并生成告警。
具体案例:在某电商的图数仓中,每个订单顶点必须通过“下单”边连接到用户顶点。数据工程师每晚会执行 g.V().hasLabel('order').not(out('placed_by')).count(),找出所有“没有下单者”的异常订单。就像巡夜人提着灯笼逐条巷子确认每个店铺都亮着灯。
Python 演示
# -*- coding: utf-8 -*-"""演示 Gremlin 风格的过程式图遍历。不依赖真实图数据库,使用内存字典模拟图结构。""" class Traverser: """模拟 Gremlin 遍历器:携带当前位置与路径信息。""" def __init__(self, vertex, path=None): self.vertex = vertex self.path = path if path is not None else [vertex] def fork(self, next_vertex): """衍生新的遍历器,表示走到了下一个顶点。""" return Traverser(next_vertex, self.path + [next_vertex]) class InMemoryGraph: """极简内存图:顶点为字符串 ID,边为 (起点, 终点, 标签)。""" def __init__(self): self.edges = [] def add_edge(self, src, dst, label): self.edges.append((src, dst, label)) def neighbors(self, vertex, label=None): """返回从 vertex 出发、指定标签的出边邻居集合。""" result = [] for s, d, l in self.edges: if s == vertex and (label is None or l == label): result.append(d) return result def gremlin_select(graph, start, steps): """模拟执行一个 Gremlin 步骤序列。 参数: graph: InMemoryGraph 实例 start: 起始顶点 ID steps: 字符串列表,如 ["out('friend')", "out('friend')"] """ traversers = [Traverser(start)] for step in steps: new_traversers = [] for t in traversers: if step.startswith("out"): label = step[4:-1].strip("'\"") if "(" in step else None for nb in graph.neighbors(t.vertex, label): new_traversers.append(t.fork(nb)) traversers = new_traversers for t in traversers: print(" 经过:", " -> ".join(t.path)) print(" 本步存活遍历器数量:", len(traversers)) return traversers if __name__ == "__main__": g = InMemoryGraph() # 构建一个小型社交图: 用户 a 关注 b, c;b 关注 d;c 关注 d, e g.add_edge("a", "b", "friend") g.add_edge("a", "c", "friend") g.add_edge("b", "d", "friend") g.add_edge("c", "d", "friend") g.add_edge("c", "e", "friend") print("Gremlin 风格查询: 从 a 出发,沿 friend 边走两步") result = gremlin_select(g, "a", ["out('friend')", "out('friend')"]) print("最终抵达的节点:", sorted(set(t.vertex for t in result)))
out('friend') 逐步传递的语义。class Traverser:对应 Gremlin 的遍历器,保存当前位置和历史路径。g.add_edge("a", "b", "friend")建图:在用户 a 和 b 之间加一条“关注”边。g.neighbors(t.vertex, label)模拟out(label)步骤:获取当前顶点的出边邻居。t.fork(nb)一个遍历器走到多个邻居时“分身”,产生多个新遍历器。for step in steps:逐个执行步骤串,体现“链式处理”的过程式思想。本步存活遍历器数量直观展示每一步后遍历器流的规模变化。sorted(set(...))因为多个遍历器可能到达同一顶点,用集合去重后展示最终结果。
常见误区
误区很多初学者看到 Gremlin 的示例大都是 Groovy 语法,便以为 Gremlin 是 Groovy 的“语法糖”或专属插件,离开了 Groovy 就跑不起来。
正解Gremlin 是一套独立的遍历语言规范,Groovy 只是 Gremlin 最常用的宿主语言之一。TinkerPop 生态里还有 Java、Python(gremlinpython)、JavaScript(gremlin-javascript)、.NET 等多种语言的客户端,它们都能执行 Gremlin 遍历。你可以用 Python 写 g.V().hasLabel('person').out('friend'),也可以在 Java 服务里以 DSL 形式调用同一套语义。
为何常见:因为 TinkerPop 官方控制台(Gremlin Console)默认使用 Groovy REPL,绝大多数教程和书中示例都直接用 Groovy 展示,导致大家误以为 Gremlin = Groovy。
误区有些开发者听说 Cypher 是声明式的、引擎会做自动优化,就认为 Gremlin 这种“一步步跑”的过程式语言效率一定更低。
正解性能高低并不由“声明式还是过程式”单方面决定。Gremlin 在遍历路径明确时,可以减少优化器误判的情况;同时,许多图数据库(如 JanusGraph)会对 Gremlin 执行做索引加速和批量拉取。如果开发者精心设计遍历顺序(先过滤再扩展),Gremlin 的查询性能完全可以媲美甚至超过 Cypher。真正的性能瓶颈往往不在语言,而在数据模型和索引使用是否合理。
为何常见:Cypher 的生态以 Neo4j 为代表,Neo4j 对 Cypher 做了大量优化,这让大家形成了“声明式优化更好”的刻板印象,忽略了图引擎实现的具体差异。
误区有人觉得 Gremlin 是“命令式走迷宫”,所以它只能做简单路径遍历;一旦涉及到复杂的图模式匹配(如环检测、多条件模式),Gremlin 就力不从心了。
正解Gremlin 本身支持匹配步骤(match()),可以表达类似 Cypher 的图模式过滤。此外 where、filter、repeat 等步骤可以组合出非常强大的模式匹配逻辑。甚至可以说,Gremlin 的灵活性允许你手动实现自定义的匹配策略,而不用受限于优化器的黑盒行为。复杂模式匹配更多考验的是步骤设计能力,而非语言本身的表达能力。
为何常见:初学者看到的 Gremlin 示例大多是简单的 out()、values() 链式调用,没有接触过 match() 和 repeat() 等进阶步骤,因此形成了“Gremlin 只能走直线”的误解。
面试题
Q1. 在 Gremlin 中,顶点(Vertex)最贴切的生活化类比是什么?
- A.一条连接两地的巷子
- B.城市地图上的一家店面← 正确答案
- C.店面招牌上的菜单信息
- D.一张手绘地图的边框
答案:B。顶点对应“店”,边对应“巷子”,属性对应“招牌菜单”。B 正确。A 是边,C 是属性,D 是无关项。记不住时想想“路口是店,路边是巷”。
Q2. out('friend') 步骤在图遍历中表示什么动作?
- A.沿着 friend 边从当前顶点走到邻居顶点← 正确答案
- B.沿着 friend 边从邻居走回当前顶点
- C.只查看当前顶点的 friend 属性值
- D.删除所有 friend 边的连接
答案:A。out() 表示“出边方向”,从当前顶点走向邻居顶点。A 正确。B 是 in() 的行为,C 是 values() 的行为,D 是 drop() 的行为。
Q3. 关于 Gremlin 的“惰性求值”特性,以下说法正确的是?
- A.每一步会立刻计算全图所有数据
- B.查询会先加载全部顶点到内存再过滤
- C.遍历器按需逐个产出结果,避免全量计算← 正确答案
- D.它代表一种执行缓慢的缺点
答案:C。惰性求值意味着步骤不会急着把所有数据算完,而是等消费者需要时才继续推进。C 正确。A 和 B 是贪婪求值的做法,D 误解了惰性求值的意义——它是为了节省资源而“按需计算”。
Q4. 在 Gremlin 中,has('cuisine', 'hotpot') 步骤的主要作用是什么?
- A.新增一个 cuisine 属性
- B.过滤掉不满足条件的顶点← 正确答案
- C.把所有顶点的菜系改为火锅
- D.统计火锅店的数量
答案:B。has 是过滤步骤,只保留满足属性条件的遍历器。B 正确。A 是 property() 的行为,C 是批量更新的行为,D 需要配合 count() 使用。
Q5. 下面哪一项最准确描述了 Gremlin 与 Cypher 的核心区别?
- A.Gremlin 是声明式,Cypher 是过程式
- B.Gremlin 是过程式,Cypher 是声明式← 正确答案
- C.两者都是过程式语言
- D.两者都是声明式语言
答案:B。Gremlin 强调“怎么走”,是过程式;Cypher 强调“要什么”,是声明式。B 正确。两者的思维方式恰好对立,选反了就错了。
Q6. 下面哪些组件属于 TinkerPop 生态的核心内容?
- A.Gremlin 遍历语言规范← 正确答案
- B.Gremlin Console 交互式终端← 正确答案
- C.Neo4j 的 Cypher 编译器
- D.多种语言客户端(Java/Python/JS)← 正确答案
答案:ABD。TinkerPop 提供了 Gremlin 规范、Gremlin Console 以及多种语言客户端。A、B、D 都是其生态组成。C 的 Cypher 是 Neo4j 自家语言,不属于 TinkerPop。
Q7. 一个遍历器流从 1 个遍历器开始,经过 out('friend') 后变成 5 个,再经过 out('friend') 后变成 20 个。请问第二步的 out('friend') 平均每个遍历器扩展出了几个邻居?
- A.3
- B.4← 正确答案
- C.5
- D.20
答案:B。遍历器流规模从 5 变成 20,20 ÷ 5 = 4,平均每个扩展了 4 个。B 正确。这是过程式遍历“流式扩展”的典型数量关系,理解了遍历器流就理解了此题。
Q8. Gremlin 中 where 步骤相比 filter 步骤的优势体现在哪些方面?
- A.可以基于遍历结构进行条件判断← 正确答案
- B.可以前后关联多个遍历器路径← 正确答案
- C.可以把嵌套遍历作为条件子句← 正确答案
- D.只允许简单的属性值比较
答案:ABC。where 支持嵌套遍历和路径关联,适合复杂结构过滤。A、B、C 都正确。D 是错误描述,where 远比“简单属性比较”强大。
Q9. 在 TinkerPop 生态中,以下哪种说法正确描述了 Groovy 与 Gremlin 的关系?
- A.Gremlin 是 Groovy 的一个插件,必须在 Groovy 中运行
- B.Gremlin 是一种语言规范,Groovy 是它的宿主语言之一← 正确答案
- C.Groovy 只能用于编写 Cypher 查询
- D.Gremlin 和 Groovy 是同一语言的两种叫法
答案:B。Gremlin 独立于具体语言,TinkerPop 通过不同语言的 DSL 实现其语义。B 正确。A 和 D 混淆了规范与宿主的关系,C 完全说反了。
Q10. 在 Gremlin 查询中,以下哪些步骤的组合可以实现“从用户 A 出发,找出其好友喜欢的、且评分大于 8 的餐厅”这个查询?
- A.
out('friend')找到好友← 正确答案 - B.
out('like')找到好友喜欢的餐厅← 正确答案 - C.
has('rating', gt(8))过滤评分← 正确答案 - D.
count()统计餐厅数量后返回
答案:ABC。这个查询需要“沿着 friend 边走到好友,再沿 like 边走到餐厅,再用 has 过滤评分”。A、B、C 正确。D 的 count() 会把结果变成单个数字,丢失餐厅实体信息,不符合“找出餐厅”的语义。