市長信箱郵件查詢服務: 使用Elasticsearch 替代 Mysql 我在上一篇文章中實現了一個基於Springboot構建的web應用: 市長信箱郵件查詢服務. 應用將郵件信息抓取後保存在Mysql中,用以提供給搜索Web使用.Mysql雖然集成簡單,能快速實現功能, 但like查詢性能一般, ...
市長信箱郵件查詢服務: 使用Elasticsearch 替代 Mysql
我在上一篇文章中實現了一個基於Springboot構建的web應用: 市長信箱郵件查詢服務. 應用將郵件信息抓取後保存在Mysql中,用以提供給搜索Web使用.Mysql雖然集成簡單,能快速實現功能, 但like查詢性能一般, 尤其數據量大了之後就必須考慮使用搜索引擎. 所以這次我把存儲從Mysql替換為Elasticsearch(ES).
Elasticsearch提供了兩種方式API來進行調用: Rest API與Java Native. Java Native的執行效率更高, 並且與當前項目集成更方便,所以我這裡選擇了Java native Api. 引入Java native Api,只需要在根pom.xml添加依賴:
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>2.3.3</version>
</dependency>
使用ES替換Mysql,需要考慮這兩方面:
- 存儲: 將Mysql的insert插入數據改為ES的添加文檔操作.
- 查詢: 替換Mysql的查詢sql語句,改為ES的搜索操作.
存儲:
由於我們的郵件結構簡單,沒有內嵌其他複雜對象, 所以從mysql轉換為ES的文檔非常自然. 只需調用ES的添加文檔API即可:
public static void indexMails(Iterable<Mail> mails) {
BulkRequestBuilder bulkRequest = client.prepareBulk();
for (Mail mail : mails) {
addMailIndexRequest(bulkRequest, mail);
}
BulkResponse bulkResponse = bulkRequest.get();
System.out.println(JSON.toJSONString(bulkResponse));
}
private static void addMailIndexRequest(BulkRequestBuilder bulkRequest, Mail mail) {
try {
bulkRequest.add(client.prepareIndex("chengdu12345", "mail")
.setSource(XContentFactory.jsonBuilder()
.startObject()
.field("content", mail.content)
.field("createDate", mail.createDate)
.field("acceptUnit", mail.acceptUnit)
.field("category", mail.category)
.field("result", mail.result)
.field("sender", mail.sender)
.field("status", mail.status)
.field("title", mail.title)
.field("url", mail.url)
.field("views", mail.views)
.endObject())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
在以上代碼中,我使用了批量添加(bulkRequest)API, 以減少API調用次數.
查詢:
之前Mysql中郵件的查詢sql如下:
select m from cheng12345 m where m.title like ? or m.content like ? or m.result like ? order by create_date desc limit ?,?
用ES來實現SQL對應效果的代碼也非常簡單:
public static SearchHits searchByKeyword(String keyword, int from, int size) {
SearchRequestBuilder request = client.prepareSearch("chengdu12345")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content", "result")
.type(MatchQueryBuilder.Type.PHRASE))//完全匹配
.addSort("createDate", SortOrder.DESC)
.setFrom(from).setSize(size).setExplain(true);
SearchResponse response = request.execute().actionGet();
return response.getHits();
}
這裡需要註意的是,查詢類型我選擇的是短語查詢(Type.PHRASE), 它能保證搜索時輸入的短語不被拆分, 否則我輸入"紅牌樓"查詢時,可能返回一堆包含"紅樓"的郵件列表, 這明顯不是我想要的結果.
另外, Springdata同樣提供了對ES的支持, 它像對JPA一樣提供了基於Repository模板方法支持,利用它能簡化不少對ES操作的代碼.