2015-04-11 25 views
7

Próbuję użyć Haversine formula, aby znaleźć obiekty w pobliżu lokalizacji w Spring Data JPA Query z Pageable, ale nie robię tego.Zapytanie o wiosnę: formuła Haversine z możliwością analizy

Moje pierwsze podejście wygląda tak

@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); 

ale nie jako Wiosna/JPA wydaje się nie być w stanie używać aliasów w klauzuli WHERE. Kod SQL w stosie jest podobny do tego, aby alias w klauzuli where nie był zamieniany. Używanie "col_1_0_" (bez ") zamiast dist też nie działa

Zgodnie z tym SO Answer, przynajmniej MySQL jest interpretowany na lewą stronę i użycie aliasów w klauzuli Where nie jest możliwe. HAVING zamiast WHERE, ale także w klauzuli HAVING, alias nie jest rozwiązany

Wiem, że mogę przenieść formułę Haversine do klauzuli where, ale nadal potrzebuję tego w klauzuli "Zakonu" i myślę, że może spowolnić wydajność przy użyciu tej samej długiej formuły Haversine w klauzuli Zamawiam, ponieważ wybieram z kilkuset tysięcy elementów. sprzymierzeńcem, ale nie wiem jak zastosować stronicowalnej do tego:

@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 ; 
} 

Więc albo trzeba pierwsze podejście @query do pracy (które Wolę) OR drugie podejście z stronicowalnej

Odpowiedz

9

Z pomocą Neila Stocktona postanowiłem trzymać się cudzego zapytania przy użyciu formuły Haversine w obu klauzulach WHERE i ORDER BY, więc nadal mogę korzystać z funkcji paginacji Spring. Moje ostateczne rozwiązanie wygląda tak:

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

Z 100.000 jednostek, to trwa około około 585 ms, aby znaleźć najbliższe 10 najlepszych jednostek do danej lokalizacji i około 8 sekund na znalezienie 10 najlepszych w ciągu najbliższych podmioty 1.000.000 jednostek , co jest w porządku dla mnie teraz. Jeśli zoptymalizuję zapytanie, opublikuję je tutaj.

+1

Wielkie dzięki! Czy w twoim przykładzie nie powinno być "m.latitude" zmienione na "e.latitude"? – Cir0X

3

JPQL nie zezwala na "aliasy wyników" w klauzulach WHERE, HAVING; mogą być użyte tylko w klauzuli ORDER. Podobnie użycie RADIANS, COS, ACOS, SIN, ASIN itp. Jest nieprzenośnym JPQL; niektóre implementacje mogą je wspierać (wiem, że Wirus DataNucleus działa, i wygląda na to, że masz również Hibernate), ale nie jest to gwarantowane.

Po prostu użyj NativeQuery i wciśnij dowolny SQL, którego potrzebujesz. Tracisz (RDBMS) przenośność, ale nie masz tego z tym, co próbujesz powyżej (dla dostawcy JPA lub RDBMS).

+0

Dzięki Neil! Nie wiedziałem, że RADIANS, COS, ACOS ... już zrywa ze specyfikacją JPQL. Jak zasugerowałeś, ja używam Hibernate jako dostawcy JPA, więc instrukcja działa do tej pory. W tej chwili zdecydowałem się pozostać przy natywnej kwerendzie i użyć formuły Haversine w klauzuli WHERE i ORDER z natywnym zapytaniem, że stracę funkcję stronicowania Spring. Przetestowałem wydajność na 100 000 jednostek, co do tej pory było ok. – zersaegen