I'm learning Springboot and MongoDB.
I have this model class:
@Data
@Document(collection = "accommodations")
public class Accommodation {
@Id
private String accommodationId;
@TextIndexed
private String title;
@Indexed
private double pricePerNight;
private int guests;
@Indexed
private double rating;
private AccommodationType type;
private String description;
private boolean listed;
private Address address;
private Landlord landlord;
private List<Image> images;
private List<Review> reviews;
private List<Booking> bookings;
public Accommodation(String title, double pricePerNight, int guests, AccommodationType type, String description, Address address, Landlord landlord) {
this.title = title;
this.pricePerNight = pricePerNight;
this.guests = guests;
this.type = type;
this.description = description;
this.listed = true;
this.address = address;
this.landlord = landlord;
this.rating = 0.0;
this.images = new ArrayList<>();
this.reviews = new ArrayList<>();
this.bookings = new ArrayList<>();
}
public boolean isAvailableBetween(LocalDate checkin, LocalDate checkout) {
return this.bookings
.stream()
.noneMatch(b -> (b.getCheckin().isBefore(checkout) || b.getCheckin().isEqual(checkout)) &&
(b.getCheckout().isAfter(checkin)) || b.getCheckout().isEqual(checkin));
}
}
I'm currently using the method "isAvailableBetween" to filter accommodations that are already booked in a certain range of dates (checkin and checkout).
I would like to achieve the same through a Mongo query, but can't figure out how.
This is what I've tried so far (ignore the other filters in the query):
@Repository
public interface AccommodationRepository extends MongoRepository<Accommodation, String>, QuerydslPredicateExecutor<Accommodation> {
@Query("{" +
"$and: [" +
"{listed:true}," +
"{pricePerNight:{$lte:?0}}," +
"{guests:{$gte:?1}}," +
"{rating:{$gte:?2}}," +
"{'landlord.trusted':?3}," +
"{bookings:{$not:{$elemMatch:{checkin:{$lte:?5},checkout:{$gte:?4}}}}}" +
"]" +
"}")
Page<Accommodation> findAccommodationsByFilter(Pageable pageable,
Double pricePerNight,
Integer guests,
Double rating,
Boolean trusted,
LocalDate checkin,
LocalDate checkout
}
Also tried using QueryDSL & Mongo Criterias with Spring Data MongoDB, but couldn't figure out a way to get the same result as the "isAvailableBetween".
Does anyone know how to do this?
After studying Mongo Criterias further, I found a solution!
public Page<Accommodation> findAccommodationsByFilter(AccommodationFilter filter, Pageable pageable) {
Criteria listedTrue = where("listed").is(true);
Query query = query(listedTrue);
filter.pricePerNight().map(where("pricePerNight")::lte).ifPresent(query::addCriteria);
filter.guests().map(where("guests")::gte).ifPresent(query::addCriteria);
filter.type().map(where("type")::is).ifPresent(query::addCriteria);
filter.rating().map(where("rating")::gte).ifPresent(query::addCriteria);
filter.trustedLandlord().map(where("landlord.trusted")::is).ifPresent(query::addCriteria);
filter.country().map(where("address.country")::is).ifPresent(query::addCriteria);
filter.city().map(where("address.city")::is).ifPresent(query::addCriteria);
if(filter.checkout().isPresent() && filter.checkin().isPresent()){
Criteria c = where("bookings")
.not()
.elemMatch(where("checkin").lte(filter.checkout().get())
.and("checkout").gte(filter.checkin().get()));
query.addCriteria(c);
}
long total = this.mongoOperations.count(query, Accommodation.class);
query.with(pageable);
List<Accommodation> accommodations = this.mongoOperations.find(query, Accommodation.class);
return new PageImpl<>(accommodations, pageable, total);
}
This achieves the same as the @Query with the added advantage that the parameters can be optional.