Java 实体未映射到 ElasticSearch GeoPoint 属性

Posted

技术标签:

【中文标题】Java 实体未映射到 ElasticSearch GeoPoint 属性【英文标题】:Java entity not mapping to ElasticSearch GeoPoint property 【发布时间】:2019-08-27 13:49:06 【问题描述】:

我正在开发一个微服务,用于使用 spring-data-elasticsearch 使用 Java/Spring Boot 管理位置,当尝试使用我的控制器向 ES 输入新数据时,数据输入未正确映射到 ES 中的文档,特别是地理点属性“位置”。

我尝试使用来自 import org.springframework.data.elasticsearch.core.geo.GeoPoint 和来自 org.springframework.data.elasticsearch.core.geo.GeoPoint 的 GeoPoint,在这两种情况下都没有输入保存到 ES 的数据作为geo_point

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;

import io.swagger.annotations.ApiModelProperty;

@Document(indexName = "location", type="location")
public class Location 

    @Id
    @ApiModelProperty(hidden = true)
    private String id;

    private String appId;

    private String resourceId;

    @GeoPointField
    private GeoPoint location;

    private String street;

    private String city;

    // more attr and method was ommited


使用 ElasticSearchRepository 将数据保存到 ES 后,当我获取映射数据时显示如下:


  "location" : 
    "mappings" : 
      "location" : 
        "properties" : 
          "appId" : 
            "type" : "text",
            "fields" : 
              "keyword" : 
                "type" : "keyword",
                "ignore_above" : 256
              
            
          ,
          "city" : 
            "type" : "text",
            "fields" : 
              "keyword" : 
                "type" : "keyword",
                "ignore_above" : 256
              
            
          ,
          "country" : 
            "properties" : 
              "countryCcFips" : 
                "type" : "text",
                "fields" : 
                  "keyword" : 
                    "type" : "keyword",
                    "ignore_above" : 256
                  
                
              ,
              "countryCcIso" : 
                "type" : "text",
                "fields" : 
                  "keyword" : 
                    "type" : "keyword",
                    "ignore_above" : 256
                  
                
              ,
              "countryName" : 
                "type" : "text",
                "fields" : 
                  "keyword" : 
                    "type" : "keyword",
                    "ignore_above" : 256
                  
                
              
            
          ,
          "location" : 
            "properties" : 
              "lat" : 
                "type" : "float"
              ,
              "lon" : 
                "type" : "float"
              
            
          ,
          "parish" : 
            "type" : "text",
            "fields" : 
              "keyword" : 
                "type" : "keyword",
                "ignore_above" : 256
              
            
// ommited more json data

请,我需要将 GeoPoint 字段(位置)映射到 ES 中的 geo_point,这对于正确执行 Geoqueries 很重要。

我使用 Spring Data ElasticSearch,使用 Spring Boot 2.1.3.RELEASE 和 ElasticSearch driver 6.4.3,使用 ElasticSearch server 6.4

【问题讨论】:

这确实很奇怪,我刚刚检查了代码,通常您只需要拥有GeoPoint 类或GeoPointField 注释即可将字段键入为“geo_point”。代码中的这个位置多年来一直没有改变。我将尝试用本地示例重现这一点,但不能保证我今天会找到时间。 【参考方案1】:

我创建了一个带有存储库、控制器的最小应用程序,并使用 Spring Boot 2.1.3.RELEASE、Spring Data Elasticsearch 3.1.5.RELEASE、elasticsearch 客户端 6.4.3 和 Elasticsearch 服务器 6.4.0 运行它。

我正在使用一个 Person pojo 类,它有两个 geo_point 字段,一个是普通的 spring GeoPoint,另一个是使用 GeoPointFieldannotation 的自定义 MyGeoPoint

启动应用并调用RestController的init方法插入记录后,索引中的映射就可以了:


  "person": 
    "aliases": ,
    "mappings": 
      "person": 
        "properties": 
          "firstName": 
            "type": "text",
            "fields": 
              "keyword": 
                "type": "keyword",
                "ignore_above": 256
              
            
          ,
          "geoPoint": 
            "type": "geo_point"
          ,
          "id": 
            "type": "long"
          ,
          "lastName": 
            "type": "text",
            "fields": 
              "keyword": 
                "type": "keyword",
                "ignore_above": 256
              
            
          ,
          "myGeoPoint": 
            "type": "geo_point"
          
        
      
    ,
    "settings": 
      "index": 
        "refresh_interval": "1s",
        "number_of_shards": "5",
        "provided_name": "person",
        "creation_date": "1554750620775",
        "store": 
          "type": "fs"
        ,
        "number_of_replicas": "1",
        "uuid": "w1L279wOQUmvDPMu4iYXtg",
        "version": 
          "created": "6040099"
        
      
    
  

Person.java

/*
 * (c) Copyright 2019 sothawo
 */
package com.sothawo.springdataelastictest;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */
@Document(indexName = "person", type = "person")
public class Person 
    @Id private Long id;
    private String lastName;
    private String firstName;

    private GeoPoint geoPoint;

    @GeoPointField private MyGeoPoint myGeoPoint;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getLastName() 
        return lastName;
    

    public void setLastName(String lastName) 
        this.lastName = lastName;
    

    public String getFirstName() 
        return firstName;
    

    public void setFirstName(String firstName) 
        this.firstName = firstName;
    

    public GeoPoint getGeoPoint() 
        return geoPoint;
    

    public void setGeoPoint(GeoPoint geoPoint) 
        this.geoPoint = geoPoint;
    

    public MyGeoPoint getMyGeoPoint() 
        return myGeoPoint;
    

    public void setMyGeoPoint(MyGeoPoint myGeoPoint) 
        this.myGeoPoint = myGeoPoint;
    

    @Override
    public String toString() 
        return "Person" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", firstName='" + firstName + '\'' +
                ", geoPoint=" + geoPoint +
                ", myGeoPoint=" + myGeoPoint +
                '';
    

    public static class MyGeoPoint 
        private double lat;
        private double lon;

        public double getLat() 
            return lat;
        

        public void setLat(double lat) 
            this.lat = lat;
        

        public double getLon() 
            return lon;
        

        public void setLon(double lon) 
            this.lon = lon;
        

        @Override
        public String toString() 
            return "MyGeoPoint" + "lat=" + lat + ", lon=" + lon + '';
        
    

