環境:
操作系統:win7
elasticsearch版本:5.4.4
java:1.8
參考文章:
1、Elasticsearch權威指南(中文版)
2、Elasticsearch筆記五之java操作es
3、java操作ElasticSearch(es)進行增刪查改操作
4、ElasticSearch入門-增刪改查(CRUD)
5、elasticsearch——Java API的使用
6、elasticsearch源碼分析之plugin的開發
7、Elasticsearch 5.X下JAVA API使用指南
——————————————————————————————————————
8、elasticsearch5.2.2 插件開發(一)
9、elasticsearch5.2.2 插件開發(二) 第一個有實際功能的插件 - CSDN博客
10、elasticsearch5.2.2 插件開發(三)ScriptPlugin 的實現 - CSDN博客
——————————————————————————————————————
11、Maven通俗講解
12、如何添加本地JAR文件到Maven項目中 - CSDN博客
13、Maven Repository: weka
14、maven系列--maven添加第三方、本地依賴 - CSDN博客
15、Maven配置項目依賴使用本地倉庫的方法彙總 - EasonJim - 博客園
有關打包部分的視頻在我的百度網盤
鏈接:https://pan.baidu.com/s/1DuIWN2uDtcA8guP_Ny91Vw 密碼:tj65
第一部分:代碼
一、創建maven項目
首先創建一個maven項目,填寫Group Id爲com.plugin和Artifact Id爲esPlugin。其中Artifact Id是項目名,Group Id是項目中包的名字。
二、修改pom.xml配置文件
修改pom.xml文件,然後右鍵項目名->maven->update maven。會發現在Maven Dependencies文件夾下多了很多包(見下圖),這些都是pom.xml文件添加進去的依賴,省去了手動導包的麻煩。(包全是在maven的中央倉庫下載到本地的,pom.xml代碼見頁面底部。)
三、核心代碼實現
代碼目錄如下(用的是之前的項目的截圖,忽略頂部的包名),在文後的鏈接中下載項目,會有這麼多包,真正用到的和需要修改的類是TasteEventRestAction.java,這裏麪包括註冊URL,以及執行對應的操作。
- package com.taste.elasticsearch_taste.rest;
-
- import static com.taste.elasticsearch_taste.action.LoggerUtils.emitErrorResponse;
- import static org.elasticsearch.rest.RestRequest.Method.POST;
-
- import java.io.File;
- import java.io.IOException;
-
- import org.elasticsearch.action.ActionListener;
- import org.elasticsearch.action.search.SearchRequest;
- import org.elasticsearch.action.search.SearchRequestBuilder;
- import org.elasticsearch.action.search.SearchResponse;
- import org.elasticsearch.client.node.NodeClient;
- import org.elasticsearch.common.inject.Inject;
- import org.elasticsearch.common.settings.Settings;
- import org.elasticsearch.common.xcontent.ToXContent;
- import org.elasticsearch.common.xcontent.XContentBuilder;
- import org.elasticsearch.index.query.QueryBuilders;
- import org.elasticsearch.rest.BaseRestHandler;
- import org.elasticsearch.rest.BytesRestResponse;
- import org.elasticsearch.rest.RestController;
- import org.elasticsearch.rest.RestRequest;
- import org.elasticsearch.rest.RestStatus;
- import org.elasticsearch.rest.action.search.RestSearchAction;
- import org.elasticsearch.search.SearchHits;
-
- import com.pligin.esPlugin.action.TasteEventAction;
- import com.pligin.esPlugin.common.TasteEventRequestBuilder;
- import com.pligin.esPlugin.common.TasteEventResponse;
-
- //我項目中的類
- //import ding.util.ESUtil.ActionParameter;
- //import ding.util.WEKAUtil.MeachineLearningBuilder;
-
-
- public class TasteEventRestAction extends BaseRestHandler{
- public ActionParameter parameter;
-
- @Inject
- public TasteEventRestAction(final Settings settings,final RestController restController) {
- super(settings);
- restController.registerHandler(RestRequest.Method.GET, "/_taste/{action}", this); // 註冊URL1,該URL中最後的字符串賦給變量action
- restController.registerHandler(RestRequest.Method.GET, "/_taste", this); // 註冊URL2
- }
-
- @Override
- protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
- if (request.method() == POST && !request.hasContent()) {
- return channel -> emitErrorResponse(channel, logger, new IllegalArgumentException("Request body was expected for a POST request."));
- }
- String action = request.param("action");
-
- // 遇到URL1,執行createDoSomethingResponse
- if (action != null) {
- logger.info("do something action -- ESData & MeachineLearning");
- return createDoSomethingResponse(request, client);
-
- // 遇到URL2,執行createMessageResponse
- } else {
- logger.info("do Message action -- do anything without parameter");
- return createMessageResponse(request);
- }
- }
-
- // 一、對應URL爲 /_taste/{action}
- private RestChannelConsumer createDoSomethingResponse(RestRequest request, NodeClient client){
- /*這裏request存放的是URL,使用request.param("action")可以獲得URL中的action變量值(可作爲參數使用)
- 我這裏對action做了一個解析,我的參數規格是
- http://127.0.0.1:9200/_taste/index=logstash-20180323&from=0&size=100&clfName=J48&classifyAttributeName=tes_tims
- 可以通過 parameter.Index, parameter.From, parameter.Size獲得action中的參數
- 然後通過前幾個包裏面的TasteEventRequestBuilder等類,在es裏面進行查詢,得到輸出myresponse
- 最後使用異步編程,即return channel,可以對es傳出來的數據進行處理,處理結果作爲String,加入builder裏面,由channel返回
- */
- String action = request.param("action");
- this.parameter= new ActionParameter(action);
-
- // 1、通過parameter進行數據查詢,構造自定義的request請求
- final TasteEventRequestBuilder actionBuilder=new TasteEventRequestBuilder(client);
- SearchRequestBuilder requestBuilder = client.prepareSearch(parameter.Index).setQuery(QueryBuilders.matchAllQuery()).setFrom(parameter.From).setSize(parameter.Size).setExplain(true);
- SearchRequest searchRequest=new SearchRequest();
- try {
- RestSearchAction.parseSearchRequest(searchRequest, request, null);
- } catch (IOException e1) {
- logger.debug("Failed to emit response.", e1);
- e1.printStackTrace();
- }
- actionBuilder.setSearchRequest(requestBuilder);
- return channel -> client.execute(TasteEventAction.INSTANSE, actionBuilder.request(),new ActionListener<TasteEventResponse>() {
- @Override
- public void onResponse(TasteEventResponse response) {
- try{
- // 2、獲得查詢數據
- SearchResponse myresponse=response.getSearchResponse();
- SearchHits ESHits = myresponse.getHits();
-
- // 3、調用機器學習功能
- String res = new MeachineLearningBuilder().getOutput(ESHits, parameter);
-
- //將response轉換成json類型,不過這個未轉化成功
- XContentBuilder builder = channel.newBuilder();
- builder.startObject();
- builder.field("res", res);
- //response.toXContent(builder, request); // 此處可以寫個覆蓋函數
- builder.endObject();
- channel.sendResponse( new BytesRestResponse(RestStatus.OK, builder));
- }catch(Exception e){
- logger.debug("Failed to emit response.", e);
- onFailure(e);
- }
- }
-
- @Override
- public void onFailure(Exception e) {
- emitErrorResponse(channel, logger, e);
- }
- });
- }
-
-
-
-
- // 2、對應URL爲 /_taste
- private RestChannelConsumer createMessageResponse(RestRequest request) {
- // 也可以像這裏面一樣,不使用es裏面的通信,直接對builder進行處理,然後在builder裏面寫入自定義的一些東西,最後可以輸出在網頁上
-
- return channel -> {
- Message message = new Message();
- XContentBuilder builder = channel.newBuilder();
- builder.startObject();
- message.toXContent(builder, request);
- builder.endObject();
- channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder));
- };
- }
-
- class Message implements ToXContent {
- @Override
- public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
- return builder.field("message", "This is my first plugin Run without error");
- }
- }
四、插件打包與安裝
在./src/main下創建 resources 文件夾,在文件夾中創建 plugin-descriptor.properties 文件,
- description=elasticsearch-esPlugin-5.4.0.1 for Elasticsearch #隨便寫
- version=5.4.1 #es的版本
- name=esPlugin #寫項目名
- site=${elasticsearch.plugin.site}
- jvm=true
- classname=com.plugin.esPlugin.plugin.TastePlugin #根據自己的TastePlugin的目錄修改
- java.version=1.8
- elasticsearch.version=5.4.1
- isolated=${elasticsearch.plugin.isolated}
打包,在pom.xml所在目錄,進入cmd,輸入目錄 mvn clean install,會
在打包後在target目錄下將看到新生成的jar包。
然後新建一個文件夾,文件夾名字爲elasticsearch,將這個jar包放入這個文件夾,並將之前寫的plugin-descriptor.properties文件複製一份放入這個文件夾(注意文件夾的名字一定爲elasticsearch,不能爲其他名字!!)
最後將這個文件夾壓縮爲zip,壓縮後文件的名字可以隨便起,這裏起爲elasticseach_taste_5.4.1.zip,然後在那個最開始下載的軟件版的elasticsearch5.4.1的bin目錄下執行elasticsearch-plugin install file:F:/java_programming/elasticsearch_taste1/target/elasticseach_taste_5.4.1.zip操作安裝插件,安裝好可以在plugins文件夾下看到自定義的插件,將這個插件複製一份到源碼的core中的plugins文件夾下也可以。
五、demo的實驗效果
第二部分、超簡潔的命令介紹
—————————————————————————————————
1、使用該命令對插件(maven項目)進行打包
mvn clean install
—————————————————————————————————
2、刪除之前安裝的插件(不刪除的話會重複佔用URL報錯)
elasticsearch-plugin remove esmlplugin2
elasticsearch-plugin remove taste
3、安裝新的插件(已經壓縮爲F盤下的taste-5.4.0.1.zip)
elasticsearch-plugin install file:F:/esmlplugin2-5.4.0.1.zip
elasticsearch-plugin install file:F:/taste-5.4.0.1.zip
這個命令效果如下圖所示,完成之後,會在 elasticsearch5.4.4/plugin 目錄下生成名爲taste的文件夾(文件夾名字根據.property中的name確定),可以直接將此文件夾複製到源碼中 /core/plugin 目錄下,在源碼中運行。
—————————————————————————————————
4、啓動
elasticsearch.bat
第三部分:測試類介紹
測試類在com.taste.elasticsearch_taste包下,核心代碼是TasteEventRestTest類。該類功能是發出http的get請求,獲取數據。代碼如下:
- package com.taste.elasticsearch_taste;
-
- import java.net.InetSocketAddress;
-
- import org.apache.http.HttpResponse;
-
- import org.apache.http.client.methods.HttpGet;
-
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.HttpClientBuilder;
- import org.apache.http.util.EntityUtils;
- import org.elasticsearch.common.network.NetworkAddress;
-
- //import org.elasticsearch.common.xcontent.XContentType;
-
- public class TasteEventRestTest extends TastePluginTest {
- public void test_recommended_items_from_user() throws Exception {
- // final String index = "blog";
- // XContentType type = randomFrom(XContentType.values());
- try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
- InetSocketAddress[] endpoint = cluster().httpAddresses();
- this.restBaseUrl = "http://" + NetworkAddress.format(endpoint[0]);
-
- String s1 = "/_taste/parameter?index=blog&from=0&size=10&clfName=RandomForest&classifyAttributeName=classify";
- // String s2 = "/_taste";
- HttpGet get=new HttpGet(restBaseUrl + s1);
- System.out.println("post請求已發送11111111111");
- HttpResponse response = httpClient.execute(get);
- System.out.println("post請求已發送");
-
- System.out.println("得到返回值:");
- if(response.getStatusLine().getStatusCode()==200){//如果狀態碼爲200,就是正常返回
- String result=EntityUtils.toString(response.getEntity());
- //得到返回的字符串
- System.out.println(result);
- }
-
- }
- }
- }
在測試類中開啓的es裏是沒有任何數據的,需要在HttpGet get=new HttpGet(restBaseUrl + s1);這裏加上斷點,debug到這句話,然後使用git想es裏面輸入數據,下面給出一個數據樣例。然後繼續debug,會執行項目第一部分中的核心代碼。
"得到返回值:"
);
- if(response.getStatusLine().getStatusCode()==200){//如果狀態碼爲200,就是正常返回
- String result=EntityUtils.toString(response.getEntity());
- //得到返回的字符串
- System.out.println(result);
- }
-
- }
- }
- }
在測試類中開啓的es裏是沒有任何數據的,需要在HttpGet get=new HttpGet(restBaseUrl + s1);這裏加上斷點,debug到這句話,然後使用git想es裏面輸入數據,下面給出一個數據樣例。然後繼續debug,會執行項目第一部分中的核心代碼。
- curl -XPUT http://127.0.0.1:9200/blog
- curl -XPUT http://127.0.0.1:9200/blog/es/1 -d '{"a":"1","b":"3","c":"4","d":"6","e":"8","path":"xxx","classify":"a"}'
- curl -XPUT http://127.0.0.1:9200/blog/es/2 -d '{"a":"4",&qu