使用Spring Boot和Elasticsearch教程

19-01-14 banq
         

Elasticsearch  是一种实时分布式和开源的全文搜索和分析引擎。它是基于文档的搜索平台,具有快速搜索功能。它针对大海捞针式的搜索进行了优化,重点不在于一致性或原子性。

在本博客中,我将介绍如何下载Elasticsearch并进行设置。此外,如何使用  Spring Boot  和  Spring Data ElasticSearch  项目与Elasticsearch 引擎集成  。

首先,安装和设置elasticsearch引擎。

现在,我们将开发一个Spring Boot应用程序,它将展示  ElasticsearchTemplate 和  ElasticsearchRepository 访问Elasticsearch引擎的方式并进行CRUD操作。在开发应用程序之前,让我们首先了解  ElasticsearchTemplate 和  ElasticsearchRepository的 工作原理。

ElasticsearchTemplate - 它是一个实现ElasticsearchOperations的Template类  。 它比ElasticsearchRepository更强大,因为它可以做的不仅仅是CRUD操作。它具有创建,删除索引,批量上传的操作。它也可以进行聚合搜索。

ElasticsearchRepository  - 如果我们定义一个扩展ElasticsearchRepository的接口  , 它由Spring数据Elasticsearch提供,它将自动为该Document提供CRUD操作。例如,通过扩展ElasticsearchRepository,UserRepository接口在下面定义了“ User ”文档。现在可以在用户文档上完成所有查找,保存,删除,更新默认操作。

@Repository
public interface UserRepository extends ElasticsearchRepository<User, String> {
}

它扩展了ElasticsearchCrudRepository,最终扩展了Repository接口。此存储库接口是Spring数据的标准功能。无需提供此接口的实现。您也可以使用@Query 注释编写自定义查询。

Maven配置:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

配置application.properties  ,ElasticsearchTemplate 和  ElasticsearchRepository用这个配置来连接引擎。我使用了诸如集群节点之类的传输客户端属性和索引名称来连接elasticsearch引擎。

#application.properties

# Local Elasticsearch config
spring.data.elasticsearch.repositories.enabled=true
spring.data.elasticsearch.cluster-nodes=localhost:9300
spring.data.elasticsearch.cluster-name=elasticsearch

elasticsearch.index.name=my_index
elasticsearch.user.type=user

# App config
server.port=8102
spring.application.name=BootElastic

Mappings

在Elasticsearch中,  Index 就像RDBMS中的DB,  Mappings / Type  类似于RDBMS中的表。 Document 是属于某种类型并位于索引中的字段的集合。Spring数据提供了像@ Document 这样的注释来创建文档。在这里,我们将User定义为索引为“ my_index ”并键入“ user ” 的文档。

@Document(indexName = "my_index", type = "user")
public class User {

    @Id
    private String userId;
    private String name;
    private Date creationDate = new Date();
    private Map<String, String> userSettings = new HashMap<>();

 //getter and setters 
}

控制器

第一个控制器是  UserController。它将使用  UserDAOImpl 让  ElasticserachTemplate 与Elasticsearch  Engine 交互  。 

@RestController
public class UserController {

    @Autowired
    private UserDAO userDAO;

    @RequestMapping("/all")
    public List<User> getAllUsers() {
        return userDAO.getAllUsers();
    }
     @RequestMapping(value = "/new", method = RequestMethod.POST)
    public User addUsers(@RequestBody User user) {
        userDAO.addNewUser(user);
        return user;
    }
  --- Other methods
}

UserDAOImpl - 此类初始化elasticsearchtemplate并使用queryForList方法检索数据。

@Repository
public class UserDAOImpl implements UserDAO {

    private final Logger LOG = LoggerFactory.getLogger(getClass());

    @Value("${elasticsearch.index.name}")
    private String indexName;

    @Value("${elasticsearch.user.type}")
    private String userTypeName;

    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Override
    public List<User> getAllUsers() {
        SearchQuery getAllQuery = new NativeSearchQueryBuilder()
                .withQuery(matchAllQuery()).build();
        return esTemplate.queryForList(getAllQuery, User.class);
    }
  // Other methods
}

另一个Controller是UserRepositoryConroller。这是使用UserRepository与elasticsearch引擎进行交互。

