使用 Query DSL 按给定公式 (Haversine) 排序
Sort by given formula (Haversine) with Query DSL
我的 Location
实体中有 latitude
和 longitude
。现在我正在使用类似的东西通过给定的坐标获得最近的位置。
String haversine = "(3956 * 2 * ASIN(SQRT(" +
"POWER(SIN((l.latitude - ?1) * pi()/180 / 2), 2) +" +
"COS(l.latitude * pi()/180) * COS(?1 * pi()/180) *" +
"POWER(SIN((l.longitude - ?2) * pi()/180 / 2), 2) )))";
@Query(value = "SELECT l FROM Location l ORDER BY " + haversine)
List<Location> findNearest(String latitude, String longitude);
但现在我想使用查询 DSL 自定义此查询。我正在尝试找出如何使用 OrderSpecifier<?>
按给定公式和实体值对记录进行排序
也许我应该在我的 Location
实体中创建方法,其中 returns 我的位置和给定坐标之间的距离。但是我认为在 MODEL 层中创建方法不是解决这个问题的最佳方法。
所以我的主要问题是:如何在查询 DSL 中按给定公式(例如 Haversine)对记录进行排序
据我所知,haversine
只是一个公式(即一个函数),带有两个参数纬度和经度。实际上,在模型内部创建方法并不是最好的方法。您应该使用 LocationService
class 和 @Service
(如果您使用的是 Spring 或 Springboot),其中包含所有程序逻辑。在那里你可以有一个计算距离的函数,然后将它传递给使用 QueryDSL
进行排序的函数。
像这样:
public class Location {
private int latitude;
private int longitude;
}
@Service
public class LocationServices {
public String haversineCalculation(int locationLat, int locationLong, int latitude, int longitude) {
//Do your calculations here in java
}
public List<Location> findNearest(int latitude, int longitude) {
QLocation location = QLocation.location;
SQLTemplates dialect = new YourSQLDialectTemplate();
SQLQuery query = new SQLQueryImpl(connection, dialect);
List<Location> locations = query
.from(location);
locations.sort((loc1, loc2) -> haversineCalculation(loc1.latitude, loc1.longitude, latitude, longitude).compareTo(haversineCalculation(loc1.latitude, loc1.longitude, latitude, longitude)));
}
}
不幸的是,我不认为 OrderSpecifier
可以执行您希望的顺序,因为涉及到整个函数,并且顺序说明符似乎只能将实体的属性作为顺序描述符.
我刚刚使用了 Apache Lucene from Hibernate Search,代码如下所示。
FullTextEntityManager fullTextSession = Search.getFullTextEntityManager(em);
QueryBuilder builder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Place.class).get();
Double centerLatitude = 51d;
Double centerLongitude = 17d;
org.apache.lucene.search.Query luceneQuery = builder
.spatial()
.within(1000, Unit.KM)
.ofLatitude(centerLatitude)
.andLongitude(centerLongitude)
.createQuery();
javax.persistence.Query jpaQuery =
fullTextSession.createFullTextQuery(luceneQuery, Place.class);
em.close();
return (List<Place>) jpaQuery.getResultList();
我的 Location
实体中有 latitude
和 longitude
。现在我正在使用类似的东西通过给定的坐标获得最近的位置。
String haversine = "(3956 * 2 * ASIN(SQRT(" +
"POWER(SIN((l.latitude - ?1) * pi()/180 / 2), 2) +" +
"COS(l.latitude * pi()/180) * COS(?1 * pi()/180) *" +
"POWER(SIN((l.longitude - ?2) * pi()/180 / 2), 2) )))";
@Query(value = "SELECT l FROM Location l ORDER BY " + haversine)
List<Location> findNearest(String latitude, String longitude);
但现在我想使用查询 DSL 自定义此查询。我正在尝试找出如何使用 OrderSpecifier<?>
也许我应该在我的 Location
实体中创建方法,其中 returns 我的位置和给定坐标之间的距离。但是我认为在 MODEL 层中创建方法不是解决这个问题的最佳方法。
所以我的主要问题是:如何在查询 DSL 中按给定公式(例如 Haversine)对记录进行排序
据我所知,haversine
只是一个公式(即一个函数),带有两个参数纬度和经度。实际上,在模型内部创建方法并不是最好的方法。您应该使用 LocationService
class 和 @Service
(如果您使用的是 Spring 或 Springboot),其中包含所有程序逻辑。在那里你可以有一个计算距离的函数,然后将它传递给使用 QueryDSL
进行排序的函数。
像这样:
public class Location {
private int latitude;
private int longitude;
}
@Service
public class LocationServices {
public String haversineCalculation(int locationLat, int locationLong, int latitude, int longitude) {
//Do your calculations here in java
}
public List<Location> findNearest(int latitude, int longitude) {
QLocation location = QLocation.location;
SQLTemplates dialect = new YourSQLDialectTemplate();
SQLQuery query = new SQLQueryImpl(connection, dialect);
List<Location> locations = query
.from(location);
locations.sort((loc1, loc2) -> haversineCalculation(loc1.latitude, loc1.longitude, latitude, longitude).compareTo(haversineCalculation(loc1.latitude, loc1.longitude, latitude, longitude)));
}
}
不幸的是,我不认为 OrderSpecifier
可以执行您希望的顺序,因为涉及到整个函数,并且顺序说明符似乎只能将实体的属性作为顺序描述符.
我刚刚使用了 Apache Lucene from Hibernate Search,代码如下所示。
FullTextEntityManager fullTextSession = Search.getFullTextEntityManager(em);
QueryBuilder builder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Place.class).get();
Double centerLatitude = 51d;
Double centerLongitude = 17d;
org.apache.lucene.search.Query luceneQuery = builder
.spatial()
.within(1000, Unit.KM)
.ofLatitude(centerLatitude)
.andLongitude(centerLongitude)
.createQuery();
javax.persistence.Query jpaQuery =
fullTextSession.createFullTextQuery(luceneQuery, Place.class);
em.close();
return (List<Place>) jpaQuery.getResultList();