Запрос приближенной комплексной тригонометрии спецификации Java springboot

#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
                    );
        }
    }