1. 程式人生 > >java+Elasticsearch搜尋引擎做增刪改查操作(Springboot專案)

java+Elasticsearch搜尋引擎做增刪改查操作(Springboot專案)

Elasticsearch查詢搜尋真的是非常快,所以企業資料量大的一般都儲存到Elasticsearch裡。用來分析,挖掘資料,

Elasticsearch的索引就相當於資料庫,型別,就相當於表,文件就相當於資料庫的row

索引-index
- 一個索引就是一個擁有幾分相似特徵的文件的集合
- 相當於資料庫中的database

型別-type
- 一個型別是你的索引的一個邏輯上的分類/分割槽
- 通常,會為具有一組共同欄位的文件定義一個型別
- 相當於資料庫中的table

文件-document
- 一個文件是一個可被索引的基礎資訊單元
- 文件以JSON(Javascript Object Notation)格式來表示
- 相當於資料庫中的row

分片-shard
- 一個分片是一個 Lucene 的例項,以及它本身就是一個完整的搜尋引擎
- 一個索引可以有多個分片,必須在建立索引的時候指定分片數量,不能動態修改分片數量
- 多個分片主要是為了提高寫入效率

副本-replicas
- 就是分片的副本,副本分片的主要目的就是為了故障轉移
- 主分片的節點掛掉了,一個副本分片就會晉升為主分片的角色
- 在索引寫入時,新文件首先被索引進主分片然後再同步到其它所有的副本分片
- 副本分片可以服務於讀請求,可以通過增加副本的數目來提升查詢效能

廢話少說,先實戰吧

對了,一定要之前下載Elasticsearch,然後啟動,才可以使用哦

首先引入Elasticsearch的依賴包,有很多不同的jar依賴,我目前就選擇了jest客戶端和transport客戶端兩種方式

 <dependency>
         <groupId>io.searchbox</groupId>
         <artifactId>jest</artifactId>
         <version>5.3.2</version>
  </dependency>

  <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.5.3</version>
 </dependency>
配置檔案:
#jest連線方式需要的配置  配置elasticsearch
spring:
 elasticsearch:
  jest:
   uris:
   - http://127.0.0.1:9200
   read-timeout: 5000
#連線資料來源
 datasource:
  url: jdbc:oracle:thin:@192.168.0.86:1521:ORCL86
  username: exdb_guotu
  password: exdb_guotu
  driver-class-name: oracle.jdbc.driver.OracleDriver
  nitialSize: 5
  maxWait: 60000
  minIdle: 5
transport連線方式,配置,寫在程式碼裡,不用transport可以不用這段程式碼 
package com.config;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.stereotype.Component;

import java.net.InetAddress;

/**
 * Created by df on 2018/9/19.
 */
@Component
public class TransportClientUitl {
    private TransportClient transportClient;
    //transportClient的載入配置
    public TransportClient getClient() throws Exception{
        //設定叢集名稱
        Settings settings = Settings.builder().put("cluster.name", "df-elaticSearch").build();// 叢集名
        //叢集名稱就是我們Elasticsearch的名稱,我的是我自己起的
        //建立client
        transportClient  = new PreBuiltTransportClient(settings)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));

        return transportClient;
    }
}

先來個測試,添加個實體,實體裡邊放索引和型別,還有儲存的欄位

package com.entity;

/**
 * Created by df on 2018/9/17.
 */
public class entity {

    public static final String INDEX_NAME = "df_index";//相當於資料庫名稱

    public static final String TYPE = "df_tstype";//相當於表名

    private String key;
    private String Value;

    public entity(){
        super();
    }
    public entity(String key,String Value){
        this.key=key;
        this.Value=Value;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return Value;
    }

    public void setValue(String value) {
        Value = value;
    }
}

然後就可以對Elasticsearch做增刪改查了  

 

package com.service;

import com.config.TransportClientUitl;
import com.entity.entity;
import com.sqlSource.SqlHadle;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.core.*;

import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.mapping.GetMapping;

import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;


import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * Created by df on 2018/9/17.
 */
@Service
public class testServiceImpl implements testService {
    private static final Logger LOGGER = LoggerFactory.getLogger(testServiceImpl.class);
    @Autowired
    private JestClient jestClient;

