首页 > 其他分享 >我的网站集成ElasticSearch初体验

我的网站集成ElasticSearch初体验

时间:2024-09-25 12:46:51浏览次数:1  
标签:集成 初体验 string List Field ElasticSearch using new public

我的网站集成ElasticSearch初体验

 

   最近,我给我的网站(https://www.xiandanplay.com/)尝试集成了一下es来实现我的一个搜索功能,因为这个是我第一次了解运用elastic,所以如果有不对的地方,大家可以指出来,话不多说,先看看我的一个大致流程

      这里我采用的sdk的版本是Elastic.Clients.Elasticsearch, Version=8.0.0.0,官方的网址Installation | Elasticsearch .NET Client [8.0] | Elastic

      我的es最开始打算和我的应用程序一起部署到ubuntu上面,结果最后安装kibana的时候,各种问题,虽好无奈,只好和我的SqlServer一起安装到windows上面,对于一个2G内容的服务器来说,属实有点遭罪了。

1、配置es

 在es里面,我开启了密码认证。下面是我的配置

"Search": {
    "IsEnable": "true",
    "Uri": "http://127.0.0.1:9200/",
    "User": "123",
    "Password": "123"
  }
然后新增一个程序集

然后再ElasticsearchClient里面去写一个构造函数去配置es

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using Core.Common; using Core.CPlatform; using Core.SearchEngine.Attr; using Elastic.Clients.Elasticsearch; using Elastic.Clients.Elasticsearch.IndexManagement; using Elastic.Transport;   namespace Core.SearchEngine.Client {     public class ElasticSearchClient : IElasticSearchClient     {         private ElasticsearchClient elasticsearchClient;         public ElasticSearchClient()         {             string uri = ConfigureProvider.configuration.GetSection("Search:Uri").Value;             string username = ConfigureProvider.configuration.GetSection("Search:User").Value;             string password = ConfigureProvider.configuration.GetSection("Search:Password").Value;             var settings = new ElasticsearchClientSettings(new Uri(uri))                           .Authentication(new BasicAuthentication(username, password)).DisableDirectStreaming();             elasticsearchClient = new ElasticsearchClient(settings);         }         public ElasticsearchClient GetClient()         {             return elasticsearchClient;         }     } }

   然后,我们看skd的官网有这个这个提示

 客户端应用程序应创建一个 该实例,该实例在整个应用程序中用于整个应用程序 辈子。在内部,客户端管理和维护与节点的 HTTP 连接, 重复使用它们以优化性能。如果您使用依赖项注入 容器中,客户端实例应注册到 单例生存期

所以我直接给它来一个AddSingleton

复制代码
using Core.SearchEngine.Client;
using Microsoft.Extensions.DependencyInjection;

namespace Core.SearchEngine
{
    public static class ConfigureSearchEngine
    {
        public static void AddSearchEngine(this IServiceCollection services)
        {
            services.AddSingleton<IElasticSearchClient, ElasticSearchClient>();
        }
    }
}
复制代码

2、提交文章并且同步到es

 然后就是同步文章到es了,我是先写入数据库,再同步到rabbitmq,通过事件总线(基于事件总线EventBus实现邮件推送功能)写入到es

先定义一个es模型

复制代码
using Core.SearchEngine.Attr;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XianDan.Model.BizEnum;

namespace XianDan.Domain.Article
{
    [ElasticsearchIndex(IndexName ="t_article")]//自定义的特性,sdk并不包含这个特性
    public class Article_ES
    {
        public long Id { get; set; }
        /// <summary>
        /// 作者
        /// </summary>
        public string Author { get; set; }
        /// <summary>
        /// 标题                                                                               
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 标签
        /// </summary>
        public string Tag { get; set; }
        /// <summary>
        /// 简介                                                                              
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 内容
        /// </summary>
        public string ArticleContent { get; set; }
        /// <summary>
        /// 专栏
        /// </summary>
        public long ArticleCategoryId { get; set; }
        /// <summary>
        /// 是否原创
        /// </summary>
        public bool? IsOriginal { get; set; }
        /// <summary>
        /// 评论数
        /// </summary>
        public int? CommentCount { get; set; }
        /// <summary>
        /// 点赞数
        /// </summary>
        public int? PraiseCount { get; set; }
        /// <summary>
        /// 浏览次数
        /// </summary>
        public int? BrowserCount { get; set; }
        /// <summary>
        /// 收藏数量
        /// </summary>
        public int? CollectCount { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; }
    }
}
复制代码

然后创建索引

复制代码
 string index = esArticleClient.GetIndexName(typeof(Article_ES));
            await esArticleClient.GetClient().Indices.CreateAsync<Article_ES>(index, s =>
            s.Mappings(
                x => x.Properties(
                    t => t.LongNumber(l => l.Id)
                         .Text(l=>l.Title,z=>z.Analyzer(ik_max_word))
                         .Keyword(l=>l.Author)
                         .Text(l=>l.Tag,z=>z.Analyzer(ik_max_word))
                         .Text(l=>l.Description,z=>z.Analyzer(ik_max_word))
                         .Text(l=>l.ArticleContent,z=>z.Analyzer(ik_max_word))
                         .LongNumber(l=>l.ArticleCategoryId)
                         .Boolean(l=>l.IsOriginal)
                         .IntegerNumber(l=>l.BrowserCount)
                         .IntegerNumber(l=>l.PraiseCount)
                         .IntegerNumber(l=>l.PraiseCount)
                         .IntegerNumber(l=>l.CollectCount)
                         .IntegerNumber(l=>l.CommentCount)
                         .Date(l=>l.CreateTime)
                    )
                )
            );
复制代码

然后每次增删改文章的时候写入到mq,例如

复制代码
 private async Task SendToMq(Article article, Operation operation)
        {
            ArticleEventData articleEventData = new ArticleEventData();
            articleEventData.Operation = operation;
            articleEventData.Article_ES = MapperUtil.Map<Article, Article_ES>(article);
            TaskRecord taskRecord = new TaskRecord();
            taskRecord.Id = CreateEntityId();
            taskRecord.TaskType = TaskRecordType.MQ;
            taskRecord.TaskName = "发送文章";
            taskRecord.TaskStartTime = DateTime.Now;
            taskRecord.TaskStatu = (int)MqMessageStatu.New;
            articleEventData.Unique = taskRecord.Id.ToString();
            taskRecord.TaskValue = JsonConvert.SerializeObject(articleEventData);
            await unitOfWork.GetRepository<TaskRecord>().InsertAsync(taskRecord);
            await unitOfWork.CommitAsync();
            try
            {
                eventBus.Publish(GetMqExchangeName(), ExchangeType.Direct, BizKey.ArticleQueueName, articleEventData);
            }
            catch (Exception ex)
            {
                var taskRecordRepository = unitOfWork.GetRepository<TaskRecord>();
                TaskRecord update = await taskRecordRepository.SelectByIdAsync(taskRecord.Id);
                update.TaskStatu = (int)MqMessageStatu.Fail;
                update.LastUpdateTime = DateTime.Now;
                update.TaskResult = "发送失败";
                update.AdditionalData = ex.Message;
                await taskRecordRepository.UpdateAsync(update);
                await unitOfWork.CommitAsync();
            }

        }
复制代码

mq订阅之后写入es,具体的增删改的方法就不写了吧

3、开始查询es

  等待写入文章之后,开始查询文章,这里sdk提供的查询的方法比较复杂,全都是通过lmbda一个个链式去拼接的,但是我又没有找到更好的方法,所以就先这样吧

   先创建一个集合存放查询的表达式

List<Action<QueryDescriptor<Article_ES>>> querys = new List<Action<QueryDescriptor<Article_ES>>>();

   然后定义一个几个需要查询的字段

   我这里使用MultiMatch来实现多个字段匹配同一个查询条件,并且指定使用ik_smart分词

复制代码
Field[] fields =
                {
                    new Field("title"),
                    new Field("tag"),
                    new Field("articleContent"),
                    new Field("description")
                };
 querys.Add(s => s.MultiMatch(y => y.Fields(Fields.FromFields(fields)).Analyzer(ik_smart).Query(keyword).Type(TextQueryType.MostFields)));
复制代码

定义查询结果高亮,给查询出来的匹配到的分词的字段添加标签,同时前端需要对这个样式处理,

:deep(.search-words) em {     color: #ee0f29;     font-style: initial; } 复制代码
 Dictionary<Field, HighlightField> highlightFields = new Dictionary<Field, HighlightField>();
            highlightFields.Add(new Field("title"), new HighlightField()
            {
                PreTags = new List<string> { "<em>" },
                PostTags = new List<string> { "</em>" },
            });
            highlightFields.Add(new Field("description"), new HighlightField()
            {
                PreTags = new List<string> { "<em>" },
                PostTags = new List<string> { "</em>" },
            });
            Highlight highlight = new Highlight()
            {
                Fields = highlightFields
            };
复制代码

为了提高查询的效率,我只查部分的字段

复制代码
 SourceFilter sourceFilter = new SourceFilter();
            sourceFilter.Includes = Fields.FromFields(new Field[] { "title", "id", "author", "description", "createTime", "browserCount", "commentCount" });
            SourceConfig sourceConfig = new SourceConfig(sourceFilter);
            Action<SearchRequestDescriptor<Article_ES>> configureRequest = s => s.Index(index)
            .From((homeArticleCondition.CurrentPage - 1) * homeArticleCondition.PageSize)
            .Size(homeArticleCondition.PageSize)
            .Query(x => x.Bool(y => y.Must(querys.ToArray())))
            .Source(sourceConfig)
             .Sort(y => y.Field(ht => ht.CreateTime, new FieldSort() { Order=SortOrder.Desc}))
复制代码

获取查询的分词结果

复制代码
 var analyzeIndexRequest = new AnalyzeIndexRequest
            {
                Text = new string[] { keyword },
                Analyzer = analyzer
            };
            var analyzeResponse = await elasticsearchClient.Indices.AnalyzeAsync(analyzeIndexRequest);
            if (analyzeResponse.Tokens == null)
                return new string[0];
            return analyzeResponse.Tokens.Select(s => s.Token).ToArray();
复制代码

到此,这个就是大致的查询结果,完整的如下

复制代码
 public async Task<Core.SearchEngine.Response.SearchResponse<Article_ES>> SelectArticle(HomeArticleCondition homeArticleCondition)
        {
            string keyword = homeArticleCondition.Keyword.Trim();
            bool isNumber = Regex.IsMatch(keyword, RegexPattern.IsNumberPattern);
            List<Action<QueryDescriptor<Article_ES>>> querys = new List<Action<QueryDescriptor<Article_ES>>>();
            if (isNumber)
            {
                querys.Add(s => s.Bool(x => x.Should(
                    should => should.Term(f => f.Field(z => z.Title).Value(keyword))
                    , should => should.Term(f => f.Field(z => z.Tag).Value(keyword))
                    , should => should.Term(f => f.Field(z => z.ArticleContent).Value(keyword))
                    )));
            }
            else
            {
                Field[] fields =
                {
                    new Field("title"),
                    new Field("tag"),
                    new Field("articleContent"),
                    new Field("description")
                };
                querys.Add(s => s.MultiMatch(y => y.Fields(Fields.FromFields(fields)).Analyzer(ik_smart).Query(keyword).Type(TextQueryType.MostFields)));
            }
            if (homeArticleCondition.ArticleCategoryId.HasValue)
            {
                querys.Add(s => s.Term(t => t.Field(f => f.ArticleCategoryId).Value(FieldValue.Long(homeArticleCondition.ArticleCategoryId.Value))));
            }
            string index = esArticleClient.GetIndexName(typeof(Article_ES));
            Dictionary<Field, HighlightField> highlightFields = new Dictionary<Field, HighlightField>();
            highlightFields.Add(new Field("title"), new HighlightField()
            {
                PreTags = new List<string> { "<em>" },
                PostTags = new List<string> { "</em>" },
            });
            highlightFields.Add(new Field("description"), new HighlightField()
            {
                PreTags = new List<string> { "<em>" },
                PostTags = new List<string> { "</em>" },
            });
            Highlight highlight = new Highlight()
            {
                Fields = highlightFields
            };
            SourceFilter sourceFilter = new SourceFilter();
            sourceFilter.Includes = Fields.FromFields(new Field[] { "title", "id", "author", "description", "createTime", "browserCount", "commentCount" });
            SourceConfig sourceConfig = new SourceConfig(sourceFilter);
            Action<SearchRequestDescriptor<Article_ES>> configureRequest = s => s.Index(index)
            .From((homeArticleCondition.CurrentPage - 1) * homeArticleCondition.PageSize)
            .Size(homeArticleCondition.PageSize)
            .Query(x => x.Bool(y => y.Must(querys.ToArray())))
            .Source(sourceConfig)
             .Sort(y => y.Field(ht => ht.CreateTime, new FieldSort() { Order=SortOrder.Desc})).Highlight(highlight);
            var resp = await esArticleClient.GetClient().SearchAsync<Article_ES>(configureRequest);
            foreach (var item in resp.Hits)
            {
                if (item.Highlight == null)
                    continue;
                foreach (var dict in item.Highlight)
                {
                    switch (dict.Key)
                    {
                        case "title":
                            item.Source.Title = string.Join("...", dict.Value);
                            break;
                        case "description":
                            item.Source.Description = string.Join("...", dict.Value);
                            break;

                    }
                }
            }
            string[] analyzeWords = await esArticleClient.AnalyzeAsync(homeArticleCondition.Keyword);
            List<Article_ES> articles = resp.Documents.ToList();
            return new Core.SearchEngine.Response.SearchResponse<Article_ES>(articles, analyzeWords);
        }
复制代码

4、演示效果    

搞完之后,发布部署,看看效果,分词这里要想做的像百度那样,估计目前来看非常有难度的

   那么这里我也向大家求教一下,如何使用SearchRequest封装多个查询条件,如下

SearchRequest searchRequest = new SearchRequest();
 searchRequest.From = 0;
searchRequest.Size = 10;
  searchRequest.Query=多个查询条件

因为我觉得这样代码读起来比lambda可读性高些,能更好的动态封装。

标签:集成,初体验,string,List,Field,ElasticSearch,using,new,public
From: https://www.cnblogs.com/sexintercourse/p/18431103

相关文章

  • 直播平台美颜功能开发方案:基于视频美颜SDK的集成详解
    本篇文章,小编将于大家共同探究视频美颜SDK的美颜功能开发方案,帮助开发者深入理解其集成过程及关键技术。 一、美颜功能的重要性在竞争激烈的直播市场中,直播平台如何提升用户体验至关重要。美颜功能不仅能够改善主播的形象,提升观众的观看体验,还能增强互动性,增加用户留存率。优秀的......
  • ADS12204具有集成 PGA 和基准的 低功耗24位 ADC 模数转换芯片
    1特点•低电流消耗:在占空比模式下低至120μA(类型)•宽电源范围:2.3V至5.5V•可编程增益:1V/V至128V/V•可编程数据率:最高2kSPS•高达20位的有效分辨率•使用单周期设定数字滤波器在20SPS下同时抑制50Hz和60Hz的噪声•两个差分或四个单端输入•双匹配可......
  • 【随手记录】docker部署jenkins,集成maven、spring项目
    1、下载镜像文件到服务器dockerpulljenkins/jenkins:lts-jdk17或离线导入镜像:dockerload-ijenkins-lts-jdk17检查镜像是否导入:dockerimages|grepjenkinsjenkins/jenkinslts-jdk177a7add0bf3da2weeksago470MB2024年6月以后国内很多大型的Dock......
  • 【Elasticsearch系列三】分词器介绍
    ......
  • gi集成idea
    IDEA集成Git&IDEA集成GiteeIDEA集成Git(本地库)首先配置Git忽略文件(1)创建忽略规则文件文件名称:xxxx.ignore(前缀名随便起,建议是git.ignore)这个文件的存放位置原则上在哪里都可以,为了便于让~/.gitconfig文件引用,建议也放在用户家目录下git.ignore文件模版内容如下#......
  • Elasticsearch7.7修改network.host IP地址 start启动失败及Elasticsearch7的配置项详
    一、Elasticsearch7.7修改network.hostIP地址start启动失败    使用的是最新的Elasticsearch7.7版本,启动后默认是绑到127.0.0.1上的,这肯定不是我们需要的,所以修改配置文件将network.host配置选改成我们的服务器局域网IP地址。Elasticsearch7.7版本安装后配置文件elastic......
  • SqlEs-像使用数据库一样使用Elasticsearch
    SqlEsSqlEs是Elasticsearch的客户端JDBC驱动程序,支持采用sql语法操作Elasticsearch。SqlEs构建在RestHighLevelClient,屏蔽了RestHighLevelClient接口的复杂度,可以像使用数据一样使用Elasticsearch。社区架构特性零入侵应用无需改造,可以无缝集成到现有业务应用。J......
  • SpringBoot集成JPA及基本使用
    参考地址: https://blog.csdn.net/JingAi_jia917/article/details/138159418前言在讲解SpringBoot集成JPA之前,先简单了解一下几个概念,JDBC、ORM、JPA以及SpringDataJPA。1.1JDBCJDBC(JavaDataBaseConnectivity),是java连接数据库操作的原生接口API,为开发者访问数据库提供标准......
  • Elasticsearch8.14.3更换自带的openjdk版本
    目录1.背景2.当前版本3.安装指定版本4.配置环境变量5.重启6.验证当前版本7.总结概述:为何更换Elasticsearch集群中的JDK版本背景在部署的Elasticsearch集群中,我们遇到了频繁的CPU使用率飙升问题,导致某些节点宕机。这种情况不仅影响了集群的稳定性和可靠性,还增加了维......
  • Flink CDC介绍:基于流的数据集成工具
    FlinkCDC是一个基于流的数据集成工具,旨在为用户提供一套功能更加全面的编程接口(API)。该工具使得用户能够以YAML配置文件的形式,优雅地定义其ETL(Extract,Transform,Load)流程,并协助用户自动化生成定制化的Flink算子并且提交Flink作业。FlinkCDC在任务提交过程中......