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、参考

相关文章
相关标签/搜索