ElasticSearch

ElasticSearch是什麼

Elaticsearch,簡稱爲es, es是一個開源的高擴展的分佈式全文檢索引擎,它能夠近乎實時的存儲、檢索數據;本
身擴展性很好,能夠擴展到上百臺服務器,處理PB級別的數據。es也使用Java開發並使用Lucene做爲其核心來實
現全部索引和搜索的功能,可是它的目的是經過簡單的RESTful API來隱藏Lucene的複雜性,從而讓全文搜索變得
簡單。

ElasticSearch和Solr

  • Solr 利用 Zookeeper 進行分佈式管理,而 Elasticsearch 自身帶有分佈式協調管理功能;
  • Solr 支持更多格式的數據,而 Elasticsearch 僅支持json文件格式;
  • Solr 官方提供的功能更多,而 Elasticsearch 自己更注重於核心功能,高級功能多有第三方插件提供;
  • Solr 在傳統的搜索應用中表現好於 Elasticsearch,但在處理實時搜索應用時效率明顯低於 Elasticsearch

ElasticSearch的安裝和啓動

下載es的安裝包java

ElasticSearch分爲LinuxWindow版本 ,ElasticSearch的官方地址: https://www.elastic.co/products/elasticsearch 
node

安裝Win版es服務git

Window版的ElasticSearch的安裝很簡單,相似Window版的Tomcat,解壓開即安裝完畢,解壓後的ElasticSearch
的目錄結構以下:
程序員

啓動ES服務github

點擊ElasticSearch下的bin目錄下的elasticsearch.bat啓動,控制檯顯示的日誌信息以下:
web

能夠看到綁定了兩個端口:

- 9300:集羣節點間通信接口,接收tcp協議
- 9200:客戶端訪問接口,接收Http協議

注意:算法

修改 config/jvm.options
    把內存改成1g

經過瀏覽器訪問ElasticSearch服務器,看到以下返回的json信息,表明服務啓動成功
數據庫

注意:ElasticSearch是使用java開發的,且本版本的es須要的jdk版本要是1.8以上,因此安裝ElasticSearch
以前保證JDK1.8+安裝完畢,並正確的配置好JDK環境變量,不然啓動ElasticSearch失敗。

安裝ES的圖形化界面插件apache

ElasticSearch不一樣於Solr自帶圖形化界面,咱們能夠經過安裝ElasticSearchhead插件,完成圖形化界面的效果,
完成索引數據的查看。安裝插件的方式有兩種,在線安裝和本地安裝。本文檔採用本地安裝方式進行head插件的
安裝。elasticsearch-5-*以上版本安裝head須要安裝nodegrunt
npm

1.安裝圖形化界面head插件

下載head插件:https://github.com/mobz/elasticsearch-head

解壓elasticsearch-head-master.zip到任意目錄,可是要和elasticsearch的安裝目錄區別開 

2.安裝nodejs程序

https://nodejs.org/en/download/

node-v8.9.4-x64.msi

3.安裝head依賴,grunt安裝爲全局命令 ,Grunt是基於Node.js的項目構建工具

cmd控制檯中輸入:  npm install -g grunt-cli
cmd進入到head目錄中執行 npm install

4.修改elasticsearch配置文件:elasticsearch.yml,增長如下兩句命令: 

http.cors.enabled: true        
http.cors.allow-origin: "*"   

此步爲容許elasticsearch跨越訪問

5.進入head目錄啓動head,在命令提示符下輸入命令:

grunt server  注:每次使用都要執行

爲了方便咱們能夠在head目錄下建立文本文檔輸入:grunt server 把它文件名及類型改成head.bat

6.打開瀏覽器,輸入 http://localhost:9100,看到以下頁面:

擴展:測試集羣

繼續安裝一個ES(解壓便可)-->修改內存-->容許跨域-->所有重啓-->測試

ElasticSearch概述

Elasticsearch是面向文檔(document oriented)的,這意味着它能夠存儲整個對象或文檔(document)。然而它不只
僅是存儲,還會索引(index)每一個文檔的內容使之能夠被搜索。在Elasticsearch中,你能夠對文檔(而非成行成列的
數據)進行索引、搜索、排序、過濾。ES
比傳統關係型數據庫,就像以下:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields

