Java/Kotlin Spring操作Elasticsearch一遍就會

Elasticsearch基本概念

Elasticsearch有四種構成存儲空間的基本結構:Indices、Types、Documents、Fields。
將這四種結構和關係型數據庫對比,能更好說明它們是什麼:

  • Indices看作Database(數據庫);
  • Types看作Tables(表);
  • Documents看作Rows(行);
  • Fields看作Columns(列)。

選擇操作ES的客戶端

官方提供很多操作ES的客戶端,比如:

  • Transport Client
  • High Level REST Client
  • 更多客戶端詳細信息可以去Spring Data Elasticsearch查看。如果你沒那麼多時間,本文可以讓你快速學會,在Spring中對ES增刪改查和條件分頁查詢的簡單操作。

本文使用High Level REST Client對ES增刪改查。因爲這是官方強烈推薦的。

Gradle引入High Level REST Client依賴

Gradle中引入依賴:

implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.4.2'

Java 實例

依賴注入Bean

@Autowired
private RestHighLevelClient restHighLevelClient;

插入

IndexRequest request = new IndexRequest("order")
		.id(data.roundId)
		.source(gson.toJson(data), XContentType.JSON);

restHighLevelClient.index(request, RequestOptions.DEFAULT);
  • IndexRequest("order"),表示index名;
  • id(data.roundId),表示document名字;
  • source(gson.toJson(data), XContentType.JSON),表示存入data對象轉json的字符串。

刪除

DeleteRequest deleteRequest = new DeleteRequest("order", data.roundId);
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

更新

UpdateRequest updateRequest = new UpdateRequest("order", data.roundId)
		.doc(gson.toJson(data), XContentType.JSON);
		
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);

查詢(條件分頁)

SearchRequest request = new SearchRequest("order");
request.source(
        new SearchSourceBuilder()
                .query((QueryBuilder) QueryBuilders.boolQuery()
                    .must(()->{
                            if(path != null){
                                QueryBuilders.termQuery(String.valueOf(Player.class.getField("name")), ip);
                            }
                        }
                    )
                    .must(
                        // 如有多個條件,添加參照上個 must
                    )
                )
                .sort(JsonUtils.class.getField("inTime"), SortOrder.DESC)
                .size(limit)
                .from(offset)
);

SearchHits hits = restHighLevelClient.search(request, RequestOptions.DEFAULT).getHits();
long total = hits.getTotalHits().value;
List<Player> result = null;
Stream.of(hits.getHits()).forEach(y -> result.add( gson.fromJson(y.getSourceAsString(), Player.class) ));
  • must()表示查詢條件,還有should()mustNot()等,must裏的lambda表達式表示有值的時候才查詢,更方便根據前端傳入的參數進行條件查詢。
  • limit爲前端傳入參數,表示查多少個。 offset爲前端傳入參數,表示從哪裏開始查。

Kotlin 實例

依賴注入Bean

@Autowired
lateinit var restHighLevelClient: RestHighLevelClient

插入

val request = IndexRequest("order")
		.id(data.roundId)
        .source(gson.toJson(data), XContentType.JSON)
        
restHighLevelClient.index(request, RequestOptions.DEFAULT)
  • IndexRequest("order"),表示index名;
  • id(data.roundId),表示document名字;
  • source(gson.toJson(data), XContentType.JSON),表示存入data對象轉json的字符串。

刪除

val deleteRequest = DeleteRequest("order", roomPlayer.roomId.toString() + "rpl" + roomPlayer.playerId)
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT)

更新

val updateRequest = UpdateRequest("order", data.orderId)
		.doc(gson.toJson(data), XContentType.JSON)
                
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT)

查詢(條件分頁)

inline fun <T> T.applyIf(validate: Boolean, block: T.() -> Unit): T {
    if (validate) {
        block()
    }
    return this
}
val request = SearchRequest("order")
request.source(
        SearchSourceBuilder()
                .query(QueryBuilders.boolQuery()
                        .applyIf(startTime != null && endTime != null) {
                            must(
                                    QueryBuilders.rangeQuery(MainVisitRecordPO::inTime.name)
                                            .gte(startTime!! * 1000)
                                            .lte(endTime!! * 1000)
                            )
                        }
                        .applyIf(ip != null) {
                            must(
                                    QueryBuilders.termQuery(MainVisitRecordPO::ip.name, ip)
                            )
                        }
                )
                .sort(MainVisitRecordPO::inTime.name, SortOrder.DESC)
                .size(pageable.limit)
                .from(pageable.offset)
)
val hits = restHighLevelClient.search(request, RequestOptions.DEFAULT).hits
val total = hits.totalHits?.value?:0
val result = hits.hits.map { gson.fromJson(it.sourceAsString, MainVisitRecordPO::class.java) }
  • must()表示查詢條件,還有should()mustNot()等,must裏的lambda表達式表示有值的時候才查詢,更方便根據前端傳入的參數進行條件查詢。
  • limit爲前端傳入參數,表示查多少個。 offset爲前端傳入參數,表示從哪裏開始查。

注意

對Elasticsearch增、刪、改的操作,並不是實時的。也就是更改A數據後立刻查詢A數據,得到的結果依舊是未更新的A數據。
需要立刻更新ES需要使用setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)設置,比如更新操作:

val updateRequest = UpdateRequest("order", data.orderId)
                .doc(gson.toJson(data), XContentType.JSON)
                .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)

這樣會對ES性能影響,所以並不推薦使用ES做業務,來儘量規避這類問題。
所以ES終究是ES,別拿它當數據庫用。

無關技術的奇怪姿勢

科普一些無關技術的奇怪的姿勢,多學點東西總歸是有用的吧。

智和德

  • 智:是指拋開利益、權利去思考問題。這可能不是此刻解決問題的最優解,但這樣做可以得到最接近真理的答案,是尋找真理的最有效方法。
  • 德:世界上不可能每個人都和你思想一致,做到理解和包容別人的思想就是德。

利用智和德去看待美國現在的種族歧視,去思考他們左派(民主黨)和右派(共和黨)的思想,真能受益匪淺。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章