@RestController
@RequestMapping("/repo")
public class UserRepositoryController {

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("/all")
    public List<User> getAllUsers() {
        List<User> users = new ArrayList<>();
        userRepository.findAll().forEach(users::add);
        return users;
    }
  //Other methods
}

此Repository类扩展了ElasticsearchRepository类,该类在内部扩展了  ElasticsearchCrudRepository - >   PagingAndSortingRepository

@Repository
public interface UserRepository extends ElasticsearchRepository<User, String> {
}

你可以在github链接找到完整的代码 - https://github.com/RajeshBhojwani/spring-boot-elasticsearch.git

构建应用程序

可以使用Maven命令构建应用程序。

 mvn clean install  

将构建代码并创建  elasticsearch-0.0.1-SNAPSHOT.jar   文件。

运行该应用程序

     java -jar  target/elasticsearch-0.0.1-SNAPSHOT.jar  

将启动该应用程序。应用程序将侦听application.properties  文件中定义的  端口  8102 。

测试应用程序 -

测试  使用ElasticsearchTemplate的UserController 流程。

第1步  - 添加新用户。使用此REST API URL添加新用户  http://localhost:8102/new 

在Request正文中添加Json数据。

{
  "name": "Sumit",
   "userSettings": {
   "gender" : "male",
   "occupation" : "CA",
   "hobby" : "chess"
   }
}

第2步  - 检查响应。您将看到使用userId生成的新用户,该文档是此文档的唯一ID。输出如下:

{
    "userId": "AWdj-3KcTJbZRlQtLZfO",
    "name": "Sumit",
    "creationDate": 1543570682521,
    "userSettings": {
        "gender": "male",
        "occupation": "CA",
        "hobby": "chess"
    }
}

第3步  - 检索所有用户。使用   http://localhost:8102/all

{
        "userId": "AWdj-3KcTJbZRlQtLZfO",
        "name": "Sumit",
        "creationDate": 1543570682521,
        "userSettings": {
            "gender": "male",
            "occupation": "CA",
            "hobby": "chess"
        }
    },
    {
        "userId": "AWdZuKFRgzULDLBu_Y0c",
        "name": "Suresh",
        "creationDate": 1543398531296,
        "userSettings": {}
    }

测试  使用ElasticsearchRepository的UserRepositoryController 流。

第1步  - 添加新用户。使用此REST API URL添加新用户  http://localhost:8102/repo/new 

像我们在之前的测试用例中那样在Request体中添加Json数据。

第2步  - 检查响应。您将看到使用userId生成的新用户,该文档是此文档的唯一ID。

Transport 客户端库

如何使用传输客户端库与最新版本的Elasticsearch引擎进行交互?我们可以直接从代码中调用Elasticsearch的REST API进行CRUD,也可以使用Elasticsearch提供的传输Transport客户端。

Maven依赖:需要Elasticsearch,一个传输客户端和log4j jar。

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.7</version>
</dependency>
</dependencies>

 配置:

由于我们将使用传输客户端连接到Elasticsearch引擎,因此我们需要为引擎的群集节点提供URL路径。所以我已将属性放在application.properties文件中,用于URL 的主机和端口。

# Local Elasticsearch config
elasticsearch.host=localhost
elasticsearch.port=9300
# App config
server.port=8102
spring.application.name=BootElastic

创建一个名为的域类User。JSON输入将映射到此  User 对象。这将用于创建与索引和类型关联的用户文档。 

public class User {
    private String userId;
    private String name;
    private Date creationDate = new Date();
    private Map<String, String> userSettings = new HashMap<>();
  -- getter/setter methods
}

创建Java配置文件以创建连接到Elasticsearch集群节点的传输客户端。它还从application.properties文件配置的环境加载主机和端口的值  。

@Configuration
public class config{
    @Value("${elasticsearch.host:localhost}") 
    public String host;
    @Value("${elasticsearch.port:9300}") 
    public int port;
    public String getHost() {
return host;
}
public int getPort() {
return port;
    }
    @Bean
    public Client client(){
        TransportClient client = null;
        try{
            System.out.println("host:"+ host+"port:"+port);
            client = new PreBuiltTransportClient(Settings.EMPTY)
            .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;
    }
}

UserController 创建以展示以下功能:

