Skip to content
Snippets Groups Projects
Commit e9cc8414 authored by CHRIS-HP\chrii's avatar CHRIS-HP\chrii
Browse files

Implementing filtering logic

parent 87055150
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
This diff is collapsed.
package edu.ntnu.group8.stayfinder.controller;
import edu.ntnu.group8.stayfinder.dto.HotelFilterRequest;
import edu.ntnu.group8.stayfinder.entity.Amenity;
import edu.ntnu.group8.stayfinder.entity.Hotel;
import edu.ntnu.group8.stayfinder.service.HotelService;
......@@ -90,6 +91,13 @@ public class HotelController {
}
}
@PostMapping("/filter")
public ResponseEntity<List<Hotel>> filterHotels(@RequestBody HotelFilterRequest request) {
List<Hotel> filteredHotels = hotelService.getFilteredHotels(request);
return ResponseEntity.ok(filteredHotels);
}
/**
* Search hotels by destination, check-in and check-out date, number of rooms, and guests.
*
......
package edu.ntnu.group8.stayfinder.dto;
import java.time.LocalDate;
import java.util.List;
public class HotelFilterRequest {
private String destination;
private LocalDate fromDate;
private LocalDate toDate;
private Integer numberOfRooms;
private Integer guests;
private List<String> requiredAmenities;
private Double minAveragePrice;
private Double maxAveragePrice;
private String sortBy; // "price", "stars", "name"
private String sortDirection; // "asc" or "desc"
private List<Integer> hotelIds; // optional: if filtering from already reduced set (e.g. from /search)
public HotelFilterRequest() {}
public HotelFilterRequest(String destination, LocalDate fromDate, LocalDate toDate,
Integer numberOfRooms, Integer guests, List<String> requiredAmenities,
Double minAveragePrice, Double maxAveragePrice, String sortBy,
String sortDirection, List<Integer> hotelIds) {
this.destination = destination;
this.fromDate = fromDate;
this.toDate = toDate;
this.numberOfRooms = numberOfRooms;
this.guests = guests;
this.requiredAmenities = requiredAmenities;
this.minAveragePrice = minAveragePrice;
this.maxAveragePrice = maxAveragePrice;
this.sortBy = sortBy;
this.sortDirection = sortDirection;
this.hotelIds = hotelIds;
}
// getters and setters
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public LocalDate getFromDate() {
return fromDate;
}
public void setFromDate(LocalDate fromDate) {
this.fromDate = fromDate;
}
public LocalDate getToDate() {
return toDate;
}
public void setToDate(LocalDate toDate) {
this.toDate = toDate;
}
public Integer getNumberOfRooms() {
return numberOfRooms;
}
public void setNumberOfRooms(Integer numberOfRooms) {
this.numberOfRooms = numberOfRooms;
}
public Integer getGuests() {
return guests;
}
public void setGuests(Integer guests) {
this.guests = guests;
}
public List<String> getRequiredAmenities() {
return requiredAmenities;
}
public void setRequiredAmenities(List<String> requiredAmenities) {
this.requiredAmenities = requiredAmenities;
}
public Double getMinAveragePrice() {
return minAveragePrice;
}
public void setMinAveragePrice(Double minAveragePrice) {
this.minAveragePrice = minAveragePrice;
}
public Double getMaxAveragePrice() {
return maxAveragePrice;
}
public void setMaxAveragePrice(Double maxAveragePrice) {
this.maxAveragePrice = maxAveragePrice;
}
public String getSortBy() {
return sortBy;
}
public void setSortBy(String sortBy) {
this.sortBy = sortBy;
}
public String getSortDirection() {
return sortDirection;
}
public void setSortDirection(String sortDirection) {
this.sortDirection = sortDirection;
}
public List<Integer> getHotelIds() {
return hotelIds;
}
public void setHotelIds(List<Integer> hotelIds) {
this.hotelIds = hotelIds;
}
}
......@@ -322,7 +322,7 @@ public class Hotel {
*
* @return collection of amenities
*/
public Collection<? extends Amenity> getAmenities() {
public Set<Amenity> getAmenities() {
return amenities;
}
......
......@@ -39,6 +39,56 @@ public interface HotelRepository extends JpaRepository<Hotel, Integer> {
@Param("toDate") LocalDate toDate
);
@Query(value = """
SELECT h.*
FROM hotel h
JOIN room r ON r.hotel_id = h.id
JOIN room_website_pricing rwp ON rwp.room_id = r.id
LEFT JOIN hotel_amenities ha ON ha.hotel_id = h.id
LEFT JOIN amenity a ON ha.amenity_id = a.id
WHERE h.visible = true
/* destination search */
AND (:destination IS NULL OR LOWER(h.name) LIKE LOWER(CONCAT('%', :destination, '%'))
OR LOWER(h.city) LIKE LOWER(CONCAT('%', :destination, '%'))
OR LOWER(h.country) LIKE LOWER(CONCAT('%', :destination, '%')))
/* booked rooms exclusion */
AND (:fromDate IS NULL OR :toDate IS NULL OR r.id NOT IN (
SELECT br.room_id FROM booked_room br
WHERE :fromDate < br.booked_to AND :toDate > br.booked_from
))
/* price filter */
GROUP BY h.id
HAVING (:minAvgPrice IS NULL OR AVG(rwp.price) >= :minAvgPrice)
AND (:maxAvgPrice IS NULL OR AVG(rwp.price) <= :maxAvgPrice)
/* amenities filter */
AND (:requiredAmenities IS NULL OR EXISTS (
SELECT 1
FROM hotel_amenities ha2
JOIN amenity a2 ON ha2.amenity_id = a2.id
WHERE ha2.hotel_id = h.id
AND a2.name IN (:requiredAmenities)
))
ORDER BY
CASE WHEN :sortBy = 'price' AND :sortDirection = 'asc' THEN AVG(rwp.price) END ASC,
CASE WHEN :sortBy = 'price' AND :sortDirection = 'desc' THEN AVG(rwp.price) END DESC,
CASE WHEN :sortBy = 'stars' AND :sortDirection = 'asc' THEN h.stars END ASC,
CASE WHEN :sortBy = 'stars' AND :sortDirection = 'desc' THEN h.stars END DESC,
CASE WHEN :sortBy = 'name' AND :sortDirection = 'asc' THEN h.name END ASC,
CASE WHEN :sortBy = 'name' AND :sortDirection = 'desc' THEN h.name END DESC
""", nativeQuery = true)
List<Hotel> findFilteredHotels(
@Param("destination") String destination,
@Param("fromDate") LocalDate fromDate,
@Param("toDate") LocalDate toDate,
@Param("minAvgPrice") Double minAvgPrice,
@Param("maxAvgPrice") Double maxAvgPrice,
@Param("requiredAmenities") List<String> requiredAmenities,
@Param("sortBy") String sortBy,
@Param("sortDirection") String sortDirection
);
/**
* Find hotels by name (case-insensitive, partial match), excluding hidden hotels.
......
package edu.ntnu.group8.stayfinder.service;
import edu.ntnu.group8.stayfinder.dto.HotelFilterRequest;
import edu.ntnu.group8.stayfinder.entity.Amenity;
import edu.ntnu.group8.stayfinder.entity.Hotel;
import edu.ntnu.group8.stayfinder.entity.Room;
import edu.ntnu.group8.stayfinder.repository.HotelRepository;
import edu.ntnu.group8.stayfinder.repository.RoomRepository;
import jakarta.annotation.Nullable;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
......@@ -26,14 +31,16 @@ public class HotelService {
private final HotelRepository hotelRepository;
private final RoomRepository roomRepository;
private final BookingService bookingService;
private final RoomWebsitePricingService roomWebsitePricingService;
private static final Logger logger = LoggerFactory.getLogger(HotelService.class);
@Autowired
public HotelService(HotelRepository hotelRepository, RoomRepository roomRepository,
BookingService bookingService) {
BookingService bookingService, RoomWebsitePricingService roomWebsitePricingService) {
this.hotelRepository = hotelRepository;
this.roomRepository = roomRepository;
this.bookingService = bookingService;
this.roomWebsitePricingService = roomWebsitePricingService;
}
/**
......@@ -111,6 +118,22 @@ public class HotelService {
return matchingHotels;
}
public List<Hotel> getFilteredHotels(HotelFilterRequest request) {
// convert request fields to query parameters
return hotelRepository.findFilteredHotels(
request.getDestination(),
request.getFromDate(),
request.getToDate(),
request.getMinAveragePrice(),
request.getMaxAveragePrice(),
request.getRequiredAmenities(),
request.getSortBy(),
request.getSortDirection()
);
}
/**
* Validates that the dates are not in the past and that the end date is after the start date.
*
......
......@@ -6,6 +6,8 @@ import edu.ntnu.group8.stayfinder.repository.RoomWebsitePricingRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
......@@ -17,6 +19,7 @@ import java.util.List;
@Service
public class RoomWebsitePricingService {
private final RoomWebsitePricingRepository roomWebsitePricingRepository;
private static final Logger logger = LoggerFactory.getLogger(RoomWebsitePricingService.class);
/**
* Constructor for RoomWebsitePricingService.
......@@ -63,6 +66,7 @@ public class RoomWebsitePricingService {
Map<String, Object> result = new HashMap<>();
result.put("averagePrice", averagePrice != null ? averagePrice : 0.0);
result.put("currencyCode", currencyCode);
logger.debug("Average price for hotel {}: {} {}", hotelId, averagePrice, currencyCode);
return result;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment