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

banq發表於2020-05-18

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應用程式中輕鬆使用這些功能。希望你喜歡!

 

相關文章