使用Spring Boot和Elasticsearch教程

banq發表於2019-01-14

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

相關文章