From 1fbb57f7947015fddda577b54fcd0701f219c5e7 Mon Sep 17 00:00:00 2001 From: Luiz Segundo Date: Sun, 22 May 2022 10:44:14 -0300 Subject: [PATCH 1/3] fix docker-compose --- docker-compose.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5f00903..f5a8d12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,10 +34,9 @@ services: condition: service_healthy # Wait for neo4j to be ready links: - neo4j-db - environment: - NEO4J_URI: bolt://neo4j-db:7687 - - NEO4J_PASSWORD: 123456 + environment: + NEO4J_URI: bolt://neo4j-db:7687 + NEO4J_PASSWORD: 123456 volumes: app-neo4j-db: \ No newline at end of file From 0307ef37e7fb70bf2ee2cd4af937542317f45af5 Mon Sep 17 00:00:00 2001 From: Luiz Segundo Date: Sun, 22 May 2022 11:27:59 -0300 Subject: [PATCH 2/3] remove application for docker-compose --- docker-compose.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f5a8d12..64c8fe2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,20 +23,5 @@ services: timeout: 10s retries: 5 - app: - image: 'springbootneo4jshortestpath:latest' - build: - context: . - dockerfile: Dockerfile - container_name: SpringBootNeo4jShortestPath - depends_on: - neo4j-db: - condition: service_healthy # Wait for neo4j to be ready - links: - - neo4j-db - environment: - NEO4J_URI: bolt://neo4j-db:7687 - NEO4J_PASSWORD: 123456 - volumes: app-neo4j-db: \ No newline at end of file From 9ee2585fcbae73d36762006d0fe9c31486e0c9ef Mon Sep 17 00:00:00 2001 From: Luiz Segundo Date: Sat, 11 Jun 2022 15:52:47 -0300 Subject: [PATCH 3/3] Generate citiId auto --- .../app/controller/CityController.java | 43 ++++++++-- .../app/controller/RouteController.java | 83 +++++++++++++++++++ .../controller/ShortestPathController.java | 59 +++++++++++++ .../com/springshortpath/app/dto/RouteDTO.java | 48 ++++++++++- .../com/springshortpath/app/model/City.java | 19 ++++- .../com/springshortpath/app/model/Route.java | 18 +++- .../app/payload/request/PathRequest.java | 13 +++ .../PathShortestConnectionResponse.java | 19 +++++ .../response/PathShortestTimeResponse.java | 20 +++++ .../app/repository/CityRepository.java | 17 ++-- .../app/repository/RouteRepository.java | 34 ++++++++ .../repository/ShortestPathRepository.java | 24 ++++++ .../app/service/CityService.java | 7 +- .../app/service/RouteService.java | 20 +++++ .../app/service/ShortestPathService.java | 13 +++ .../app/service/impl/CityServiceImpl.java | 13 +-- .../app/service/impl/RouteServiceImpl.java | 62 ++++++++++++++ .../service/impl/ShortestPathServiceImpl.java | 68 +++++++++++++++ 18 files changed, 551 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/springshortpath/app/controller/RouteController.java create mode 100644 src/main/java/com/springshortpath/app/controller/ShortestPathController.java create mode 100644 src/main/java/com/springshortpath/app/payload/request/PathRequest.java create mode 100644 src/main/java/com/springshortpath/app/payload/response/PathShortestConnectionResponse.java create mode 100644 src/main/java/com/springshortpath/app/payload/response/PathShortestTimeResponse.java create mode 100644 src/main/java/com/springshortpath/app/repository/RouteRepository.java create mode 100644 src/main/java/com/springshortpath/app/repository/ShortestPathRepository.java create mode 100644 src/main/java/com/springshortpath/app/service/RouteService.java create mode 100644 src/main/java/com/springshortpath/app/service/ShortestPathService.java create mode 100644 src/main/java/com/springshortpath/app/service/impl/RouteServiceImpl.java create mode 100644 src/main/java/com/springshortpath/app/service/impl/ShortestPathServiceImpl.java diff --git a/src/main/java/com/springshortpath/app/controller/CityController.java b/src/main/java/com/springshortpath/app/controller/CityController.java index bd0519b..08e5e4c 100644 --- a/src/main/java/com/springshortpath/app/controller/CityController.java +++ b/src/main/java/com/springshortpath/app/controller/CityController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.UUID; @RestController @RequestMapping(path = "/api/v1/city") @@ -23,20 +24,45 @@ public CityController(CityService cityService) { this.cityService = cityService; } - @GetMapping("/{cityId}") - public City getByCityId(@PathVariable(value = "cityId") Long cityId) { + @GetMapping("/id/{cityId}") + public City getCityById(@PathVariable(value = "cityId") UUID cityId) { LOGGER.info("CityController | getByCityId is started"); LOGGER.info("CityController | getByCityId | cityId : " + cityId); return cityService.getById(cityId); } + /* + [ + { + "id": 0, + "name": "A", + "routes": [ + { + "from": "A", + "destiny": "B", + "departureTime": "10:00", + "arrivalTime": "11:00" + } + ] + }, + ... + ] + */ @GetMapping("/cities") public ResponseEntity> getAllCities(){ LOGGER.info("CityController | getAllCities is started"); List cityList = cityService.listAll(); + LOGGER.info("CityController | getAllCities | cityList : " + cityList.toString()); return new ResponseEntity<>(cityList, HttpStatus.OK); } + @GetMapping("/name/{cityName}") + public City getCityByName(@PathVariable(value = "cityName") String cityName) { + LOGGER.info("CityController | getByCityName is started"); + LOGGER.info("CityController | getByCityName | cityName : " + cityName); + return cityService.getByCityName(cityName); + } + @PostMapping(consumes = "application/json") public ResponseEntity createCity(@RequestBody CityDTO cityDTO) { LOGGER.info("CityController | createCity is started"); @@ -46,20 +72,23 @@ public ResponseEntity createCity(@RequestBody CityDTO cityDTO) { return new ResponseEntity<>(savedCity, HttpStatus.CREATED); } - @PutMapping(consumes = "application/json") - public ResponseEntity updateCity(@RequestBody CityDTO cityDTO) { + @PutMapping("/{cityId}") + public ResponseEntity updateCity(@PathVariable(value = "cityId") UUID cityId, @RequestBody CityDTO cityDTO) { LOGGER.info("CityController | updateCity is started"); + LOGGER.info("CityController | updateCity | cityId : " + cityId); LOGGER.info("CityController | updateCity | update city name : " + cityDTO.getName()); - City city = cityService.getByCityName(cityDTO.getName()); - City updatedCity = cityService.updateCity(city.getId(),city); + City city = new City(cityDTO.getName()); + City updatedCity = cityService.updateCity(cityId,city); + LOGGER.info("CityController | updateCity | updatedCity : " + updatedCity.toString()); return new ResponseEntity<>(updatedCity, HttpStatus.OK); } @DeleteMapping("/{cityId}") - public ResponseEntity deleteCity(@PathVariable(value = "cityId") Long cityId) { + public ResponseEntity deleteCity(@PathVariable(value = "cityId") UUID cityId) { LOGGER.info("CityController | deleteCity is started"); LOGGER.info("CityController | deleteCity | cityId : " + cityId); cityService.deleteCity(cityId); return new ResponseEntity<>("City is deleted.", HttpStatus.OK); + } } diff --git a/src/main/java/com/springshortpath/app/controller/RouteController.java b/src/main/java/com/springshortpath/app/controller/RouteController.java new file mode 100644 index 0000000..fa10b3a --- /dev/null +++ b/src/main/java/com/springshortpath/app/controller/RouteController.java @@ -0,0 +1,83 @@ +package com.springshortpath.app.controller; + +import com.springshortpath.app.dto.RouteDTO; +import com.springshortpath.app.model.Route; +import com.springshortpath.app.service.RouteService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping(path = "/api/v1/route") +public class RouteController { + + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); + + private final RouteService routeService; + + public RouteController(RouteService routeService) { + this.routeService = routeService; + } + + @GetMapping("/{routeId}") + public ResponseEntity getByRouteId(@PathVariable(value = "routeId") UUID routeId) { + LOGGER.info("RouteController | getByRouteId is started"); + LOGGER.info("RouteController | getByRouteId | routeId : " + routeId); + return new ResponseEntity<>(routeService.getById(routeId), HttpStatus.OK); + } + + @GetMapping("/{cityId}/routes") + public ResponseEntity> getAllRoutes(@PathVariable(value = "cityId") UUID cityId){ + LOGGER.info("RouteController | getAllRoutes is started"); + LOGGER.info("RouteController | getAllRoutes | cityId : " + cityId); + List routeList = routeService.listAllByCityId(cityId); + return new ResponseEntity<>(routeList, HttpStatus.OK); + } + + @PostMapping(value = "/{cityId}/create-route", consumes = "application/json") + public ResponseEntity createRoute(@PathVariable(value = "cityId") UUID cityId, @RequestBody RouteDTO routeDTO) { + + LOGGER.info("RouteController | createRoute is started"); + LOGGER.info("RouteController | createRoute | cityId : " + cityId); + + RouteDTO dto = new RouteDTO(routeDTO.getFrom(),routeDTO.getDestination(), + routeDTO.getDepartureTime(),routeDTO.getArriveTime()); + + + Route savedRoute = routeService.save(cityId, dto); + return new ResponseEntity<>(savedRoute, HttpStatus.CREATED); + } + + @PutMapping(value = "/{cityId}/update-route/{routeId}",consumes = "application/json") + public ResponseEntity updateRoute(@PathVariable(value = "cityId") UUID cityId, + @PathVariable(value = "routeId") UUID routeId, + @RequestBody RouteDTO routeDTO) { + + LOGGER.info("RouteController | updateRoute is started"); + LOGGER.info("RouteController | updateRoute | cityId : " + cityId); + LOGGER.info("RouteController | updateRoute | routeId : " + routeId); + + RouteDTO dto = new RouteDTO(routeDTO.getFrom(),routeDTO.getDestination(), + routeDTO.getDepartureTime(),routeDTO.getArriveTime()); + + Route updatedRoute = routeService.update(cityId, routeId, dto); + return new ResponseEntity<>(updatedRoute, HttpStatus.OK); + } + + @DeleteMapping("{cityId}/delete-route/{routeId}") + public ResponseEntity deleteRoute(@PathVariable(value = "cityId") UUID cityId, + @PathVariable(value = "routeId") UUID routeId) { + + LOGGER.info("RouteController | deleteRoute is started"); + LOGGER.info("RouteController | deleteRoute | cityId : " + cityId); + LOGGER.info("RouteController | deleteRoute | routeId : " + routeId); + + routeService.delete(cityId, routeId); + return new ResponseEntity<>("Route is deleted.", HttpStatus.OK); + } +} diff --git a/src/main/java/com/springshortpath/app/controller/ShortestPathController.java b/src/main/java/com/springshortpath/app/controller/ShortestPathController.java new file mode 100644 index 0000000..c16715c --- /dev/null +++ b/src/main/java/com/springshortpath/app/controller/ShortestPathController.java @@ -0,0 +1,59 @@ +package com.springshortpath.app.controller; + +import com.springshortpath.app.payload.request.PathRequest; +import com.springshortpath.app.payload.response.PathShortestConnectionResponse; +import com.springshortpath.app.payload.response.PathShortestTimeResponse; +import com.springshortpath.app.service.ShortestPathService; +import org.springframework.core.io.PathResource; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping(path = "/api/v1/shortestpath") +public class ShortestPathController { + + private final ShortestPathService shortestPathService; + + public ShortestPathController(ShortestPathService shortestPathService) { + this.shortestPathService = shortestPathService; + } + + /* + { + "arrivalCity": "A", + "departureCity": "B", + "totalConnections": 1 + } + */ + @GetMapping("/shortest-path") + public Mono getShortestPath(@RequestBody PathRequest pathRequest) { + + return shortestPathService.getShortestPath(pathRequest.getFrom(), pathRequest.getDestination()) + .map(PathShortestConnectionResponse::new) + .switchIfEmpty(Mono.error(new IllegalArgumentException("Error"))); + } + + /* + { + "arrivalCity": "A", + "departureCity": "B", + "totalHours": 5 + } + */ + @GetMapping("/shortest-path-in-time") + public Mono getShortestPathInTime(@RequestBody PathRequest pathRequest) { + + return shortestPathService.getShortestPathInTime(pathRequest.getFrom(), pathRequest.getDestination()) + .map(PathShortestTimeResponse::new) + .switchIfEmpty(Mono.error(new IllegalArgumentException("Error"))); + } + + @ResponseStatus( + value = HttpStatus.NOT_FOUND, + reason = "Illegal arguments") + @ExceptionHandler(IllegalArgumentException.class) + public void illegalArgumentHandler() { + + } +} diff --git a/src/main/java/com/springshortpath/app/dto/RouteDTO.java b/src/main/java/com/springshortpath/app/dto/RouteDTO.java index 35db1c9..613438e 100644 --- a/src/main/java/com/springshortpath/app/dto/RouteDTO.java +++ b/src/main/java/com/springshortpath/app/dto/RouteDTO.java @@ -3,15 +3,61 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.time.LocalDateTime; @Data @AllArgsConstructor @NoArgsConstructor public class RouteDTO { + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); + + private String from; private String destination; private String departureTime; private String arriveTime; - private Long duration; + private Double duration; + + public RouteDTO(String from,String destination,String departureTime, String arriveTime) { + this.from = from; + this.destination = destination; + this.departureTime = departureTime; + this.arriveTime = arriveTime; + this.duration = calculateDuration(); + } + + private double calculateDuration() { + + LOGGER.info("RouteDTO | calculateDuration is started"); + + final LocalDateTime departureDate = + LocalDateTime.of(0, 1, 1, getHours(departureTime), getMinutes(departureTime)); + final LocalDateTime arrivalDate = + LocalDateTime.of(0, 1, 1, getHours(arriveTime), getMinutes(arriveTime)); + + LOGGER.info("RouteDTO | calculateDuration | departureDate : " + departureDate); + LOGGER.info("RouteDTO | calculateDuration | arrivalDate : " + arrivalDate); + + Duration duration = Duration.between(departureDate, arrivalDate); + + double result = duration.toHours() + duration.toMinutesPart()/60f; + + LOGGER.info("RouteDTO | calculateDuration | duration result : " + result); + + return result; + } + + private int getHours(final String time) { + LOGGER.info("RouteDTO | getHours | hour : " + time.split(":")[0]); + return Integer.parseInt(time.split(":")[0]); + } + private int getMinutes(final String time) { + LOGGER.info("RouteDTO | getMinutes | minute : " + time.split(":")[1]); + return Integer.parseInt(time.split(":")[1]); + } } diff --git a/src/main/java/com/springshortpath/app/model/City.java b/src/main/java/com/springshortpath/app/model/City.java index cf1aa05..699d50e 100644 --- a/src/main/java/com/springshortpath/app/model/City.java +++ b/src/main/java/com/springshortpath/app/model/City.java @@ -4,10 +4,13 @@ import org.springframework.data.annotation.Id; import org.springframework.data.neo4j.core.schema.GeneratedValue; import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Property; import org.springframework.data.neo4j.core.schema.Relationship; +import org.springframework.data.neo4j.core.support.UUIDStringGenerator; import java.util.HashSet; import java.util.Set; +import java.util.UUID; @Node @Data @@ -18,9 +21,11 @@ public class City { @Id - @GeneratedValue - private Long id; + @GeneratedValue(GeneratedValue.UUIDGenerator.class) + @Property + private UUID id; + @Property private String name; @Relationship(type = "ROUTES", direction = Relationship.Direction.OUTGOING) @@ -29,4 +34,14 @@ public class City { public City(String name) { this.name = name; } + + public City withId(UUID id) { + if (this.id.equals(id)) { + return this; + } else { + City newObject = new City(this.name); + newObject.id = id; + return newObject; + } + } } diff --git a/src/main/java/com/springshortpath/app/model/Route.java b/src/main/java/com/springshortpath/app/model/Route.java index 30d8a54..a23c809 100644 --- a/src/main/java/com/springshortpath/app/model/Route.java +++ b/src/main/java/com/springshortpath/app/model/Route.java @@ -4,6 +4,11 @@ import org.springframework.data.annotation.Id; import org.springframework.data.neo4j.core.schema.GeneratedValue; import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Property; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.UUID; @Node @Data @@ -14,14 +19,21 @@ public class Route { @Id - @GeneratedValue - private Long id; + @Property + private UUID id; + + @Property + private String from; + @Property private String destination; + @Property private String departureTime; + @Property private String arriveTime; - private Long duration; + @Property + private Double duration; } diff --git a/src/main/java/com/springshortpath/app/payload/request/PathRequest.java b/src/main/java/com/springshortpath/app/payload/request/PathRequest.java new file mode 100644 index 0000000..734ff4c --- /dev/null +++ b/src/main/java/com/springshortpath/app/payload/request/PathRequest.java @@ -0,0 +1,13 @@ +package com.springshortpath.app.payload.request; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PathRequest { + + private String from; + private String destination; + +} diff --git a/src/main/java/com/springshortpath/app/payload/response/PathShortestConnectionResponse.java b/src/main/java/com/springshortpath/app/payload/response/PathShortestConnectionResponse.java new file mode 100644 index 0000000..5f8f913 --- /dev/null +++ b/src/main/java/com/springshortpath/app/payload/response/PathShortestConnectionResponse.java @@ -0,0 +1,19 @@ +package com.springshortpath.app.payload.response; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PathShortestConnectionResponse { + + private String departureCity; + private String arrivalCity; + private Integer totalConnections; + + public PathShortestConnectionResponse(PathShortestConnectionResponse pathShortestConnectionResponse) { + this.departureCity = pathShortestConnectionResponse.getDepartureCity(); + this.arrivalCity = pathShortestConnectionResponse.getArrivalCity(); + this.totalConnections = pathShortestConnectionResponse.getTotalConnections(); + } +} diff --git a/src/main/java/com/springshortpath/app/payload/response/PathShortestTimeResponse.java b/src/main/java/com/springshortpath/app/payload/response/PathShortestTimeResponse.java new file mode 100644 index 0000000..d7f9651 --- /dev/null +++ b/src/main/java/com/springshortpath/app/payload/response/PathShortestTimeResponse.java @@ -0,0 +1,20 @@ +package com.springshortpath.app.payload.response; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PathShortestTimeResponse { + + private String departureCity; + private String arrivalCity; + private Integer totalInTime; + + public PathShortestTimeResponse(PathShortestTimeResponse pathShortestTimeResponse) { + this.departureCity = pathShortestTimeResponse.getDepartureCity(); + this.arrivalCity = pathShortestTimeResponse.getArrivalCity(); + this.totalInTime = pathShortestTimeResponse.getTotalInTime(); + } + +} diff --git a/src/main/java/com/springshortpath/app/repository/CityRepository.java b/src/main/java/com/springshortpath/app/repository/CityRepository.java index 5c476c9..3e43d91 100644 --- a/src/main/java/com/springshortpath/app/repository/CityRepository.java +++ b/src/main/java/com/springshortpath/app/repository/CityRepository.java @@ -5,24 +5,25 @@ import org.springframework.data.neo4j.repository.query.Query; import java.util.List; +import java.util.UUID; -public interface CityRepository extends Neo4jRepository { +public interface CityRepository extends Neo4jRepository { - @Query("MATCH (cities:City) RETURN cities") + @Query("MATCH (city:City) OPTIONAL MATCH (city)-[r:ROUTES]->(route:Route) RETURN city, collect(r), collect(route)") List listAll(); - @Query("MATCH (city:City {id: $cityId}) RETURN city") - City getById(Long cityId); + @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 {name: $cityName}) RETURN city") + @Query("CREATE (city:City {id: randomUUID(), name: $cityName}) RETURN city") City saveCity(String cityName); - @Query("MATCH (city:City {name: $cityName}) SET city.id = $cityId RETURN city") - City updateCity(Long cityId, 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 deleteUser(Long cityId); + void deleteCity(UUID cityId); } diff --git a/src/main/java/com/springshortpath/app/repository/RouteRepository.java b/src/main/java/com/springshortpath/app/repository/RouteRepository.java new file mode 100644 index 0000000..cbab7db --- /dev/null +++ b/src/main/java/com/springshortpath/app/repository/RouteRepository.java @@ -0,0 +1,34 @@ +package com.springshortpath.app.repository; + +import com.springshortpath.app.model.Route; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; + +import java.util.List; +import java.util.UUID; + +public interface RouteRepository extends Neo4jRepository { + + @Query("MATCH (city:City {id: $cityId})-[:ROUTES]->(route:Route) RETURN route") + List listAllByCityId(UUID cityId); + + @Query("MATCH (route:Route {id: $routeId}) RETURN route") + Route getById(UUID routeId); + + @Query("MATCH (city:City {id: $cityId}) " + + "MERGE (city)-[: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); +} diff --git a/src/main/java/com/springshortpath/app/repository/ShortestPathRepository.java b/src/main/java/com/springshortpath/app/repository/ShortestPathRepository.java new file mode 100644 index 0000000..d3b6064 --- /dev/null +++ b/src/main/java/com/springshortpath/app/repository/ShortestPathRepository.java @@ -0,0 +1,24 @@ +package com.springshortpath.app.repository; + +import com.springshortpath.app.model.City; +import org.neo4j.driver.internal.value.PathValue; +import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.query.Param; +import reactor.core.publisher.Flux; + +import java.util.UUID; + +public interface ShortestPathRepository extends ReactiveNeo4jRepository { + + @Query("MATCH p=shortestPath((a:City {name:$from})-[*]->(b:City {name:$to})) RETURN p") + Flux shortestPath(@Param("from") String from, @Param("to") String to); + + @Query("MATCH (a:City {name: $from})\n" + + "MATCH (b:City {name: $to})\n" + + "CALL apoc.algo.dijkstra(a, b, 'ROUTES', 'duration')\n" + + "YIELD path, weight\n" + + "RETURN path\n" + + "ORDER BY weight ASC LIMIT 1") + Flux shortestPathInTime(@Param("from") String from, @Param("to") String to); +} diff --git a/src/main/java/com/springshortpath/app/service/CityService.java b/src/main/java/com/springshortpath/app/service/CityService.java index e1aaa69..1f23207 100644 --- a/src/main/java/com/springshortpath/app/service/CityService.java +++ b/src/main/java/com/springshortpath/app/service/CityService.java @@ -3,18 +3,19 @@ import com.springshortpath.app.model.City; import java.util.List; +import java.util.UUID; public interface CityService { List listAll(); - City getById(Long cityId); + City getById(UUID cityId); City getByCityName(String cityName); City saveCity(City city); - City updateCity(Long cityId, City city); + City updateCity(UUID cityId, City city); - void deleteCity(Long cityId); + void deleteCity(UUID cityId); } diff --git a/src/main/java/com/springshortpath/app/service/RouteService.java b/src/main/java/com/springshortpath/app/service/RouteService.java new file mode 100644 index 0000000..6e31461 --- /dev/null +++ b/src/main/java/com/springshortpath/app/service/RouteService.java @@ -0,0 +1,20 @@ +package com.springshortpath.app.service; + +import com.springshortpath.app.dto.RouteDTO; +import com.springshortpath.app.model.Route; + +import java.util.List; +import java.util.UUID; + +public interface RouteService { + + List listAllByCityId(UUID cityId); + + Route getById(UUID routeId); + + Route save(UUID cityId, RouteDTO routeDTO); + + Route update(UUID cityId, UUID routeId, RouteDTO routeDTO); + + void delete(UUID cityId, UUID routeId); +} diff --git a/src/main/java/com/springshortpath/app/service/ShortestPathService.java b/src/main/java/com/springshortpath/app/service/ShortestPathService.java new file mode 100644 index 0000000..26f35f0 --- /dev/null +++ b/src/main/java/com/springshortpath/app/service/ShortestPathService.java @@ -0,0 +1,13 @@ +package com.springshortpath.app.service; + +import com.springshortpath.app.payload.response.PathShortestConnectionResponse; +import com.springshortpath.app.payload.response.PathShortestTimeResponse; +import reactor.core.publisher.Mono; + +public interface ShortestPathService { + + public Mono getShortestPath(String from, String to); + + public Mono getShortestPathInTime(String from, String to); + +} diff --git a/src/main/java/com/springshortpath/app/service/impl/CityServiceImpl.java b/src/main/java/com/springshortpath/app/service/impl/CityServiceImpl.java index 3ecb9b3..1d37346 100644 --- a/src/main/java/com/springshortpath/app/service/impl/CityServiceImpl.java +++ b/src/main/java/com/springshortpath/app/service/impl/CityServiceImpl.java @@ -5,10 +5,13 @@ import com.springshortpath.app.service.CityService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.UUID; @Service +@Transactional @RequiredArgsConstructor public class CityServiceImpl implements CityService { @@ -20,7 +23,7 @@ public List listAll() { } @Override - public City getById(Long cityId) { + public City getById(UUID cityId) { return cityRepository.getById(cityId); } @@ -31,16 +34,16 @@ public City getByCityName(String cityName) { @Override public City saveCity(City city) { - return cityRepository.saveCity(city.getName()); + return cityRepository.save(city); } @Override - public City updateCity(Long cityId, City city) { + public City updateCity(UUID cityId, City city) { return cityRepository.updateCity(cityId,city.getName()); } @Override - public void deleteCity(Long cityId) { - cityRepository.deleteUser(cityId); + public void deleteCity(UUID cityId) { + cityRepository.deleteCity(cityId); } } diff --git a/src/main/java/com/springshortpath/app/service/impl/RouteServiceImpl.java b/src/main/java/com/springshortpath/app/service/impl/RouteServiceImpl.java new file mode 100644 index 0000000..e78334e --- /dev/null +++ b/src/main/java/com/springshortpath/app/service/impl/RouteServiceImpl.java @@ -0,0 +1,62 @@ +package com.springshortpath.app.service.impl; + +import com.springshortpath.app.dto.RouteDTO; +import com.springshortpath.app.model.Route; +import com.springshortpath.app.repository.RouteRepository; +import com.springshortpath.app.service.RouteService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@Service +@Transactional +@RequiredArgsConstructor +public class RouteServiceImpl implements RouteService { + + private final RouteRepository routeRepository; + + @Override + public List listAllByCityId(UUID cityId) { + return routeRepository.listAllByCityId(cityId); + } + + @Override + public Route getById(UUID routeId) { + return routeRepository.getById(routeId); + } + + @Override + public Route save(UUID cityId, RouteDTO routeDTO) { + + String from = routeDTO.getFrom(); + String destination = routeDTO.getDestination(); + String departureTime = routeDTO.getDepartureTime(); + String arriveTime = routeDTO.getArriveTime(); + double duration = routeDTO.getDuration(); + + return routeRepository.saveRoute(cityId,from,destination,departureTime, + arriveTime,duration); + } + + @Override + public Route update(UUID cityId, UUID routeId, RouteDTO routeDTO) { + + String from = routeDTO.getFrom(); + String destination = routeDTO.getDestination(); + String departureTime = routeDTO.getDepartureTime(); + String arriveTime = routeDTO.getArriveTime(); + double duration = routeDTO.getDuration(); + + + return routeRepository.updateRoute(cityId,routeId,from, destination,departureTime, + arriveTime,duration); + } + + @Override + public void delete(UUID cityId, UUID routeId) { + routeRepository.deleteRoute(cityId,routeId); + } +} diff --git a/src/main/java/com/springshortpath/app/service/impl/ShortestPathServiceImpl.java b/src/main/java/com/springshortpath/app/service/impl/ShortestPathServiceImpl.java new file mode 100644 index 0000000..f5ab0a1 --- /dev/null +++ b/src/main/java/com/springshortpath/app/service/impl/ShortestPathServiceImpl.java @@ -0,0 +1,68 @@ +package com.springshortpath.app.service.impl; + + +import com.springshortpath.app.payload.response.PathShortestConnectionResponse; +import com.springshortpath.app.payload.response.PathShortestTimeResponse; +import com.springshortpath.app.repository.ShortestPathRepository; +import com.springshortpath.app.service.ShortestPathService; +import lombok.RequiredArgsConstructor; +import org.neo4j.driver.internal.value.PathValue; +import org.neo4j.driver.types.Relationship; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +@Service +@Transactional +@RequiredArgsConstructor +public class ShortestPathServiceImpl implements ShortestPathService { + + private final ShortestPathRepository shortestPathRepository; + + @Override + public Mono getShortestPath(String from, String to) { + + final Flux rows = shortestPathRepository.shortestPath(from, to); + return rows + .map(it -> this.convert(it.asPath())) + .take(1) + .next() + .switchIfEmpty(Mono.empty()); + + } + + @Override + public Mono getShortestPathInTime(String from, String to) { + + final Flux rows = shortestPathRepository.shortestPathInTime(from, to); + return rows + .map(it -> this.convertTimePath(it.asPath())) + .take(1) + .next() + .switchIfEmpty(Mono.empty()); + } + + + private PathShortestConnectionResponse convert(org.neo4j.driver.types.Path connection) { + + String departureCity = connection.start().get("name").asString(); + String arriveCity = connection.end().get("name").asString(); + int length = connection.length(); + + return new PathShortestConnectionResponse(departureCity, arriveCity, length); + } + + private PathShortestTimeResponse convertTimePath(org.neo4j.driver.types.Path connection) { + + String departureCity = connection.start().get("name").asString(); + String arriveCity = connection.end().get("name").asString(); + Stream targetStream = StreamSupport.stream(connection.relationships().spliterator(), false); + int totalInTime = targetStream.mapToInt(it -> it.get("duration").asInt()).sum(); + + return new PathShortestTimeResponse(departureCity, arriveCity, totalInTime); + } +}