ElasticSearch.net第五步-c#操作ElasticSearch详解

来源:互联网 发布:读卡器输出格式软件 编辑:程序博客网 时间:2024/05/21 17:07

前面我们讲解了关于ElasticSearch的安装配置,以及CRUD

本章我将讲解怎么使用c#操作ElasticSearch。

首先你需要一定的技术储备,比如:asp.net webapi,mvc,jsonp,knockout。这些知识在这里不再讲解,请自行Google。

项目DEMO介绍

搜索和索引功能我是以服务(webapi项目)方式提供的,在客户端(mvc项目)中的view视图中,直接使用ajax(jsonp格式)方式调用webapi,然后使用knockout绑定到table上的。

项目结构如图:

 

引入驱动

工欲善其事必先利其器,首先我们需要在Supernova.Webapi层中引入操作ElasticSearch的驱动dll PlainElastic.Net

如图:

 

封装操作ElasticSearch的ElasticSearchHelper

 View Code

demo中涉及的实体对象模型

 View Code

 

详细介绍ElasticSearchHelper里面的方法

1.索引文档(注意:索引文档之前先用配置filed对应的ik分词):

复制代码
 public IndexResult Index(string indexName, string indexType, string id, string jsonDocument)        {                        var serializer = new JsonNetSerializer();            string cmd = new IndexCommand(indexName, indexType, id);            OperationResult result = Client.Put(cmd, jsonDocument);            var indexResult = serializer.ToIndexResult(result.Result);            return indexResult;        }        public IndexResult Index(string indexName, string indexType, string id, object document)        {            var serializer = new JsonNetSerializer();            var jsonDocument = serializer.Serialize(document);            return Index(indexName, indexType, id, jsonDocument);        }
复制代码

2.对单个字段的全文检索,字段intro 包含词组key中的任意一个单词。例如:词组(中国美好),只要每条数据的intro字段包含"中国"或者"美好"就返回。