ElasticSearch核心概念

接近實時NRT

Elasticsearch是一個接近實時的搜索平臺。這意味着,從索引一個文檔直到這個文檔可以被搜索到有一個輕微的延
遲(一般是1秒之內)

集羣cluster

一個集羣就是由一個或多個節點組織在一塊兒,它們共同持有整個的數據,並一塊兒提供索引和搜索功能。一個集羣由
一個惟一的名字標識,這個名字默認就是「elasticsearch」。這個名字是重要的,由於一個節點只能經過指定某個集
羣的名字,來加入這個集羣

節點node

一個節點是集羣中的一個服務器,做爲集羣的一部分,它存儲數據,參與集羣的索引和搜索功能。和集羣相似,一
個節點也是由一個名字來標識的,默認狀況下,這個名字是一個隨機的漫威漫畫角色的名字,這個名字會在啓動的
時候賦予節點。這個名字對於管理工做來講挺重要的,由於在這個管理過程當中,你會去肯定網絡中的哪些服務器對
應於Elasticsearch集羣中的哪些節點。
一個節點能夠經過配置集羣名稱的方式來加入一個指定的集羣。默認狀況下,每一個節點都會被安排加入到一個叫
作「elasticsearch」的集羣中,這意味着,若是你在你的網絡中啓動了若干個節點,並假定它們可以相互發現彼此,
它們將會自動地造成並加入到一個叫作「elasticsearch」的集羣中。
在一個集羣裏,只要你想,能夠擁有任意多個節點。並且,若是當前你的網絡中沒有運行任何Elasticsearch節點,
這時啓動一個節點,會默認建立並加入一個叫作「elasticsearch」的集羣。

索引index

一個索引就是一個擁有幾分類似特徵的文檔的集合。好比說,你能夠有一個客戶數據的索引,另外一個產品目錄的索
引,還有一個訂單數據的索引。一個索引由一個名字來標識(必須所有是小寫字母的),而且當咱們要對對應於這
個索引中的文檔進行索引、搜索、更新和刪除的時候,都要使用到這個名字。在一個集羣中,能夠定義任意多的索
引

類型type

在一個索引中,你能夠定義一種或多種類型。一個類型是你的索引的一個邏輯上的分類/分區,其語義徹底由你來
定。一般,會爲具備一組共同字段的文檔定義一個類型。好比說,咱們假設你運營一個博客平臺而且將你全部的數
據存儲到一個索引中。在這個索引中,你能夠爲用戶數據定義一個類型,爲博客數據定義另外一個類型,固然,也可
覺得評論數據定義另外一個類型。

文檔document

一個文檔是一個可被索引的基礎信息單元。好比,你能夠擁有某一個客戶的文檔,某一個產品的一個文檔,固然,
也能夠擁有某個訂單的一個文檔。文檔以JSON(Javascript Object Notation)格式來表示,而JSON是一個處處存
在的互聯網數據交互格式。
在一個index/type裏面,你能夠存儲任意多的文檔。注意,儘管一個文檔,物理上存在於一個索引之中,文檔必須
被索引/賦予一個索引的type。

分片和複製 shards&replicas

