DataStax Enterprise(简称DSE)在开源Cassandra的基础上增加了搜索引擎、分析和图计算能力,其中DSE Graph模块以Cassandra为存储后端,提供了基于Apache TinkerPop的Gremlin图查询语言。对于Node.js开发者来说,官方提供的cassandra-driver包从3.2版本开始就内置了DSE Graph支持,可以直接复用同一套驱动完成CQL查询和Gremlin遍历。本文将完整讲解环境准备、连接配置、Gremlin执行以及结果解析的全过程。

一、安装驱动与理解DSE Graph的基本架构
首先在项目里安装官方驱动,推荐使用DataStax维护的cassandra-driver包,它同时支持原生Cassandra协议和DSE的图协议:
npm install cassandra-driver
安装完成后需要理解一个关键点:DSE Graph并不是一个独立的数据库服务,它依附于DSE集群本身。图数据最终以Cassandra表的形式落盘,每个图的顶点和边都对应底层存储中的多张表,表名的格式一般是<图名>_vertex和<图名>_edge。这意味着你可以同时用CQL去查看图的底层存储,也可以用Gremlin去操作图,两种视角看的是同一份数据。
驱动的连接方式和普通Cassandra完全一致,都是通过接触点(contact points)加端口9042建立连接。区别在于执行图查询时,驱动会在协议层面携带额外的图选项,告诉DSE服务端这次请求要走Gremlin引擎而不是CQL引擎。理解了这一点,后面配置GraphOptions时就不会觉得突兀。
二、初始化客户端并配置GraphOptions
驱动的核心类是Client,初始化时除了基本的keyspace和主机地址,还可以在options中传入graphOptions来启用图功能。下面是一个典型的连接示例,包含了DSE认证和图选项两部分:
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({
contactPoints: ['192.168.0.1:9042'],
localDataCenter: 'dc1',
credentials: {
username: 'cassandra',
password: 'cassandra'
},
graphOptions: {
name: 'sample_graph', // 默认操作的图名称
source: 'g', // 遍历源
language: 'gremlin-groovy' // 查询语言
}
});
client.connect()
.then(() => console.log('已连接到DSE集群'))
.catch(err => console.error('连接失败:', err));graphOptions中有几个重要字段需要说明。name指定默认的图,之后的查询如果不再显式传入graph选项,都会作用到这个图上。source一般是g,代表标准遍历源。language字段决定Gremlin脚本的方言,老版本DSE使用gremlin-groovy,而较新的DSE 6.8版本引入了GremlinServer风格的查询方式,可以使用字节码形式的遍历,此时需要将language设置为bytecode-json并采用GraphSON序列化。
认证部分使用credentials字段传递用户名和密码,这与普通Cassandra连接一致。如果集群启用了Kerberos或TLS,还需要额外配置sslopts和authProvider。注意localDataCenter是必填项,驱动依赖它做负载均衡和副本感知路由,漏掉这一项在高版本驱动中会直接抛出异常。
三、执行Gremlin查询与结果解析
执行图查询使用client.executeGraph方法,第一个参数是Gremlin脚本字符串,第二个参数可以传入参数映射。下面演示创建一个图并插入两个顶点、一条边,然后做一次遍历查询:
async function runGraphDemo() {
// 创建图(如果已存在会报错,可忽略)
await client.executeGraph('system.graph("demo_graph").ifNotExists().create()');
// 切换到新图的选项
const graphOptions = { name: 'demo_graph' };
// 添加顶点,label为person
await client.executeGraph(
'graph.addVertex(label, "person", "name", "张三")',
null, { graphOptions }
);
await client.executeGraph(
'graph.addVertex(label, "person", "name", "李四")',
null, { graphOptions }
);
// 查询所有person顶点
const result = await client.executeGraph(
'g.V().hasLabel("person").values("name")',
null, { graphOptions }
);
result.forEach(item => console.log('顶点名称:', item));
}
runGraphDemo().catch(console.error);executeGraph返回的结果是一个GraphResultSet,它本身是可迭代的,每个元素都是GraphNode类型的包装对象。GraphNode很灵活,可以直接当字符串输出,也可以调用asObject拿到JavaScript原生对象,或用vertex、edge属性判断元素类型。例如查询边时要判断方向和关联顶点,就需要先取inV和outV再做后续处理。
参数化查询同样受支持。把容易变化的部分用mapPlaceholder传入可以避免脚本注入,写法类似CQL的占位符,只是图查询使用mapInline方式将参数内联到脚本中。对于批量写入场景,建议使用批量遍历或session级别的批量模式,能显著减少网络往返次数。
四、混合使用CQL与图查询的实践建议
DSE的一个优势是CQL和Graph可以共存,Node.js服务里可以创建两个Client实例分别处理两类请求,也可以在同一个实例上调用execute和executeGraph。实际项目中比较常见的分层方式是:关系型业务数据走CQL,例如用户账户、订单记录;关系网络和推荐路径走Gremlin,例如好友关系、商品关联图谱。两者共享同一个集群的伸缩性和高可用能力。
性能方面需要注意几点。第一,图查询尽量避免全图扫描,给label和属性建立二级索引,或者用vertex label的主键设计来收敛查询范围。第二,深度遍历的层数要控制,每次expand都会放大读取压力,通常不超过三到四层。第三,驱动侧开启prepare模式可以复用执行计划,对高频固定查询有明显收益。
常见报错也需要掌握排查方法。如果出现DSE graph is not enabled的错误,说明服务端节点没有开启graph功能,需要在cassandra.yaml或dse.yaml中启用后重启;如果出现图不存在的错误,检查graphOptions中name拼写以及是否用systemgraph语法创建了图;如果结果反序列化异常,多半是language和结果格式不匹配,尝试显式指定GraphSON版本即可解决。按照以上步骤,你就可以在Node.js中稳定地使用DSE Graph的完整能力了。