复制代码
public personList Search<person>(string indexName, string indexType, string key,int from ,int size)        {            string cmd = new SearchCommand(indexName, indexType);            string query = new QueryBuilder<person>()                //1 查询                .Query(b =>                            b.Bool(m =>                                //并且关系                                m.Must(t =>                                    //分词的最小单位或关系查询                                   t.QueryString(t1=>t1.DefaultField("intro").Query(key))                                   //.QueryString(t1 => t1.DefaultField("name").Query(key))                                   // t .Terms(t2=>t2.Field("intro").Values("研究","方鸿渐"))                                   //范围查询                                    // .Range(r =>  r.Field("age").From("100").To("200") )                                       )                                  )                                )                  //分页                 .From(from)                 .Size(size)                 //排序                // .Sort(c => c.Field("age", SortDirection.desc))                  //添加高亮                  .Highlight(h => h                      .PreTags("<b>")                      .PostTags("</b>")                      .Fields(                             f => f.FieldName("intro").Order(HighlightOrder.score),                             f => f.FieldName("_all")                             )                     )                    .Build();            string result = Client.Post(cmd, query);            var serializer = new JsonNetSerializer();            var list = serializer.ToSearchResult<Supernova.Webapi.DbHelper.person>(result);            personList datalist = new personList();            datalist.hits = list.hits.total;            datalist.took = list.took;             var personList= list.hits.hits.Select(c => new Supernova.Webapi.DbHelper.person() {                 id=c._source.id,                  age=c._source.age,                 birthday =c._source.birthday,                  intro=string.Join("",c.highlight["intro"]), //高亮显示的内容,一条记录中出现了几次                 name=c._source.name,                 sex=c._source.sex,                              });             datalist.list.AddRange(personList);             return  datalist;        }
复制代码

 

3.字段intro 或者name 包含词组key中的所有单词。例如:词组(中国美好),只要每条数据的intro或者name字段包含"中国"并且包含"美好"就返回。

复制代码
   public personList SearchFullFileds<person>(string indexName, string indexType, string key, int from, int size)        {            MustQuery<person> mustNameQueryKeys = new MustQuery<person>();            MustQuery<person> mustIntroQueryKeys = new MustQuery<person>();            var arrKeys = GetIKTokenFromStr(key);            foreach (var item in arrKeys)            {                mustNameQueryKeys = mustNameQueryKeys.Term(t3 => t3.Field("name").Value(item)) as MustQuery<person>;                mustIntroQueryKeys = mustIntroQueryKeys.Term(t3 => t3.Field("intro").Value(item)) as MustQuery<person>;            }                        string cmd = new SearchCommand(indexName, indexType);            string query = new QueryBuilder<person>()                //1 查询                .Query(b =>                            b.Bool(m =>                                m.Should(t =>                                           t.Bool(m1 =>                                                     m1.Must(                                                             t2 =>                                                                 //t2.Term(t3=>t3.Field("name").Value("研究"))                                                                 //   .Term(t3=>t3.Field("name").Value("方鸿渐"))                                                                   mustNameQueryKeys                                                             )                                                )                                       )                               .Should(t =>                                         t.Bool(m1 =>                                                     m1.Must(t2 =>                                                                      //t2.Term(t3 => t3.Field("intro").Value("研究"))                                                                     //.Term(t3 => t3.Field("intro").Value("方鸿渐"))                                                                       mustIntroQueryKeys                                                            )                                                )                                      )                                  )                        )                //分页                 .From(from)                 .Size(size)                //排序                // .Sort(c => c.Field("age", SortDirection.desc))                //添加高亮                  .Highlight(h => h                      .PreTags("<b>")                      .PostTags("</b>")                      .Fields(                             f => f.FieldName("intro").Order(HighlightOrder.score),                              f => f.FieldName("name").Order(HighlightOrder.score)                             )                     )                    .Build();            string result = Client.Post(cmd, query);            var serializer = new JsonNetSerializer();            var list = serializer.ToSearchResult<Supernova.Webapi.DbHelper.person>(result);            personList datalist = new personList();            datalist.hits = list.hits.total;            datalist.took = list.took;            var personList = list.hits.hits.Select(c => new Supernova.Webapi.DbHelper.person()            {                id = c._source.id,                age = c._source.age,                birthday = c._source.birthday,                intro = c.highlight==null||!c.highlight.Keys.Contains("intro") ? c._source.intro : string.Join("", c.highlight["intro"]), //高亮显示的内容,一条记录中出现了几次                name = c.highlight==null||!c.highlight.Keys.Contains("name") ? c._source.name : string.Join("", c.highlight["name"]),                sex = c._source.sex            });            datalist.list.AddRange(personList);            return datalist;        }
复制代码

 

3.搜索age在1到500之间,并且字段intro 或者name 包含词组key中的所有单词。

复制代码
 public personList SearchFullFiledss<person>(string indexName, string indexType, string key, int from, int size)        {            MustQuery<person> mustNameQueryKeys = new MustQuery<person>();            MustQuery<person> mustIntroQueryKeys = new MustQuery<person>();            var arrKeys = GetIKTokenFromStr(key);            foreach (var item in arrKeys)            {                mustNameQueryKeys = mustNameQueryKeys.Term(t3 => t3.Field("name").Value(item)) as MustQuery<person>;                mustIntroQueryKeys = mustIntroQueryKeys.Term(t3 => t3.Field("intro").Value(item)) as MustQuery<person>;            }            string cmd = new SearchCommand(indexName, indexType);            string query = new QueryBuilder<person>()                //1 查询                .Query(b =>                            b.Bool(m =>                                m.Must(t =>                                          t.Range(r => r.Field("age").From("1").To("500"))                                          .Bool(ms =>                                                    ms.Should(ts =>                                                               ts.Bool(m1 =>                                                                         m1.Must(                                                                                 t2 =>                                                                                     //t2.Term(t3=>t3.Field("name").Value("研究"))                                                                                     //   .Term(t3=>t3.Field("name").Value("方鸿渐"))                                                                                       //                                                                                      mustNameQueryKeys                                                                                 )                                                                    )                                                           )                                                   .Should(ts =>                                                             ts.Bool(m1 =>                                                                         m1.Must(t2 =>                                                                                          //t2.Term(t3 => t3.Field("intro").Value("研究"))                                                                                         //.Term(t3 => t3.Field("intro").Value("方鸿渐"))                                                                                                                                                                                   //                                                                                        mustIntroQueryKeys                                                                                )                                                                    )                                                          )                                                      )                                                        )                                                            )                       )                //分页                 .From(from)                 .Size(size)                //排序                // .Sort(c => c.Field("age", SortDirection.desc))                //添加高亮                  .Highlight(h => h                      .PreTags("<b>")                      .PostTags("</b>")                      .Fields(                             f => f.FieldName("intro").Order(HighlightOrder.score),                              f => f.FieldName("name").Order(HighlightOrder.score)                             )                     )                    .Build();            string result = Client.Post(cmd, query);            var serializer = new JsonNetSerializer();            var list = serializer.ToSearchResult<Supernova.Webapi.DbHelper.person>(result);            personList datalist = new personList();            datalist.hits = list.hits.total;            datalist.took = list.took;            var personList = list.hits.hits.Select(c => new Supernova.Webapi.DbHelper.person()            {                id = c._source.id,                age = c._source.age,                birthday = c._source.birthday,                intro = c.highlight==null||!c.highlight.Keys.Contains("intro") ? c._source.intro : string.Join("", c.highlight["intro"]), //高亮显示的内容,一条记录中出现了几次                name = c.highlight==null||!c.highlight.Keys.Contains("name") ? c._source.name : string.Join("", c.highlight["name"]),                sex = c._source.sex            });            datalist.list.AddRange(personList);            return datalist;        }
复制代码

需要用到的方法:将语句用ik分词,返回分词结果的集合

复制代码
 private List<string> GetIKTokenFromStr(string key)        {            string s = "/db_test/_analyze?analyzer=ik";            var result = Client.Post(s, "{"+key+"}");            var serializer = new JsonNetSerializer();            var list = serializer.Deserialize(result, typeof(ik)) as ik;                        return list.tokens.Select(c=>c.token).ToList();        }
复制代码

 

ASP.NET WebApi 调用ElasticSearchHelper

1.首先我们添加一个基类ApiController

复制代码
 public class BaseApiController : ApiController    {        public MongoDatabase db;        public MongoCollection col = null;//用于直接返回查询的json               public BaseApiController() { }        public BaseApiController(string collectionName)        {            db = DbHelper.MongodbHelper.Instance.DB;            col = db.GetCollection(collectionName);        }        public string GetStringRequest(string paramter)        {            return HttpContext.Current.Request.QueryString[paramter] ?? "";        }        public int? GetIntRequest(string paramter)        {            string tmp = HttpContext.Current.Request.QueryString[paramter] ?? "";            int tag = 0;            if (!int.TryParse(tmp, out tag))            {                return null;            }            return tag;        }    }
复制代码

 

2.操作ElasticSearch的apicontroller如下:

 View Code

 

3.索引数据的api如下:

复制代码
/// <summary>         /// 索引数据        /// </summary>        /// <returns></returns>         [Route("estest/index")]         [HttpGet]         public object index()         {                          int length = S.test.Length;             Random rd = new Random();             Random rdName = new Random();             ParallelOptions _po = new ParallelOptions();             _po.MaxDegreeOfParallelism = 4;             Parallel.For(0, 10000000, _po, c =>             {                 var start = rd.Next(0, S.test.Length - 700);                 var startName = rd.Next(0, S.test.Length - 30);                 person p = new person() { age = DateTime.Now.Millisecond, birthday = DateTime.Now, id = Guid.NewGuid().ToString(), intro = S.test.Substring(start, 629) + c, name = S.test.Substring(startName, 29) + c, sex = true };                 ElasticSearchHelper.Intance.Index("db_test", "person", Guid.NewGuid().ToString(), p);             });             return 1;         }
复制代码

索引使用的测试数据如下:

 View Code

 

4.搜索api如下:

复制代码
[Route("estest")]        [HttpGet]        public object Search()        {             //1 搜索数据            string key = GetStringRequest("Key");            int? from = GetIntRequest("from");            int? size = GetIntRequest("size");                         return ElasticSearchHelper.Intance.Search<person>("db_test", "person", key ?? "方鸿渐", from == null ? 0 : from.Value, size == null ? 20 : size.Value);                                  }         [Route("estest/SearchFullFileds")]         [HttpGet]         public object SearchFullFileds()         {             //1 搜索数据             string key = GetStringRequest("Key");             int? from = GetIntRequest("from");             int? size = GetIntRequest("size");                          return ElasticSearchHelper.Intance.SearchFullFileds<person>("db_test", "person", key ?? "方鸿渐", from == null ? 0 : from.Value, size == null ? 20 : size.Value);         }         [Route("estest/SearchFullFiledss")]         [HttpGet]         public object SearchFullFiledss()         {             //1 搜索数据             string key = GetStringRequest("Key");             int? from = GetIntRequest("from");             int? size = GetIntRequest("size");                         return ElasticSearchHelper.Intance.SearchFullFiledss<person>("db_test", "person", key ?? "方鸿渐", from == null ? 0 : from.Value, size == null ? 20 : size.Value);         }
复制代码

 

WebSite中的view视图调用webapi

说明:我是直接使用ajax(jsop格式)调用webapi,返回的数据直接用knockout绑定到table中的。

视图代码如下:

复制代码
@{    ViewBag.Title = "ElasticSearch测试";    Layout = null;}<link href="~/Content/bootstrap.css" rel="stylesheet" /><script src="~/Scripts/jquery-2.1.3.min.js"></script><script src="~/Scripts/knockout-3.3.0.js"></script><style>    b{        color:red;    }</style><script>    $(function () {        function ViewModel() {            self = this;            self.getData = ko.observableArray();            //定义加载数据方法            self.loadData = function () {                $.get("http://192.168.0.230/api/estest//SearchFullFiledss", { key: "@Request.QueryString["key"]", from: "@Request.QueryString["from"]", size: "@Request.QueryString["size"]" }, function (data) {                    // alert(data.hits);                    // alert(data.list.length);                    $("#count").text("符合条件的数据供:" + data.hits + "  共花费了:" + data.took + "毫秒");                                       $.each(data.list, function (i) {                        var date=data.list[i].birthday;                                           });                    self.getData(data.list);                }, "jsonp");            };            //调用定义方法            self.loadData();        }        ko.applyBindings(new ViewModel());    });</script><div id="count"></div><div class="row">    <table class="table-bordered table-condensed table-hover table-striped">        <tbody data-bind="foreach: getData">            <tr>                <td data-bind="text: id" />                <td data-bind="html: name" />                <td data-bind="text: sex" />                <td data-bind="text: age" />                                <td data-bind="text: birthday" />                <td data-bind="html: intro" />            </tr>        </tbody>    </table></div><script></script>
复制代码

 

搜索结果测试如下(我是用"api/estest//SearchFullFiledss"这个api测试的,搜索age在1到500之间,并且字段intro 或者name 包含词组key中的所有单词。):

1.首先我们看一看测试数据总共有多少:

 

我们可以看到总共db_test中总共有两千多万条数据。

 

搜索测试1

测试条件:key=上海方鸿渐&from=0&size=100,key指搜索关键短语,0是从第0条开始区数据,100是指取一百条数据,隐藏条件是age大于1小于500

 

我们可以看到,首次搜索时,两千多万条数据大约耗时3.5秒,这里还包括取100条数据的时间,如果把数据改为20条则会更快。这里的硬件配置还只限于我的本机测试(内存8G,处理器Intel i5-4590 3.3GHZ)。

 

搜索测试2

测试条件同测试1:key=上海方鸿渐&from=0&size=100,key指搜索关键短语,0是从第0条开始区数据,100是指取一百条数据,隐藏条件是age大于1小于500

 

 

我们可以看到,搜索耗时降到了大约1.5秒。这说明同一个关键词搜索越频繁,搜索速度越快,这是因为ElasticSearch会自动将搜索的内容缓存到内存中。

 

搜索测试3

测试条件:key=香烟德国&from=0&size=20,key指搜索关键短语,0是从第0条开始区数据,20是指取一百条数据,隐藏条件是age大于1小于500

 

搜索测试4

测试条件同测试3,二次相同条件搜索:key=香烟德国&from=0&size=20,key指搜索关键短语,0是从第0条开始区数据,20是指取一百条数据,隐藏条件是age大于1小于500

 

我们可以看到,在二次搜索条件相同,搜索数据降低到20条的时候,只耗时不到0.5秒。

原创粉丝点击