You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用单条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")));
}

优化点说明

  1. 合并查询逻辑:在找到目标节点的同一条Gremlin遍历中,通过project步骤同时获取目标节点属性、子节点信息及边的visibility属性,彻底消除二次查询。
  2. 降低性能损耗:单次请求即可获取所有需要的数据,减少了网络往返延迟和Neptune连接开销。
  3. 简化代码结构:无需再调用findById方法,逻辑更紧凑,减少代码冗余。
  4. 保持API兼容:返回的Node对象结构与原有实现完全一致,不会影响上层调用逻辑。

额外建议

  • 若数据量较大,可在Gremlin查询中添加limit或分页逻辑,避免单次返回过多数据导致内存压力。
  • 确保Neptune针对entityId、label等查询字段创建了合适的索引,进一步提升查询效率。

内容的提问来源于stack exchange,提问作者CptWasp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:35:56