1. 程式人生 > >恕我直言,我也是才知道ElasticSearch條件更新是這麼玩的

恕我直言,我也是才知道ElasticSearch條件更新是這麼玩的

## 背景 ElasticSearch 的使用度越來越普及了,很多公司都在使用。有做日誌搜尋的,有做商品搜尋的,有做訂單搜尋的。 大部分使用場景都是通過程式定期去匯入資料到 ElasticSearch 中,或者通過 CDC 的方式來構建索引。在這種場景下,更新資料都是單條更新,比如 ID=1 的資料發生了修改操作,那麼就會把 ElasticSearch 中 ID=1 的這條資料更新下。 但有些場景下需要根據條件同時更新多條資料,就像 Mysql 中我們使用 Update Table Set Name=XXX where Age=18 去更新一批資料一樣。 正好有同學微信問我怎麼批量更新,接下來就看看在 ElasticSearch 中是如何去進行按條件更新的操作。 ## 單條更新 ElasticSearch 的客戶端官方推薦使用 elasticsearch-rest-high-level-client。所以本文也是基於 elasticsearch-rest-high-level-client 來構建程式碼。 首先來回顧下單條資料的更新是怎麼做的,程式碼如下: ```plain UpdateRequest updateRequest = new UpdateRequest(index, type, id); updateRequest.doc(documentJson, XContentType.JSON); restHighLevelClient.update(updateRequest, options); ``` 構建 UpdateRequest 的時候就指定了索引,型別,ID 三個欄位,也就精確到了某一條資料,所以更新的自然也是這一條資料。 ## 條件更新 首先我們準備幾條測試資料,如下: ```plain {     id: 1,     title: "Java怎麼學",     type: 1,     userId: 1,     tags: [         "java"     ],     textContent: "我要學Java",     status: 1,     heat: 100 } {     id: 2,     title: "Java怎麼學",     type: 1,     userId: 1,     tags: [         "java"     ],     textContent: "我要學Java",     status: 1,     heat: 100 } ``` 假如我們的需求是將 userId=1 的所有文件資料改成無效,也就是 status=0。如果不用按條件更新,你就得查詢出 userId=1 的所有資料,然後一條條更新,這就太慢了。 下面看看按條件更新是如何使用的,如下: ```plain POST http://47.105.66.210:9200/article_v1/doc/_update_by_query {     "script": {         "source":"ctx._source['status']=0;"     },     "query": {         "term": {             "userId": 1         }     }  } ``` 按條件更新需要使用\_update_by_query 來進行,query 用於指定更新資料的匹配條件,script 用於更新的邏輯。 詳細使用文件: [https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html "https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html") [https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html "https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html") 在 Java 程式碼中如何實現條件更新呢? ```plain UpdateByQueryRequest request = new UpdateByQueryRequest("article_v1"); request.setQuery(new TermQueryBuilder("userId", 1)); request.setScript(new Script("ctx._source['status']=0;")); restHighLevelClient.updateByQuery(request, RequestOptions.DEFAULT); ``` 是不是也很簡單,跟單條資料更新差不多,使用 UpdateByQueryRequest 構建更新物件,然後設定 Query 和 Script 就可以了。 ## 條件更新陣列 比如我們的需求是要移除 tags 中的 java,如下: ```plain POST http://47.105.66.210:9200/article_v1/doc/_update_by_query {     "script": {         "source":"ctx._source['tags'].removeIf(item -> item == 'java');"     },     "query": {         "term": {             "userId": 1         }     }  } ``` 新增的話只需要將 removeIf 改成 add 就可以了。 ```plain ctx._source['tags'].add('java'); ``` 如果有特殊的業務邏輯,Script 中還可以寫判斷來判斷是否需要修改。 ```plain POST http://47.105.66.210:9200/article_v1/doc/_update_by_query {     "script": {         "source":"if(ctx._source.type == 11) {ctx._source['tags'].add('java');}"     },     "query": {         "term": {             "userId": 1         }     }  } ``` ## 封裝通用的條件更新 大部分場景下的更新都比較簡單,根據某個欄位去更新某個值,或者去更新多個值。在 Java 中如果每個地方都去寫指令碼,就重複了,最好是抽一個比較通用的方法來更新。 **下面是簡單的示列,其中還有很多需要考慮的點,像資料型別我只處理了數字,字串,和 List,其他的大家需要自己去擴充套件。** ```plain public BulkByScrollResponse updateByQuery(String index, QueryBuilder qu