I have a problem about implementing CRUD operations through the Neo4j query in Spring Boot.
My issue is located at both CityRepository, RouteRepository, ShortestPathRepository and Route entity.
1 ) When I called listAll and getById method of CityRepository, I get empty city name with listing its route after adding its route shown below.
[
    {
        "id": "077d1b16-9a4b-4fb0-947b-52031774d949",
        "name": "London",
        "routes": []
    },
    {
        "id": "077d1b16-9a4b-4fb0-947b-52031774d949",
        "name": null,
        "routes": [
            {
                "id": "6db5dd3f-085a-4d50-b025-4f0bee847fcf",
                "from": "London",
                "destination": "Berlin",
                "departureTime": "9:00",
                "arriveTime": "11:30",
                "duration": 2.5
            }
        ]
    }
]
2 ) (Edited) After adding any route of City and calling listAll of City, I got this result shown in the screenshot.
Here is my project link : Project
Here is the postman request collection : Link
Here are screenshots related with my issues : Link
How can I fix my issue?
Here are my entities as City and Route shown below.
City
public class City {
    @Id
    @Property
    private UUID id;
    @Property
    private String name;
    @Relationship(type = "ROUTES", direction = Relationship.Direction.OUTGOING)
    private Set<Route> routes = new HashSet<>();
    public City(String name) {
        this.name = name;
    }
}
Route
public class Route {
    @Id
    @Property
    private UUID id;
    @Property
    private String from;
    @Property
    private String destination;
    @Property
    private String departureTime;
    @Property
    private String arriveTime;
    @Property
    private Double duration;
}
Here is my CityRepository shown below.
public interface CityRepository extends Neo4jRepository<City,UUID> {
    @Query("MATCH (city:City) OPTIONAL MATCH (city)-[r:ROUTES]->(route:Route) RETURN city, collect(r), collect(route)")
    List<City> listAll();
    @Query("MATCH (city:City {id: $cityId}) OPTIONAL MATCH (city)-[r:ROUTES]->(route:Route) RETURN city, collect(r), collect(route)")
    City getById(UUID cityId);
    @Query("MATCH (city:City {name: $cityName}) RETURN city")
    City getByCityName(String cityName);
    @Query("CREATE (city:City {id: randomUUID(), name: $cityName}) RETURN city")
    City saveCity(String cityName);
    @Query("MATCH (city:City {id: $cityId}) SET city.name = $cityName RETURN city")
    City updateCity(UUID cityId, String cityName);
    @Query("MATCH (city:City {id: $cityId}) DELETE city")
    void deleteCity(UUID cityId);
}
Here is my RouteRepository shown below.
public interface RouteRepository extends Neo4jRepository<Route,UUID> {
    @Query("MATCH (city:City {id: $cityId})-[:ROUTES]->(route:Route) RETURN route")
    List<Route> listAllByCityId(UUID cityId);
    @Query("MATCH (route:Route {id: $routeId}) RETURN route")
    Route getById(UUID routeId);
    @Query("CREATE (city:City {id: $cityId})-[:ROUTES]->(route:Route {id: randomUUID(), from: $from, destination: $destination, departureTime: $departureTime," +
            "arriveTime: $arriveTime, duration: $duration}) " +
            "RETURN route")
    Route saveRoute(UUID cityId, String from, String destination, String departureTime,
                    String arriveTime, double duration);
    @Query("MATCH (city:City {id: $cityId})-[:ROUTES]->(route:Route {id: $routeId}) " +
            "SET route.from = $from, route.destination = $destination,route.departureTime = $departureTime," +
            "route.arriveTime = $arriveTime, route.duration = $duration RETURN route")
    Route updateRoute(UUID cityId, UUID routeId, String from, String destination,String departureTime,
                      String arriveTime,double duration);
    @Query("MATCH (city:City {id: $cityId})-[r:ROUTES]->(route:Route {id: $routeId}) DELETE r, route")
    void deleteRoute(UUID cityId, UUID routeId);
}


 
                        
Third update: There is a problem with the custom save query. With the
CREATE....in place, Neo4j will always create new entities. The safest way to create a new relationship with at least one existing node is to match on the existing node and then use it by naming reference in theMERGEstatement. I updated my project but here is basically the solution:Second update: Alright, I updated the project to include web tests now starting on the controller level. Please have a look and adjust it to reproduce the errors that I am missing out right now.
I cannot reproduce 1 and 4, please have a look at
com.springshortpath.app.ApplicationWebTests#listCitiesWithRoutes/com.springshortpath.app.ApplicationWebTests#listRoutesByCity.For 2 and 3: the query pattern demands to have (at least) one route defined. In the case of also wanting to get cities without routes, you would have to go with optional match.
For 5: Spring Data Neo4j's repositories can handle entity definitions only as a return type. If you want to use the Java driver's type, please use SDN's
Neo4jClientfor interaction. SDN documentation / Neo4jClientUpdate to the edited question:
For the deletion of the route: The error states that the relationship has to get removed before. The correct query should be
MATCH (city:City {id: $cityId})-[r:ROUTES]->(route:Route {id: $routeId}) DELETE r, routeFor the unable to find routes by city UUID: The direction of the relationship is wrong, the correct one is:
MATCH (city:City {id: $cityId})-[:ROUTES]->(route:Route) RETURN routeI could not reproduce the other problems but created test cases for what I understand in a fork of your repository: https://github.com/meistermeier/SpringBootNeo4jShortestPath
Old answers: In general, I see a misconception of
idproperty vs. theidof a node.You defined your id to be the internal Neo4j one.
As a result SDN will assign this value to the id field on save. The internal id of a node will be compared/retrieved via
id(node)in the Cypher query.As a consequence the query
MATCH (city:City {id: $cityId}) RETURN citywill not look for the right id field but for a propertyid.If you are still referring to the
CityRepositoryhere, you cannot see anyRoutebecause you don't return them. Altering the query toMATCH (city:City)-[r:ROUTES]->(route:Route) RETURN city, collect(r), collect(route)(or adding an optional clause to get the routes instead of requiring the pattern), will also add theRoutesto the correspondingCities.This should work as expected but I assume that you are referring to something else by saying couldn't see any result. It should be the same result as the method
City getByCityName(String cityName)including the id property change.For the rest of the questions, I bet it's the
node.idproperty vs.id(node)problem again. So basically every place you have(n:City|Route...{id: $id})should become(n:City|Route...) WHERE id(n) = $id.