使用查询DSL按给定公式(Haversine)排序

时间:2016-05-28 00:42:23

标签: spring-data querydsl

我的latitude恩赐中有longitudeLocation。现在我正在使用类似的东西来获得最合适的坐标。

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

但是现在我想用Query DSL自定义这个查询。我试图找出如何使用OrderSpecifier<?>

按实体给定公式和值对记录进行排序

也许我应该在我的Location实体中创建方法,它返回地点和给定坐标之间的距离。但我认为在MODEL层中创建方法并不是解决这个问题的最佳方法。

所以我的主要问题是:如何通过查询DSL中的给定公式(例如Haversine)对记录进行排序

2 个答案:

答案 0 :(得分:0)

从我所看到的情况来看,haversine只是一个具有2个参数纬度和经度的公式(即函数)。确实,在模型中创建方法并不是最好的方法。您应该使用LocationService@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可以执行您希望的顺序,因为涉及整个函数,并且订单说明符似乎只能将实体的属性作为排序描述符

答案 1 :(得分:0)

我刚使用Apache Lucene中的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();