#java #sql #spring-boot #jpa #spring-data-jpa
#java #sql #spring-boot #jpa #spring-data-jpa
Вопрос:
Я создаю приложение, в котором пользователь может фильтровать результаты с помощью множества различных (необязательных) фильтров, одним из которых является поиск по близости. Я использую org.springframework.data.jpa.domain.Спецификация для динамического построения запроса, на основе которого предоставляются параметры, большинство из которых являются простыми ge / le некоторыми целочисленными значениями. Мое предложение where: (location_lng / lat — это значения из базы данных, где числа задают координаты в запросе, а < 10 — расстояние)
SELECT *
FROM rental
WHERE(
6371 * acos(
cos( radians(52.2477331) ) * cos( radians( location_lat ) )
*
cos( radians( location_lng ) - radians(21.0136079) )
sin( radians(52.2477331) )
*
sin( radians( location_lat ) )
)
) < 10
Я, мой серверный сервер, запрашиваю репозиторий таким методом:
public Page<RentalDTO> getByCriteria(RentalSearchCriteria cr,
Optional<Integer> page,
Optional<String> sort,
Optional<Integer> size,
Optional<Sort.Direction> order) {
return rentalRepository.findAll(
where(
priceFrom(cr.getPriceFrom())
.and(inProximity(cr.getDist(),cr.getLat(),cr.getLng()))
.and(priceTo(cr.getPriceTo()))
.and(sizeFrom(cr.getSizeFrom()))
.and(sizeTo(cr.getSizeTo()))
.and(buildFrom(cr.getBuildFrom()))
.and(builtTo(cr.getBuildTo()))
.and(roomFrom(cr.getRoomFrom()))
.and(roomTo(cr.getRoomTo()))
.and(moveInAt(cr.getMoveInTo()))
.and(tagsIncluded(cr.getFeatures()))
),
PageRequest.of(
page.orElse(0),
size.orElse(3),
order.orElse(Sort.Direction.ASC),
sort.orElse("price"))
).map(RentalDTO::createFrom);
}
И спецификация (на данный момент она пуста):
public static Specification<Rental> inProximity(Integer distance, Double lat, Double lng) {
if (distance == null || lat == null || lng == null) {
return null;
} else {
return (root, query, cb) -> {
return null; // todo
};
}
}
Запрос использует group by, в то время как другие запросы этого не делают, но, безусловно, должен быть какой-то способ сделать это без него, поскольку мне не нужно проверять всю таблицу, чтобы вычислить расстояние для определенной строки, возможно, некоторые подзапросы или тому подобное, не совсем уверен, как справиться с этим одним фильтром.
Ответ №1:
В итоге я создал функцию PostgreSQL и использовал эту функцию в спецификации:
create or replace function distance(lat double precision, lng double precision, db_lat double precision, db_lng double precision)
returns double precision as $dist$
begin
return
6371 * acos(
cos(radians(lat)) * cos(radians(db_lat))
*
cos(radians(db_lng) - radians(lng))
sin(radians(lat))
*
sin(radians(db_lat))
);
end;
$dist$ language plpgsql;
Springboot:
public static Specification<Rental> inProximity(Double distance, Double lat, Double lng) {
if (distance == null || lat == null || lng == null) {
return null;
} else {
return (root, query, cb) ->
cb.lessThanOrEqualTo(
cb.function("distance", Double.class,
cb.literal(lat), cb.literal(lng), root.get("locationLat"), root.get("locationLng")),
distance
);
}
}