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

Spring Boot中无法将Neo4j路径查询结果映射到Kotlin数据类

问题:Neo4j路径查询结果映射Kotlin数据类失败

我在Neo4j中查询两个节点间的所有路径,节点和关系创建正常,但将包含节点和关系的路径结果映射到Kotlin数据类时出错。

仓库层代码及查询

import com.nextbus.nextbusapi.DTO.RelationshipDTO
import com.nextbus.nextbusapi.entity.MyNode
import org.springframework.data.neo4j.repository.Neo4jRepository
import org.springframework.data.neo4j.repository.query.Query
import org.springframework.data.repository.query.Param

interface MyNodeRepository : Neo4jRepository<MyNode, Long>{
    fun findByName(name:String): MyNode?

    @Query(
        """MATCH p = ( :MyNode {name: 'istanbul'})-[*]-( :MyNode {name: 'ankara'})
           WITH relationships(p) AS rels
           RETURN collect(rels)""",
    )
    fun findPathsBetweenNodes(
        @Param("startNodeName") startNodeName: String,
        @Param("endNodeName") endNodeName: String
    ): List<List<RelationshipDTO>>
}

定义的RelationshipDTO

data class RelationshipDTO(
    val identity: Long,
    val start: Long,
    val end: Long,
    val type: String,
    val properties: MutableMap<String, String>, 
    val elementId: String,
    val startNodeElementId: String,
    val endNodeElementId: String
)

报错信息

2024-02-14T16:10:27.669+03:00 ERROR 1884 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: LIST OF ANY? is not a keyed collection; Error code 'N/A'] with root cause

org.neo4j.driver.exceptions.value.NotMultiValued: LIST OF ANY? is not a keyed collection

尝试将返回类型改为List<Any>后,接口返回异常数据:

[
{
"empty": false,
"false": false,
"true": false,
"null": false
}
]


解决方案

1. 修复Cypher查询的参数与返回结构问题

原查询存在两个关键问题:

  • 硬编码节点名称,未使用方法传入的startNodeName和endNodeName参数
  • 使用collect(rels)将所有路径的关系合并为一个大集合,破坏了"每个路径对应一个关系列表"的结构

修改后的Cypher查询:

MATCH p = (start:MyNode {name: $startNodeName})-[*]-(end:MyNode {name: $endNodeName})
RETURN relationships(p) AS pathRels

对应的仓库方法:

@Query(
    """
    MATCH p = (start:MyNode {name: $startNodeName})-[*]-(end:MyNode {name: $endNodeName})
    RETURN relationships(p) AS pathRels
    """,
)
fun findPathsBetweenNodes(
    @Param("startNodeName") startNodeName: String,
    @Param("endNodeName") endNodeName: String
): List<List<RelationshipDTO>>

2. 调整DTO字段适配Neo4j Driver返回结构

Neo4j Driver返回的关系对象字段与你定义的DTO存在映射不匹配问题,调整后的DTO需对应Driver的关系属性:

data class RelationshipDTO(
    val identity: Long, // 对应关系的数值ID
    val elementId: String, // 对应关系的字符串ID(Neo4j 4+推荐)
    val start: Long, // 对应起始节点数值ID
    val startNodeElementId: String, // 对应起始节点字符串ID
    val end: Long, // 对应结束节点数值ID
    val endNodeElementId: String, // 对应结束节点字符串ID
    val type: String, // 关系类型
    val properties: Map<String, String> // 关系属性,若属性非全字符串可改为Map<String, Any>
)

3. 手动映射(更可靠的方案)

如果Spring Data自动映射仍有问题,可直接返回Neo4j Driver的Path对象,在服务层手动转换为DTO:

仓库层修改

@Query(
    """
    MATCH p = (start:MyNode {name: $startNodeName})-[*]-(end:MyNode {name: $endNodeName})
    RETURN p
    """,
)
fun findPaths(
    @Param("startNodeName") startNodeName: String,
    @Param("endNodeName") endNodeName: String
): List<org.neo4j.driver.types.Path>

服务层转换逻辑

fun getPaths(startNodeName: String, endNodeName: String): List<List<RelationshipDTO>> {
    val paths = myNodeRepository.findPaths(startNodeName, endNodeName)
    return paths.map { path ->
        path.relationships().map { rel ->
            RelationshipDTO(
                identity = rel.id(),
                elementId = rel.elementId(),
                start = rel.startNodeId(),
                startNodeElementId = rel.startNodeElementId(),
                end = rel.endNodeId(),
                endNodeElementId = rel.endNodeElementId(),
                type = rel.type(),
                properties = rel.properties().mapValues { it.value.asString() }
            )
        }
    }
}

内容的提问来源于stack exchange,提问作者Emre Tüker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 05:12:56