2015-04-11 2 views
7

Я пытаюсь использовать Haversine formula, чтобы найти объекты рядом с местоположением в Spring Data JPA Query с Pageable, но я не сделаю это.Весенний запрос: Формула Хаверсина со страницей

Мой первый подход выглядит следующим образом

@Query("SELECT m, (6371 * acos(cos(radians(:latitude)) * cos(radians(m.latitude)) * cos(radians(m.longitude) - radians(:longitude)) + sin(radians(:latitude)) * sin(radians(m.latitude)))) as dist FROM Entity m WHERE dist < :distance ORDER BY dist DESC") 
    public List<Entity> findEntitiesByLocation(@Param("latitude") final double latitude, @Param("longitude") final double longitude, @Param("distance") final double distance, Pageable pageable); 

но терпит неудачу как Spring/JPA, кажется, не способен использовать псевдонимы в ИНЕКЕ. SQL в стеке выглядит так:

select (...),(haversine) as col_1_0_ where dist<? order by col_1_0_ DESC 

так что псевдоним в предложении where не заменяется. Использование «col_1_0_» (без «)» вместо «dist» также не работает.

В соответствии с этим SO Answer по крайней мере MySQL интерпретируется наизнанку и использование псевдонимов в предложении Where не представляется возможным. HAVING вместо WHERE, но также и в предложении HAVING, псевдоним не разрешен.

Я знаю, что могу переместить Формулу Хаверсина в предложение where, но мне все еще нужно это в предложении Order By, и я думаю, что это может замедлить производительность, используя ту же самую длинную формулу Хаверсина в предложении Order By, как я выбираю из нескольких сотен тысяч объектов.

Затем я попытался создать Query manu союзник, но я не знаю, как применить выгружаемой к этому:

@Override 
    public List<Entity> findEntitiesByLocation(final double latitude, final double longitude, final double distance, Pageable pageable) { 
    final javax.persistence.Query query = this.entityManager.createQuery(SELECT_ENTITES_BY_DISTANCE); 

    query.setParameter("latitude", latitude); 
    query.setParameter("longitude", longitude); 
    query.setParameter("distance", distance); 

    final List<Entity> entities = query.getResultList(); 
    // order by distance 
    entities .sort(new EntityDistanceComparator(latitude, longitude)); 

    return entities ; 
} 

Итак, я либо нужен первый подход с @Query работать (я бы предпочел) ИЛИ второй подход с выгружаемой

ответ

9

С помощью Нила Стоктона я решил придерживаться не родного запроса, используя формулу Хаверсина как в предложении WHERE, так и в ORDER BY, поэтому я все еще могу использовать функцию разбиения на страницы Spring. Мое окончательное решение выглядит следующим образом:

static final String HAVERSINE_PART = "(6371 * acos(cos(radians(:latitude)) * cos(radians(m.latitude)) * cos(radians(m.longitude) - radians(:longitude)) + sin(radians(:latitude)) * sin(radians(m.latitude))))"; 

@Query("SELECT m FROM Entity m WHERE "+HAVERSINE_PART+" < :distance ORDER BY "+HAVERSINE_PART+" DESC") 
public List<Entity> findEntitiesByLocation(@Param("latitude") final double latitude, @Param("longitude") final double longitude, @Param("distance") final double distance, Pageable pageable); 

С 100.000 лиц, это занимает примерно около 585 мс, чтобы найти лучшие 10 ближайших объектов к данной местности и около 8 секунд, чтобы найти 10 лучших ближайших объектов в пределах 1.000.000 юридических лиц , и это нормально для меня прямо сейчас. Если я оптимизирую запрос, я отправлю его здесь.

+1

Спасибо большое! Не должен ли 'm.latitude' изменяться на' e.latitude' в вашем примере? – Cir0X

3

JPQL не разрешает «псевдонимы результата» в предложениях WHERE, HAVING; они могут использоваться только в предложении ORDER. Аналогичным образом использование RADIANS, COS, ACOS, SIN, ASIN и т. Д. Является не переносным JPQL; некоторые реализации могут поддержать их (я знаю, что DataNucleus JPA делает, и похоже, что у вас есть Hibernate), но не гарантируется.

Просто используйте NativeQuery и нажмите любой SQL-запрос. Вы потеряли (РСУБД) переносимость, но тогда у вас не было того, с чем вы пытались выше (для провайдера JPA или РСУБД).

+0

Спасибо Нейл! Я не знал, что RADIANS, COS, ACOS ... уже нарушает спецификацию JPQL. Как вы предположили, я использую Hibernate в качестве поставщика JPA, поэтому заявление работает до сих пор. Прямо сейчас я решил придерживаться не родного запроса и использовать формулу Хаверсина в причине предложения WHERE и ORDER с помощью собственного запроса, я бы потерял функцию разбивки на весну. Я протестировал производительность на 100 000 Entities, которая была в порядке до сих пор. – zersaegen

Смежные вопросы