一個索引能夠存儲超出單個結點硬件限制的大量數據。好比,一個具備10億文檔的索引佔據1TB的磁盤空間,而任
一節點都沒有這樣大的磁盤空間;或者單個節點處理搜索請求,響應太慢。爲了解決這個問題,Elasticsearch提供
了將索引劃分紅多份的能力,這些份就叫作分片。當你建立一個索引的時候,你能夠指定你想要的分片的數量。每
個分片自己也是一個功能完善而且獨立的「索引」,這個「索引」能夠被放置到集羣中的任何節點上。分片很重要,主
要有兩方面的緣由: 1)容許你水平分割/擴展你的內容容量。 2)容許你在分片(潛在地,位於多個節點上)之上
進行分佈式的、並行的操做,進而提升性能/吞吐量。
至於一個分片怎樣分佈,它的文檔怎樣聚合回搜索請求,是徹底由Elasticsearch管理的,對於做爲用戶的你來講,
這些都是透明的。
在一個網絡/雲的環境裏,失敗隨時均可能發生,在某個分片/節點不知怎麼的就處於離線狀態,或者因爲任何緣由
消失了,這種狀況下,有一個故障轉移機制是很是有用而且是強烈推薦的。爲此目的,Elasticsearch容許你建立分
片的一份或多份拷貝,這些拷貝叫作複製分片,或者直接叫複製。
複製之因此重要,有兩個主要緣由: 在分片
/節點失敗的狀況下,提供了高可用性。由於這個緣由,注意到複製分 片從不與原/主要(original/primary)分片置於同一節點上是很是重要的。擴展你的搜索量/吞吐量,由於搜索能夠 在全部的複製上並行運行。總之,每一個索引能夠被分紅多個分片。一個索引也能夠被複制0次(意思是沒有複製) 或屢次。一旦複製了,每一個索引就有了主分片(做爲複製源的原來的分片)和複製分片(主分片的拷貝)之別。分 片和複製的數量能夠在索引建立的時候指定。在索引建立以後,你能夠在任什麼時候候動態地改變複製的數量,可是你 過後不能改變分片的數量。 默認狀況下,Elasticsearch中的每一個索引被分片5個主分片和1個複製,這意味着,若是你的集羣中至少有兩個節 點,你的索引將會有5個主分片和另外5個複製分片(1個徹底拷貝),這樣的話每一個索引總共就有10個分片

映射mapping

mapping是處理數據的方式和規則方面作一些限制,如某個字段的數據類型、默認值、分析器、是否被索引等等,
這些都是映射裏面能夠設置的,其它就是處理es裏面數據的一些使用規則設置也叫作映射,按着最優規則處理數據
對性能提升很大,所以才須要創建映射,而且須要思考如何創建映射才能對性能更好?和創建表結構表關係數據庫
三範式相似。

ElasticSearch操做入門

建立maven工程

引入座標

<dependencies>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>5.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>5.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.24</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>

新建索引

@Test//建立索引庫及添加文檔
    public void test1() throws Exception {
        //1.建立客戶端訪問對象
        TransportClient client=new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //2.建立文檔
        //{id:"1",title:"xxx",content:"xxxxxx"}
        XContentBuilder builder= XContentFactory.jsonBuilder()
                .startObject()
                    .field("id","1")
                    .field("title","ElasticSearch是一個基於Lucene的搜索服務器")
                    .field("content","它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用Java開發的,並做爲Apache許可條款下的開放源碼發佈,是當前流行的企業級搜索引擎。")
                .endObject();
        //3.建立索引庫及添加文檔
        client.prepareIndex("blog1","article","1").setSource(builder).get();
        //4.關閉資源
        client.close();
    }

搜索文檔數據

查詢所有

 @Test//查詢所有
    public void test2() throws Exception {
        //建立客戶端訪問對象
        TransportClient client=new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //建立查詢對象
        SearchResponse searchResponse=client.prepareSearch("blog1").setTypes("article").setQuery(QueryBuilders.matchAllQuery())
                .get();
        //處理查詢結果
        SearchHits hits=searchResponse.getHits();//表示查詢到的文檔
        System.out.println("查詢到了"+hits.totalHits+"個文檔");

        //迭代器遍歷
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()){
            SearchHit hit = iterator.next();//拿到一個文檔對象
            System.out.println(hit.getSourceAsString());
            System.out.println(hit.getSource().get("title"));
        }
        //關閉資源
        client.close();
    }

字符串查詢

查詢所有的代碼不變,只需改正建立對象便可
//
二、建立查詢對象,並返回結果 SearchResponse searchResponse = client.prepareSearch("blog1").setTypes("article")
        .setQuery(QueryBuilders.queryStringQuery("提供")).get();

詞條查詢

查詢所有的代碼不變,只需改正建立對象便可
//二、建立查詢對象,並返回結果
        SearchResponse searchResponse = client.prepareSearch("blog1").setTypes("article")
        .setQuery(QueryBuilders.termQuery("title","搜索")).get();

模糊查詢

查詢所有的代碼不變,只需改正建立對象便可
//二、建立查詢對象,並返回結果
        SearchResponse searchResponse = client.prepareSearch("blog1").setTypes("article")
      .setQuery(QueryBuilders.wildcardQuery("title","*搜索*")).get();

ElasticSearch集成IK分詞器

ik分詞器的安裝

下載地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

解壓

