在Spring Data Elasticsearch 4中使用地理距离排序 - sothawo


Spring Data Elasticsearch在4.0版中的发布(请参阅文档)带来了两个新功能,这些新功能现在使用户能够在存储库查询中使用地理距离排序:第一个是新类GeoDistanceOrder; 第二个是存储库方法的新返回类型 SearchHit < T >。在这篇文章中,我将展示使用这些类回答诸如“哪个酒馆离指定位置最近?”之类的问题有多么容易。

源代码
GitHub上提供了用于此帖子的完整可运行代码为了运行该应用程序,您将需要Java 8或更高版本以及一个正在运行的Elasticsearch实例。如果无法通过以下网址访问localhost:9200。您需要在src / main / resources / application.yaml文件中设置正确的值。

样本数据
对于此示例应用程序,我将csv文件与来自OpenStreetMap的POI数据一起使用,该数据包含德国的POI,这些POI归类为食物,例如餐馆,酒吧,快餐等。总共有826843条记录。
启动应用程序时,将创建Elasticsearch中的索引,并将其与数据一起加载(如果尚不存在)。因此,第一次启动需要花费更长的时间,可以在控制台上看到进度。在应用程序中,这些POI由以下实体建模:

@Document(indexName = "foodpois")
public class FoodPOI {
    @Id
    private String id;
    @Field(type = FieldType.Text)
    private String name;
    @Field(type = FieldType.Integer)
    private Integer category;
    private GeoPoint location;
   
// constructors, getter/setter left out for brevity
}

注意location和name属性。
仓储定义:

public interface FoodPOIRepository extends ElasticsearchRepository<FoodPOI, String> {
    List<SearchHit<FoodPOI>> searchTop3By(Sort sort);
    List<SearchHit<FoodPOI>> searchTop3ByName(String name, Sort sort);
}

我们定义了两个函数,第一个函数用于搜索给定点附近的任何POI,第二个函数可以搜索具有名称的POI。我们只需要在接口中定义这些方法,因为Spring Data Elasticsearch将通过分析方法名称和参数来为这些方法创建实现。
在第4版之前的Spring Data Elasticsearch中,我们只能从存储库方法得到一个List<FoodPOI>,但是现在获得但是现在有SearchHit < T >。它不仅包含实体,还包含其他值,例如得分,突出显示或(在此需要的)排序值。在进行地理距离排序时,排序值包含POI到我们传递给搜索的值的实际距离。

我们定义了一个REST控制器,因此我们可以调用我们的应用程序来获取数据。请求参数将出现在POST主体中,该主体将映射到以下类:

public class RequestData {
    private String name;
    private double lat;
    private double lon;
    // constructors, getter/setter ...
}

将发送到客户端的结果数据如下所示:

public class ResultData {
    private String name;
    private GeoPoint location;
    private Double distance;
  // constructor, gette/setter ...
}

控制器有一个方法:

@RestController
@RequestMapping("/foodpois")
public class FoodPOIController {
    private final FoodPOIRepository repository;
    public FoodPOIController(FoodPOIRepository repository) {
        this.repository = repository;
    }
    @PostMapping(
"/nearest3")
    List<ResultData> nearest3(@RequestBody RequestData requestData) {
        GeoPoint location = new GeoPoint(requestData.getLat(), requestData.getLon());
        Sort sort = Sort.by(new GeoDistanceOrder(
"location", location).withUnit("km"));
        List<SearchHit<FoodPOI>> searchHits;
        if (StringUtils.hasText(requestData.getName())) {
            searchHits = repository.searchTop3ByName(requestData.getName(), sort);
        } else {
            searchHits = repository.searchTop3By(sort);
        }
        return searchHits.stream()
            .map(searchHit -> {
                Double distance = (Double) searchHit.getSortValues().get(0);
                FoodPOI foodPOI = searchHit.getContent();
                return new ResultData(foodPOI.getName(), foodPOI.getLocation(), distance);
            }).collect(Collectors.toList());
    }
}

在第15行中,我们创建了一个 Sort对象,指定Elasticsearch应该将按地理距离排序的数据返回给我们从请求数据中获取的给定值。然后,根据我们是否有名称,我们调用相应的方法并返回一个List< SearchHit < FoodPOI >>。
然后,在第27至29行中,我们从返回的对象中提取所需的信息,并构建结果数据对象。

启动应用程序后,我们可以检查结果。
甚至无需知道如何将这些请求发送到Elasticsearch以及Elasticsearch所发回的内容,我们就可以在Spring应用程序中轻松使用这些功能。希望你喜欢!