  1. 创建一个名为“ users ” 的索引并键入“ employee ”。它将创建一个用于存储用户信息的文档。文档的id可以作为JSON输入传递,如果没有传递,Elasticsearch将生成自己的id。客户端有一个称为方法  prepareIndex() 构建文档对象和存储针对索引和类型。这方法是一种  POST 方法调用,其中  User 信息将作为JSON传递。

@Autowired
    Client client;
    @PostMapping("/create")
    public String create(@RequestBody User user) throws IOException {
        IndexResponse response = client.prepareIndex("users", "employee", user.getUserId())
                .setSource(jsonBuilder()
                        .startObject()
                        .field("name", user.getName())
                        .field("userSettings", user.getUserSettings())
                        .endObject()
                )
                .get();
               System.out.println("response id:"+response.getId());
        return response.getResult().toString();
    }

2.根据传递的“id”查看用户信息。客户端有一种  prepareGet() 方法可以根据索引,类型和id检索信息。它将以JSON格式返回用户信息。 

@GetMapping("/view/{id}")
    public Map<String, Object> view(@PathVariable final String id) {
        GetResponse getResponse = client.prepareGet("users", "employee", id).get();
        return getResponse.getSource();
    }

3.根据字段名称查看用户信息。我用  matchQuery() 这里搜索“ 名称 ”字段并返回  User 信息。但是,班级有许多不同类型的可用   。例如,用于  搜索特定范围内的字段值,例如10到20年之间的年龄。有一种    方法可以使用通配符搜索字段:

@GetMapping("/view/name/{field}")
    public Map<String, Object> searchByName(@PathVariable final String field) {
        Map<String,Object> map = null;
        SearchResponse response = client.prepareSearch("users")
                                .setTypes("employee")
                                .setSearchType(SearchType.QUERY_AND_FETCH)
                                .setQuery(QueryBuilders..matchQuery("name", field))
                                .get()
                                ;
        List<SearchHit> searchHits = Arrays.asList(response.getHits().getHits());
        map =   searchHits.get(0).getSource();
        return map;
    }

4.通过使用Id搜索文档来更新文档并替换字段值。客户端有一个名为的方法   update()。它接受  UpdateRequest 更新查询的输入。

@GetMapping("/update/{id}")
    public String update(@PathVariable final String id) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index("users")
                .type("employee")
                .id(id)
                .doc(jsonBuilder()
                        .startObject()
                        .field("name", "Rajesh")
                        .endObject());
        try {
            UpdateResponse updateResponse = client.update(updateRequest).get();
            System.out.println(updateResponse.status());
            return updateResponse.status().toString();
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(e);
        }
        return "Exception";
    }

5.最后一种方法是展示如何删除索引和类型的文档。客户端确实有一个  prepareDelete() 接受索引,类型和id的方法来删除文档。 

@GetMapping("/delete/{id}")
    public String delete(@PathVariable final String id) {
        DeleteResponse deleteResponse = client.prepareDelete("users", "employee", id).get();
        return deleteResponse.getResult().toString();
    }

代码见:GitHub.

测试应用

该应用程序将在http://localhost:8102URL 上运行  。现在让我们测试一下我们上面讨论过的几个用例。

1.测试创建文档。

通过curl 或Postman  启动  。http://localhost:8102/rest/users/createPOST 

输入:

{
"userId":"1",
"name": "Sumit",
"userSettings": {
"gender" : "male",
"occupation" : "CA",
"hobby" : "chess"
}
}

您将看到显示“已创建”的响应。

2.要测试文档是否已创建,让我们测试视图功能。

 启动。http://localhost:8102/rest/users/view/1GET 

作为响应,您将看到id的用户信息,其值为“1”。

{
"userSettings": {
"occupation": "CA",
"gender": "male",
"hobby": "chess"
},
"name": "Rajesh"
}

3.您可以通过名称字段查看用户信息以及启动  http://localhost:8102/rest/users/view/name/Rajesh。这是将“Rajesh”作为“名称”字段值传递。

同样,可以通过启动  和 来测试更新和删除功能  。http://localhost:8102/rest/users/update/1http://localhost:8102/rest/users/delete/1