將解壓後的文件夾拷貝到elasticsearch-5.6.8\plugins下,並重命名文件夾爲ik

從新啓動ElasticSearch,便可加載IK分詞器

ik分詞器測試

IK提供了兩個分詞算法ik_smart ik_max_word
其中 ik_smart 爲最少切分,ik_max_word爲最細粒度劃分
咱們分別來試一下
1)最小切分:在瀏覽器地址欄輸入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序員
輸出的結果爲:

2)最細切分:在瀏覽器地址欄輸入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_max_word&pretty=true&text=我是程序員
輸出的結果爲

ElasticSearch經常使用編程操做

索引相關操做

建立索引

 @Test
    //建立索引庫
    public void test1() throws Exception {
        //一、建立客戶端訪問對象
        TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
        //二、建立索引  index
        //client.admin().indices().prepareCreate("blog2").get();
        client.admin().indices().prepareDelete("blog2").get();
        //三、關閉資源
        client.close();

    }

刪除索引

映射相關操做

建立映射

@Test
//建立映射
public void test2() throws Exception {
//1.建立客戶端訪問對象
TransportClient client=new PreBuiltTransportClient(Settings.EMPTY)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
//建立索引
client.admin().indices().prepareCreate("blog2").get();
/*** 格式:
* "mappings" : {
"article" : {
"properties" : {
"id" : { "type" : "string" },
"content" : { "type" : "string" },
"title" : { "type" : "string" }
}
}
}
*/
//建立映射
XContentBuilder builder= XContentFactory.jsonBuilder()
.startObject()
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type","long")
.field("store","long")
.endObject()
.startObject("title")
.field("type","string")
.field("store","true")
.field("analyzer","ik_smart")
.endObject()
.startObject("content")
.field("type","string")
.field("store","true")
.field("analyzer","ik_smart")
.endObject()

.endObject()
.endObject()
.endObject();
//創建與索引庫和type的關係
PutMappingRequest mappingRequest= Requests.putMappingRequest("blog2").type("article").source(builder);

//執行
client.admin().indices().putMapping(mappingRequest).get();
//關閉資源
client.close();
}

