Elasticsearch.net項目實戰

elasticsearch.net項目實戰


目錄

  • Elasticsearch+kibana
    • 環境搭建
      • windows 10環境配置
      • 安裝Elasticsearch
      • head安裝(非必需)
      • 安裝kibana
    • 基本概念
      • Index
      • Type
      • Document
    • DSL的基本使用
      • 增長
      • 修改
      • 查詢
      • 刪除
  • Elasticsearch .Net
    • Low level client基本使用
    • 項目實戰
  • 總結
  • 參考

     Elasticsearch是一個基於Apache Lucene(TM)的開源搜索引擎。不管在開源仍是專有領域,Lucene能夠被認爲是迄今爲止最
先進、性能最好的、功能最全的搜索引擎庫。html

     一說到全文搜索,lucene久負盛名。早年間,由於項目須要,接觸過一個叫盤古分詞的開源項目,藉助其中的分詞實現了分詞搜索的功能。而盤古分詞就是lucence的.NET版本。聽說這個開源項目已經恢復更新並支持. NET Core,有興趣的童鞋能夠去圍觀一下(https://github.com/LonghronShen/Lucene.Net.Analysis.PanGu/tree/netcore2.0)。java

      我想不少童鞋都聽過ELK,ELK是Elasticsearch、Logstash、Kibana。正好公司運維同事引入了這樣一套體系,用於創建集中式日誌收集系統,將全部節點上的日誌統一收集,管理,訪問。雖然可以從必定程度上解決基本的問題,可是原生的kibana界面和查詢方式都不夠友好,很難推向廣大的開發人員。因而我在想,咱們是否能夠利用這個開源的庫集成到運維自動化平臺當中,讓這把利劍發揮出更大的價值。node

1、環境搭建
 mysql

本文是基於windows 10操做系統的es環境的搭建。git

  1. java環境安裝

     因爲es是java語言開發的,因此這裏要安裝java環境。github

     jdk下載:sql

https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html數據庫

     安裝完成以後就是配置環境變量:
npm



     查看是否安裝成功:
json



2.安裝Elasticsearch

    Elasticsearch版本已經比較多,初學者可能比較懵。特別是在安裝head和Kibana的時候,若是版本不匹配,每每會致使沒法使用。這裏使用的是elasticsearch-5.6.11版本。

     elasticsearch-5.6.11下載:

https://www.elastic.co/downloads/past-releases/elasticsearch-5-6-11

解壓到C:\ELk 備用。

3.head安裝(非必需)

    es 4.x 版本安裝head很簡單,只需下載head插件解壓到指定目錄便可。es 5.x+須要藉助node安裝。

     head下載:

https://github.com/mobz/elasticsearch-head

解壓到C:\ELk\elasticsearch-5.6.11



     node下載:

https://nodejs.org/dist/v8.12.0/node-v8.12.0-win-x64.zip

安裝node



檢查node和npm是否安裝成功



path環境變量末尾 會自動增長 C:\Program Files\nodejs
安裝 phantomjs
官網:http://phantomjs.org/下載【配置環境變量

安裝grunt
npm install -g grunt-cli



執行C:\ELk\elasticsearch-5.6.11\bin\elasticsearch.bat

執行命令啓動 head



瀏覽器訪問:http://localhost:9100/



4.安裝kibana

    致使爲止,其實elasticsearch自身已經安裝完成。經過Head就能很方便的操做es,可是kibana集成了head相似功能,並提供了更加友好的訪問界面。

     kibana-5.6.9-windows-x86下載:

https://www.elastic.co/downloads/past-releases/kibana-5-6-9

下載以後,解壓到C:\ELk\kibana-5.6.9-windows-x86

執行C:\ELk\kibana-5.6.9-windows-x86\bin\kibana.bat

瀏覽器訪問:http://localhost:5601




2、基本概念

  • Cluster(集羣)

         集羣是一個或多個節點(服務器)的集合,這些節點一塊兒保存整個數據,並在全部節點上提供聯合索引和搜索功能。

         一個運行中的 Elasticsearch 實例稱爲一個 節點,而集羣是由一個或者多個擁有相同 cluster.name 配置的節點組成, 它們共同承擔數據和負載的壓力。當有節點加入集羣中或者從集羣中移除節點時,集羣將會從新平均分佈全部的數據。

          做爲用戶,咱們能夠將請求發送到 集羣中的任何節點 ,包括主節點。 每一個節點都知道任意文檔所處的位置,而且可以將咱們的請求直接轉發到存儲咱們所需文檔的節點。 不管咱們將請求發送到哪一個節點,它都能負責從各個包含咱們所需文檔的節點收集回數據,並將最終結果返回給客戶端。 Elasticsearch 對這一切的管理都是透明的。

  • Node(節點)

    節點是集羣的一部分、存儲數據並參與集羣的索引和搜索功能的單個服務器。

  • Index

    索引是具備類似特性的文檔集合。

    • 相似於關係型數據庫中""的概念
  • Type

    Type是具備一組公共字段的文檔定義類型

         例如,假設您運行一個博客平臺並將全部數據存儲在一個索引中。在該索引中,能夠定義用戶數據的類型、博客數據的另外一種類型以及註釋數據的另外一種類型。

    • 相似於關係型數據庫中""的概念
  • Document

    被索引信息的基本單元。

    • 相似於關係型數據庫的一個記錄(行)
    • 會被壓縮成json格式
  • Shards & Replicas(分片&副本分片)

    索引能夠潛在地存儲能夠超過單個節點的硬件限制的大量數據。例如,佔用1TB磁盤空間的十億個文檔的單個索引可能不適合單個節點的磁盤,或者可能太慢而沒法單獨爲來自單個節點的搜索請求提供服務。

         分片的兩個主要緣由:
    • 它容許您水平分割/縮放您的內容卷。
    • 它容許你分配和並行操做的碎片(可能在多個節點上)從而提升性能/吞吐量

    在網絡/雲環境中,在任什麼時候候均可以預期到故障,在碎片/節點不知何故脫機或因爲任何緣由消失的狀況下,很是有用,而且強烈建議使用故障轉移機制。爲此,Elasticsearch容許您將一個或多個索引碎片的副本複製到稱爲副本碎片(replica shards)或簡稱爲副本(replica)中。

         複製是重要的兩個主要緣由:
    • 在碎片/節點失敗的狀況下,它提供了高可用性。因爲這個緣由,須要注意的是,副本碎片永遠不會分配到與原始/主碎片相同的節點上。
    • 它容許您擴展搜索量/吞吐量,由於能夠並行地在全部副本上執行搜索。

         添加故障轉移

    當集羣中只有一個節點在運行時,意味着會有一個單點故障問題——沒有冗餘。 幸運的是,咱們只需再啓動一個節點便可防止數據丟失。

         擁有兩個節點的集羣——全部主分片和副本分片都已被分配。



3、DSL的基本使用

elasticsearch也像mysql同樣提供了專門的語法來操做數據。Elasticsearch provides a full Query DSL (Domain Specific Language) based on JSON to define queries.

  • 建立文檔
PUT people/person/1?op_type=create
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
  • 修改
POST /user/guest/20/_update
{
    "doc": {
      "RealName":"LukyHuu20"
    }
}
  • 查詢
GET /user/guest/_search
{
    "query": {
      "match": {
        "Id":22
      }
    }
}
  • 刪除
DELETE /user/guest/15
{
  
}

4、Elasticsearch .Net

     elasticsearch是以restfulAPI方式對外提供接口,並提供客戶端給多種語言使用。Elasticsearch uses standard RESTful APIs and JSON. We also build and maintain clients in many languages such as Java, Python, .NET, SQL, and PHP. Plus, our community has contributed many more. They’re easy to work with, feel natural to use, and, just like Elasticsearch, don't limit what you might want to do with them.
參考(https://www.elastic.co/products/elasticsearch)

1.Low level client基本使用

     本文是介紹ES的.NET客戶端,Elasticsearch .Net - Low level client[5.x]

經過引入對應的版本的客戶端,即可經過C#操做ES。參考(https://www.elastic.co/guide/en/elasticsearch/client/net-api/5.x/elasticsearch-net.html)

鏈接

var settings = new ConnectionConfiguration(new Uri("http://example.com:9200"))
    .RequestTimeout(TimeSpan.FromMinutes(2));

var lowlevelClient = new ElasticLowLevelClient(settings);

插入文檔

var indexResponse = lowlevelClient.Index<byte[]>("user", "guest", user.Id.ToString(), user);
 byte[] responseBytes = indexResponse.Body;

更新文檔

var searchResponse = lowlevelClient.Update<string>("user", "guest", id.ToString(), new
                {
                    doc = new
                    {
                        RealName = realname,
                        Description = description
                    }
                });

bool successful = searchResponse.Success;

查詢

var searchResponse = lowlevelClient.Search<string>("user", "guest", new
                {
                    query = new
                    {
                        match = new
                        {
                            Id = id
                        }
                    }
                });

bool successful = searchResponse.Success;

刪除

var searchResponse = lowlevelClient.Delete<string>("user", "guest", id.ToString());

                bool successful = searchResponse.Success;

2.項目實戰

     前面大體介紹了ES的安裝和基本使用。那麼,如何在項目中落地呢?

使用nuget安裝Elasticsearch.Net 5.6.4

Install-Package Elasticsearch.Net -Version 5.6.4

安裝完後,



基本的增刪該查在項目中的實現上面已經有所介紹,這裏重點講一下查詢:

筆者使用的.NET MVC5 Web框架,對於返回的結果筆者作了一個簡單封裝:

public class ESearchRoot<T>
    {
        /// <summary>
        /// 
        /// </summary>
        public int took { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string timed_out { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public _shards _shards { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public Hits<T> hits { get; set; }
    }

    public class _shards
    {
        /// <summary>
        /// 
        /// </summary>
        public int total { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int successful { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int skipped { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int failed { get; set; }
    }

    public class HitsItem<T>
    {
        /// <summary>
        /// 
        /// </summary>
        public string _index { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string _type { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string _id { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string _score { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public T _source { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<int> sort { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public Highlight highlight { get; set; }
    }

    public class Hits<T>
    {
        /// <summary>
        /// 
        /// </summary>
        public int total { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string max_score { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<HitsItem<T>> hits { get; set; }
    }

    public class Highlight
    {
        /// <summary>
        /// 
        /// </summary>
        public List<string> Description { get; set; }
    }

由於soure返回的對象是不定的,因此使用了泛型。
本項目soure對應的類,user:

///<summary>
    /// 
    /// </summary>
    public class User
    {
        /// <summary>
        /// 
        /// </summary>
        public string Account { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Phone { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Email { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string RealName { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string CanReview { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string CanExcute { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Avatar { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string IsUse { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public DateTime CreateTime { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public DateTime ModifyTime { get; set; }
    }

項目使用了帶條件的分頁查詢:

public List<AdminUser> GetBySomeWhere(string keyword, int limit, int pageSize, out int total)
        {
            List<AdminUser> users = new List<AdminUser>();

            total = 0;
            try
            {
                var settings = new ConnectionConfiguration(new Uri("http://localhost:9200/"))
   .RequestTimeout(TimeSpan.FromMinutes(2));

                var lowlevelClient = new ElasticLowLevelClient(settings);

                //根據不一樣的參數 來構建不一樣的查詢條件
                var request = new object();
                if (!String.IsNullOrEmpty(keyword))
                {
                    request = new
                    {
                        from = limit,
                        size = pageSize,
                        query = new
                        {
                            match = new
                            {
                                Description = keyword
                            }
                        },
                        highlight = new
                        {
                            fields = new
                            {
                                Description = new { }
                            }
                        },
                        sort = new
                        {
                            Id = new
                            {
                                order = "desc"
                            }
                        }
                    };
                }
                else
                {
                    request = new
                    {
                        from = limit,
                        size = pageSize,
                        query = new
                        {
                            match_all = new
                            {

                            }
                        },
                        highlight = new
                        {
                            fields = new
                            {
                                Description = new { }
                            }
                        },
                        sort = new
                        {
                            Id = new
                            {
                                order = "desc"
                            }
                        }
                    };
                }


                var searchResponse = lowlevelClient.Search<string>("user", "guest", request);

                bool successful = searchResponse.Success;
                var responseJson = searchResponse.Body;

                if (!successful)
                {
                    return users;
                }

                ESearchRoot<User> root = JsonHelper.JSONStringObject<ESearchRoot<User>>(responseJson);
                if (root != null)
                {
                    total = root.hits.total;
                    foreach (HitsItem<User> item in root.hits.hits)
                    {
                        if (item._source != null)
                        {
                            string highlightDescription = String.Empty;
                            StringBuilder sbDs = new StringBuilder();
                            if (item.highlight != null && item.highlight.Description.Count > 0)
                            {
                                //ighlightDescription = item.highlight.Description[0];
                                foreach (var d in item.highlight.Description)
                                {
                                    sbDs.Append(d);
                                }
                                highlightDescription = sbDs.ToString();
                            }

                            AdminUser user = new AdminUser
                            {
                                Id = item._source.Id,
                                RealName = item._source.RealName,
                                Account = item._source.Account,
                                Email = item._source.Email,
                                Phone = item._source.Phone,
                                //IsUse=item._source.IsUse,
                                Avatar = item._source.Avatar,
                                Description = item._source.Description,
                                HighlightDescription = highlightDescription,
                                CreateTime = item._source.CreateTime,
                                ModifyTime = item._source.ModifyTime
                            };
                            users.Add(user);
                        }
                    }
                }

                return users;
            }
            catch (ElasticsearchClientException ex)
            {
                //Log4Helper.Error
            }
            return users;
        }

項目最終的效果以下:

5、總結

     elasticsearch是很強大的開源工具,在實現全文搜索上有其獨到之處,也是大數據的分析方面利器,值得你們深刻去研究和實踐。

6、參考

相關文章
相關標籤/搜索