Reputation: 135
I am currently trying to query for all the paths from one node to another in a Neo4j database. I have been able to create nodes and relations with no problem. However, when it comes to mapping the result of a query in which I'm essentially trying to get all the paths (with the nodes and relations) between two nodes and map it to a Kotlin class, something goes wrong.
Here is my repository layer and the corresponding query:
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>>
}
When I run the query above the return value is as follows in the Neo4j Browser:
Therefore having a DTO as follows seemed perfectly fine to me:
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
)
However when I try to return the DTO in one of my endpoints I get the following error:
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
I also tried to change the return type of the repository function to List<Any>, even though the returned value from the query was something like:
what was returned in the response was:
[ { "empty": false, "false": false, "true": false, "null": false } ]
I tried to reach the variables stored in the path variable that was returned from the query (like path[0].values[0].values[0].adapted.start), but the IDE just did not allow me to. Not sure but it could be related to those variables being "fields"
So, I'd really appreciate it if you could help me figure this one out.
Upvotes: 0
Views: 134
Reputation: 1
yep, I just got the similar probs, but I found the solution, https://docs.spring.io/spring-data/neo4j/docs/6.0.15/reference/html/#projections.sdn.full-example this is the official api link, and this might help, and by the way neo4j's query annotation could only return the result in flat map, so it might not return the value as List<List<?>>
My Example(find routes between two nodes of which might not directly connected):
Repo:
@Query("MATCH p = (start:NODE {id: $startId})-[contact:CONTACT*]-(end:NODE {id: $endId}) " +
"WHERE start <> end AND " +
"NONE(node IN nodes(p)[1..-1] WHERE node = start) AND "+
"REDUCE (sum=0, rel IN relationships(p) | sum + rel.weight) <= $weightLimit "+
"RETURN nodes(p) AS routes")
Flux<NodeDTOProjection> findResultByCypher(@Param("startId")Long startId, @Param("endId") Long endId, @Param("weightLimit") double weightLimit);
And the service look like this:
@Override
public List<LinkedList<NodeDTOProjection>> retrieve(Long startId,Long endId,double weightLimit){
List<NodeDTOProjection>queryResult=pointRepo.findResultByCypher(startId,endId,weightLimit).collectList()
.blockOptional().orElse(Collections.emptyList());
if(queryResult.isEmpty())return Collections.emptyList();
List<LinkedList<RoutePointDTOProjection>>result=new ArrayList<>();
LinkedList<RoutePointDTOProjection> partialResult=new LinkedList<>();
for(RoutePointDTOProjection r:queryResult) {
if (r.getId().longValue() == endId.longValue()) {
partialResult.addLast(r);
result.add(partialResult);
partialResult = new LinkedList<>();
continue;
}
partialResult.addLast(r);
}
return result;
}
Upvotes: 0