創建文檔(經過XContentBuilder

    @Test
    //建立索引庫及添加文檔
    public void test3() throws Exception {
        //一、建立客戶端訪問對象
        TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //二、建立文檔對象
        //{id:"1",title:"xxx",content:"xxxxxx"}
        XContentBuilder builder = XContentFactory.jsonBuilder()
                .startObject()
                .field("id","1")
                .field("title","ElasticSearch是一個基於Lucene的搜索服務器。")
                .field("content","它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用Java開發的,並做爲Apache許可條款下的開放源碼發佈,是當前流行的企業級搜索引擎。設計用於雲計算中,可以達到實時搜索,穩定,可靠,快速,安裝使用方便。官方客戶端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和許多其餘語言中都是可用的。根據DB-Engines的排名顯示,Elasticsearch是最受歡迎的企業搜索引擎,其次是Apache Solr,也是基於Lucene。")
                .endObject();
        //三、建立索引庫及添加文檔
        client.prepareIndex("blog2", "article", "1").setSource(builder).get();
        //四、關閉資源
        client.close();
    }

創建文檔(使用Jackson轉換實體)

建立Article實體

public class Article {
private Integer id;
private String title;
private String content;
getter/setter...
}

引入jackson座標

<
dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.1</version>
</dependency>

代碼實現

@Test
    //建立文檔(經過實體轉json)
    public void test5() throws Exception {
        //一、建立客戶端訪問對象
        TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //二、建立文檔對象
        Article a = new Article();
        a.setId(2L);
        a.setTitle("搜索工做其實很快樂");
        a.setContent("咱們但願咱們的搜索解決方案要快,咱們但願有一個零配置和一個徹底免費的搜索模\n" +
                "式,咱們但願可以簡單地使用JSON經過HTTP的索引數據,咱們但願咱們的搜索服務器始終可用,咱們但願可以一臺開\n" +
                "始並擴展到數百,咱們要實時搜索,咱們要簡單的多租戶,咱們但願創建一個雲的解決方案。Elasticsearch旨在解\n" +
                "決全部這些問題和更多的問題。");
        //3.建立一個json轉換器
        ObjectMapper mapper =new ObjectMapper();

        //四、建立索引庫及添加文檔                                                        {id:xxx, title:xxx, content:xxx}
        client.prepareIndex("blog2", "article", a.getId()+"").setSource(mapper.writeValueAsString(a)).get();
        //五、關閉資源
        client.close();
    }

更改文檔

在建立文檔代碼基礎上更改步驟4
     //方式一
        client.prepareUpdate("blog2", "article", a.getId()+"").setDoc(mapper.writeValueAsString(a)).get();
        //方式二
        client.update(new UpdateRequest("blog2","article",a.getId()+"").doc(mapper.writeValueAsString(a))).get();
        

刪除文檔

在建立文檔代碼基礎上更改步驟4
方式一
client.prepareDelete("blog2", "article", a.getId()+"").get();
方式二
client.delete(new DeleteRequest("blog1","article",a.getId()+"")).get();

批量插入數據

刪除blog2而後建立索引和映射

 @Test
    //批量建立文檔
    public void test8() throws Exception {
        //一、建立客戶端訪問對象
        TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //3.建立一個json轉換器
        ObjectMapper mapper =new ObjectMapper();
        for (int i = 1; i <= 100; i++) {
            //二、建立文檔對象
            Article a = new Article();
            a.setId(i);
            a.setTitle(i+"搜索工做其實很快樂");
            a.setContent(i+"咱們但願咱們的搜索解決方案要快,咱們但願有一個零配置和一個徹底免費的搜索模\n" +
                    "式,咱們但願可以簡單地使用JSON經過HTTP的索引數據,咱們但願咱們的搜索服務器始終可用,咱們但願可以一臺開\n" +
                    "始並擴展到數百,咱們要實時搜索,咱們要簡單的多租戶,咱們但願創建一個雲的解決方案。Elasticsearch旨在解\n" +
                    "決全部這些問題和更多的問題。");


            //四、建立索引庫及添加文檔                                                        {id:xxx, title:xxx, content:xxx}
            client.prepareIndex("blog2", "article", a.getId()+"").setSource(mapper.writeValueAsString(a)).get();
        }
        //五、關閉資源
        client.close();
    }

分頁和排序

  @Test
    //分頁和排序
    public void test9() throws Exception {
        //建立客戶端訪問對象
        TransportClient client=new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //建立查詢對象
        SearchRequestBuilder requestBuilder = client.prepareSearch("blog2").setTypes("article")
                .setQuery(QueryBuilders.matchAllQuery());
        //分頁:參數1:從哪開始查,參數2:每頁查多少
        requestBuilder.setFrom(0).setSize(20);

        //排序
        requestBuilder.addSort("id", SortOrder.ASC);
        //執行查詢並返回結果
        SearchResponse searchResponse = requestBuilder.get();
        SearchHits hits = searchResponse.getHits();
        System.out.println("共查詢到了"+hits.getTotalHits()+"條");

        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()){
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString());
        }

    }

高亮顯示

    @Test
    //高亮顯示
    public void test10() throws Exception {
        //建立客戶端訪問對象
        TransportClient client=new PreBuiltTransportClient(Settings.EMPTY)
                .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
        //建立查詢對象
        SearchRequestBuilder requestBuilder = client.prepareSearch("blog2").setTypes("article")
                .setQuery(QueryBuilders.termQuery("title","搜索"));
     //高亮顯示
        HighlightBuilder builder=new HighlightBuilder();
        builder.preTags("<font color='red'>");
        builder.field("title");
        builder.postTags("</font>");
        requestBuilder.highlighter(builder);
        //分頁:參數1:從哪開始查,參數2:每頁查多少
        requestBuilder.setFrom(0).setSize(20);

        //排序
        requestBuilder.addSort("id", SortOrder.ASC);
        //執行查詢並返回結果
        SearchResponse searchResponse = requestBuilder.get();
        SearchHits hits = searchResponse.getHits();
        System.out.println("共查詢到了"+hits.getTotalHits()+"條");

        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()){
            SearchHit searchHit = iterator.next();
            //System.out.println(searchHit.getSourceAsString());
            HighlightField field = searchHit.getHighlightFields().get("title");
            System.out.println(field.fragments()[0]);
        }

    }
相關文章
相關標籤/搜索