PersonRepository.java

package com.sothawo.springdataelastictest;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface PersonRepository extends ElasticsearchRepository<Person, Long> 

ElasticsearchRepositoryController.java

package com.sothawo.springdataelastictest;

import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/repo")
public class ElasticsearchRepositoryController 

    private PersonRepository personRepository;

    public ElasticsearchRepositoryController(PersonRepository personRepository) 
        this.personRepository = personRepository;
    

    @GetMapping("/init")
    public Long initPerson() 
        final Person person = new Person();
        person.setId(42L);
        person.setFirstName("John");
        person.setLastName("Doe");

        GeoPoint geoPoint = new GeoPoint(12.34, 56.78);
        person.setGeoPoint(geoPoint);

        Person.MyGeoPoint myGeoPoint = new Person.MyGeoPoint();
        myGeoPoint.setLat(43.21);
        myGeoPoint.setLon(87.65);
        person.setMyGeoPoint(myGeoPoint);

        return personRepository.save(person).getId();

    

SpringdataElasticTestApplication.java

package com.sothawo.springdataelastictest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@SpringBootApplication
@EnableElasticsearchRepositories
public class SpringdataElasticTestApplication 

    public static void main(String[] args) 
        SpringApplication.run(SpringdataElasticTestApplication.class, args);
    

所以我找不到此代码和我使用的版本的问题。您能否使用这些类重现错误或构建一个重现错误的最小示例?

您确定映射是由 Repository 类而不是其他地方创建的吗?

您能否检查一下映射之后应用程序启动,但之前任何数据插入索引?

【讨论】:

您是如何创建索引的?对我来说,它以一种没有 geo_point 的方式创建!! 如果索引不存在会在启动时自动创建 我发现对于 Geo Point,动态映射不起作用,必须手动完成或通过代码强制执行。另外,我尝试了您提供的指定版本的解决方案,但没有解决问题。【参考方案2】:

在应用程序运行时spring-boot2.2.1.RELEASEspring-data-elasticserach4.0.0.DATAES-690-SNAPSHOT)删除索引后,我遇到了同样的问题。

保存文档后,位置属性的索引错误映射

... other properites...

"location" : 
  "properties" : 
    "lat" : 
      "type" : "float"
    ,
    "lon" : 
      "type" : "float"
    
  

在应用程序运行时无法解决,映射总是错误的。

这个解决方案对我有用:

    删除索引 重新启动应用程序

现在索引是用 JUST 位置字段创建的,所有其他字段映射都丢失了。索引文档后(使用ElasticSearchRepository.save),一切都很好,添加了其他映射。


    "properties": 
        "location": 
            "type": "geo_point"
        
    

【讨论】:

【参考方案3】:

我遇到了类似的问题,但我是这样解决的: 在 ES 端,像这样创建索引:

PUT my_index

  "mappings": 
    "properties": 
      "doc.location": 
        "type": "geo_point"
      
    
  

location 中是我想成为geo_point 类型的字段。原谅我的英语。 当然,ES 的持久化必须遵循以下结构:

"location":  
    "lat": 41.12,
    "lon": -71.34

你说过here。

【讨论】:

解释你的答案。【参考方案4】:

我遇到了类似的问题,但在浏览了一些文档后,我不得不使用以下代码 sn-p 让 Elasticsearch 将我的 GeoPoint 属性创建为 geo_point 类型字段。

@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;

@PostConstruct
public void init() 
    IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(PropertySearch.class);
    indexOperations.putMapping(indexOperations.createMapping());
    indexOperations.refresh();

更多信息可以参考post

【讨论】:

【参考方案5】:
if (elasticsearchRestTemplate.indexOps(MerchantModel.class).exists()) 
    elasticsearchRestTemplate.indexOps(MerchantModel.class).delete();

// fix geo_point mapping
CreateIndexRequest createIndexRequest = new CreateIndexRequest("merchant");
createIndexRequest.mapping("\n" +
        "      \"properties\": \n" +
        "        \"location\": \n" +
        "          \"type\": \"geo_point\"\n" +
        "        \n" +
        "      \n" +
        "    ", XContentType.JSON);
IndicesClient indicesClient = restHighLevelClient.indices();
CreateIndexResponse createIndexResponse = indicesClient.create(createIndexRequest, RequestOptions.DEFAULT);
log.info("create ES merchant mapping:[isAcknowledged=]", createIndexResponse.isAcknowledged());

【讨论】:

请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助,质量更高,更有可能吸引投票。

以上是关于Java 实体未映射到 ElasticSearch GeoPoint 属性的主要内容,如果未能解决你的问题,请参考以下文章

java使用elasticsearch进行模糊查询之must使用

关联结束未映射到ADO实体框架中

我可以对未映射到表的实体使用休眠查询语言吗?

当我为此目的创建构造函数时,如何处理 Doctrine::find 未初始化未映射的实体属性?

RestKit 映射结果已填充,但未设置核心数据实体

elasticsearch迁移数据到新索引中