    /**
     * 插入單個數據
     */
    @Override
    public void saveEntity(entity en) {
        Index index = new Index.Builder(en)
                .index(entity.INDEX_NAME).type(entity.TYPE).build();
        try {
            jestClient.execute(index);
            LOGGER.info("ES 插入完成");
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage());
        }
    }

    SqlHadle sqlHadle = new SqlHadle();

   /**
     * 批量插入多個
     * index  索引名(資料庫名)
     * type   型別(表)
     */
    @Override
    public void insertBatch(String index, String type) {
        //從資料庫查詢出來的資料 然後在儲存到Elasticsearch裡
        List<Map<String, Object>> entityList = sqlHadle.queryTest();
        long startTime = System.currentTimeMillis();
        boolean result = false;
        try {
            //Bulk方式
            Bulk.Builder bulk = new Bulk.Builder().defaultIndex(index).defaultType(type);
            for (Map map : entityList) {
                Index index1 = new Index.Builder(map).build();
                bulk.addAction(index1);
            }
            BulkResult br = jestClient.execute(bulk.build());
            result = br.isSucceeded();
        } catch (IOException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("批量新增:" + result + " 消耗了" + (endTime - startTime) + "ms");
    }

 /**
     * 查詢某一個欄位
     * searchContent 查詢的屬性值
     * key           欄位名稱
     */
    @Override
    public List<entity> searchEntity(String searchContent, String key) {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchQuery(key, searchContent));
        Search search = new Search.Builder(builder.toString()).addIndex(entity.INDEX_NAME)
                .addType(entity.TYPE).build();
        try {
            JestResult result = jestClient.execute(search);
            return result.getSourceAsObjectList(entity.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

   /**
     * 刪除某個id下的文件,想知道id可以去Elasticsearch的共工具裡看,也可以用程式獲取
     * id      文件id
     * index  索引名(資料庫名)
     * type   型別(表)
     */
    @Override
    public void deleteDoc(String id, String index, String type) {
        Delete delete = new Delete.Builder(id).index(index).type(type).build();
        JestResult result = null;
        try {
            result = jestClient.execute(delete);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  /**
     * 刪除某個索引
     * index  索引名(資料庫名)
     */
    @Override
    public void deleteIndex(String index) {
        DeleteIndex deleteIndex = new DeleteIndex.Builder(index).build();
        JestResult result = null;
        try {
            result = jestClient.execute(deleteIndex);
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  /**
     * 查詢所有  結果Elasticsearch預設只能查出10條,最多的size也只能10000條
     * index  索引名(資料庫名)
     * type   型別(表)
     */
    @Override
    public void searchAll(String index, String type) {
        GetMapping getMapping = new GetMapping.Builder().addIndex(index)
                .addType(type).build();
        try {
            JestResult jr = jestClient.execute(getMapping);
            System.out.println(jr.getJsonString());
        } catch (IOException e) {
            e.printStackTrace();
        }

//        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        sourceBuilder.query(QueryBuilders.matchAllQuery());
//        Search search = new Search.Builder(sourceBuilder.toString()).addIndex("df_index").build();
//        SearchResult result = null;
//        List his = null;
//        try {
//            result = jestClient.execute(search);
//            System.out.println("本次查詢共查到:" + result.getTotal() + "個關鍵字!");
//            his = result.getHits(Object.class);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return his;
    }

  /**
     * 全文搜尋  根據某個欄位全篇搜尋
     * indexName  索引名(資料庫名)
     * typeName  型別(表)
     * query     欄位
     * query資料是這種格式
     * query="{\n" +
     "  \"from\" : 0,\n" +
     "  \"size\" : 9999,\n" +
     "  \"query\" : {\n" +
     "    \"query_string\" : {\n" +
     "      \"query\" : \"惠州\"\n" +
     "    }\n" +
     "  }\n" +
     "}\n";
     */
    @Override
    public String search(String indexName, String typeName, String query) {
        long startTime = System.currentTimeMillis();
        Search search = new Search.Builder(query).addIndex(indexName).addType(typeName).build();
        JestResult jr = null;
        try {
            jr = jestClient.execute(search);
            long endTime = System.currentTimeMillis();
            System.out.println("Elasticsearch中查詢消耗:" + (endTime - startTime) + "ms");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jr.getJsonString();
    }


    @Autowired
    private TransportClientUitl clientUitl;

    /**
     * 滾動分頁,採用的是TransportClient客戶端方式操作
     * 這個滾動分頁其實是查詢索引裡的全部資料,這個效率最高,最快,每一次滾動設定查詢多少頁,
     * 然後下一次找到這一次記錄的id然後繼續滾動,直至資料都沒有為止
     * 採用條件:資料量大,
     * query    查詢需要的配置,如下query變數所示
     * indexName  索引名(資料庫名)
     * typeName  型別(表)
     */
   String  query= "\"{\" +\n"+
            "            \"    \\\"query\\\":{\" +\n"+
            "            \"        \\\"match\\\":{\\\"srv_content\\\":\\\"google\\\"}}\" +\n"+
            "            \"    }\" +\n"+
            "            \"}\"";
    public void searchScroll(String query, String indexName, String typeName) {
        long startTime = System.currentTimeMillis();
        try {
            SearchRequestBuilder searchBuilder = clientUitl.getClient().prepareSearch(indexName);
            searchBuilder.setTypes(typeName);
            //設定每批讀取的的資料量
            searchBuilder.setSize(10000);
            //預設是查詢所有
            searchBuilder.setQuery(QueryBuilders.queryStringQuery("*:*"));
            //設定 search context 維護1分鐘的有效期
            searchBuilder.setScroll(TimeValue.timeValueMillis(3));
            //獲得首次查詢結果
            SearchResponse searchResponse = searchBuilder.get();
            System.out.println("命中總數量:" + searchResponse.getHits().getTotalHits());
            //列印計數
            int count = 1;

            do {
                System.out.println("第" + count + "次列印資料:");
                for (SearchHit hit : searchResponse.getHits().getHits()) {
                   // System.out.println(hit.getSource());
                }
                count++;
                //將scorllId迴圈傳遞
                searchResponse=clientUitl.getClient().prepareSearchScroll(searchResponse.getScrollId())
                        .setScroll(TimeValue.timeValueMillis(1)).execute().actionGet();
                //當searchHits的陣列為空的時候結束迴圈,至此資料全部讀取完畢
            } while (searchResponse.getHits().getHits().length != 0);
            long endTime = System.currentTimeMillis();
            System.out.println("Elasticsearch中查詢所有的資料消耗:" + (endTime - startTime) + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



}

對Elasticsearch做基本的增刪改查就弄好了,附贈兩個Elasticsearch做完操作的圖片