如何用单条Gremlin查询从AWS Neptune加载节点及其子节点与边?
问题描述
我正在修改一个适配Spring Boot的Java库,用来集成AWS Neptune。为兼容该库此前对外暴露的API,需要从Neptune中加载一个节点及其所有子节点,同时获取节点到子节点的关联边。但目前我无法通过单条Gremlin查询实现此需求,只能通过两次独立查询完成(现有实现代码如下),这对性能影响较大。有没有更优雅、更优化的实现方式?
注:节点包含
entityId和name属性
现有实现代码
@Repository @Slf4j public class VisibilityRepositoryGremlin { private final GraphTraversalSource g; @Autowired private Client client; @Autowired public VisibilityRepositoryGremlin(GraphTraversalSource g) { this.g = g; } public Mono<Node> findVisibleNode(UUID originEntityId, String originLabel, UUID targetEntityId, String targetLabel, boolean isPrivileged) { return g.V() .hasLabel(originLabel) .has(Node.ENTITY_ID_PROPERTY, originEntityId.toString()) .repeat(outE(Node.CAN_SEE_REL_TYPE, CONTAINS_REL_TYPE) .has(VisibilityGroupRelationship.VISIBILITY, isPrivileged ? within(VisibilityGroupRelationship.Visibility.PRIVILEGED.name(), VisibilityGroupRelationship.Visibility.STANDARD.name()) : within(VisibilityGroupRelationship.Visibility.STANDARD.name())) .otherV().dedup()) .until(hasLabel(targetLabel) .has(Node.ENTITY_ID_PROPERTY, targetEntityId.toString())) .elementMap().fold().next() .stream() .map(VisibilityRepositoryGremlin::getNodeFromVertexProps) .map(vg->findById(vg.getId())) .findAny().orElse(Mono.error(new NotFoundException(("Entity not found")))); } @SuppressWarnings("unchecked") public Mono<Node> findById(String s) { List<Map<String, Object>> result= g.V().hasId(s) .project("visibilityGroup", "children") .by(elementMap()) .by(outE().hasLabel(CONTAINS_REL_TYPE) .project("edge", "visibility") .by(inV().elementMap()) .by("visibility") .fold()) .fold().next(); if (result.isEmpty()) return Mono.error(new NotFoundException("Not found")); Node vg = getNodeFromVertexProps((Map<Object, Object>)result.get(0).get("visibilityGroup")); List<Map<Object, Object>> childrenMaps = (List<Map<Object, Object>>)result.get(0).get("children"); childrenMaps.forEach(map -> { Map<Object, Object> edgeProps = (Map<Object, Object>) map.get("edge"); Node child = getNodeFromVertexProps(edgeProps); if (VisibilityGroupRelationship.Visibility.valueOf((String)map.get("visibility")) == VisibilityGroupRelationship.Visibility.PRIVILEGED) vg.addExplicitlyVisibleChild(child); else vg.addChild(child); }); return Mono.just(vg); } private static Node getNodeFromVertexProps(Map<Object, Object> r) { return Node.builder() .id(r.get(T.id).toString()) .entityId(UUID.fromString(r.get(Node.ENTITY_ID_PROPERTY).toString())) .nodeName(r.get("nodeName").toString()) .label(r.get(T.label).toString()) .build(); } }
优化方案
可以将两次查询合并为单条Gremlin查询,一次性获取目标节点、子节点及关联边信息,避免多次请求带来的性能损耗。核心思路是在找到目标节点的遍历流程中,直接嵌套查询子节点和边的属性,无需发起二次请求。
改写后的findVisibleNode方法
public Mono<Node> findVisibleNode(UUID originEntityId, String originLabel, UUID targetEntityId, String targetLabel, boolean isPrivileged) { // 定义可见性过滤条件 P<String> visibilityFilter = isPrivileged ? within(VisibilityGroupRelationship.Visibility.PRIVILEGED.name(), VisibilityGroupRelationship.Visibility.STANDARD.name()) : within(VisibilityGroupRelationship.Visibility.STANDARD.name()); return Mono.fromSupplier(() -> g.V() .hasLabel(originLabel) .has(Node.ENTITY_ID_PROPERTY, originEntityId.toString()) .repeat(outE(Node.CAN_SEE_REL_TYPE, CONTAINS_REL_TYPE) .has(VisibilityGroupRelationship.VISIBILITY, visibilityFilter) .otherV().dedup()) .until(hasLabel(targetLabel) .has(Node.ENTITY_ID_PROPERTY, targetEntityId.toString())) // 一次性获取目标节点、子节点及边属性 .project("targetNode", "children") .by(elementMap()) .by(outE(CONTAINS_REL_TYPE) .project("childNode", "visibility") .by(inV().elementMap()) .by("visibility") .fold()) .next()) .map(result -> { // 转换目标节点 Node target = getNodeFromVertexProps((Map<Object, Object>) result.get("targetNode")); // 处理子节点与边的关联关系 List<Map<Object, Object>> childrenData = (List<Map<Object, Object>>) result.get("children"); childrenData.forEach(childMap -> { Node child = getNodeFromVertexProps((Map<Object, Object>) childMap.get("childNode")); VisibilityGroupRelationship.Visibility visibility = VisibilityGroupRelationship.Visibility.valueOf((String) childMap.get("visibility")); if (visibility == VisibilityGroupRelationship.Visibility.PRIVILEGED) { target.addExplicitlyVisibleChild(child); } else { target.addChild(child); } }); return target; }) .onErrorResume(e -> Mono.error(new NotFoundException("Entity not found"))); }
优化点说明
- 合并查询逻辑:在找到目标节点的同一条Gremlin遍历中,通过
project步骤同时获取目标节点属性、子节点信息及边的visibility属性,彻底消除二次查询。 - 降低性能损耗:单次请求即可获取所有需要的数据,减少了网络往返延迟和Neptune连接开销。
- 简化代码结构:无需再调用
findById方法,逻辑更紧凑,减少代码冗余。 - 保持API兼容:返回的
Node对象结构与原有实现完全一致,不会影响上层调用逻辑。
额外建议
- 若数据量较大,可在Gremlin查询中添加
limit或分页逻辑,避免单次返回过多数据导致内存压力。 - 确保Neptune针对
entityId、label等查询字段创建了合适的索引,进一步提升查询效率。
内容的提问来源于stack exchange,提问作者CptWasp
相关产品推荐
相关产品推荐

