如何在我们的Asp.NETCore应用程序中使用ElasticSearch高级功能将其集成到我们的.NETWeb应用程序中。今天,我们仍然有一个电子商务网站来向您展示如何使用ElasticSearch的许多功能来改进您的搜索。我们使用没有嵌套类的平面Product类来轻松管理搜索,但这种方法有很多局限性。然后我们引入了一个新的数据模型,以便任何对象都是要建模的实体。一个文档可以包含无限数量的相关字段和值(数组、简单类型和复杂类型),并保存为JSON文档。我们的模型产品类别已分别更改为:publicclassProduct{publicintId{get;set;}publicstringEan{get;set;}publicstringName{get;set;}publicstringDescription{get;set;}publicBrandBrand{get;set;}publicCategoryCategory{get;设置;}publicStoreStore{get;set;}publicdecimalPrice{get;set;}publicstringCurrency{get;set;}publicintQuantity{get;set;}publicfloatRating{get;set;}publicDateTimeReleaseDate{get;set;}publicstringImage{get;set;}publicListReviews{get;set;}}其中品牌,类别,商家评论和用户类别别别是:publicclassBrand{publicintId{get;set;}publicstringName{get;set;}publicstringDescription{get;set;}}publicclassCategory{publicintId{get;set;}publicstringName{get;set;}publicstringDescription{get;set;}}publicclassStore{publicintId{get;set;}publicstringName{get;set;}publicstringDescription{get;set;}}publicclassReview{publicintId{get;set;}publicshortRating{get;set;}publicstringDescription{get;set;}publicUserUser{get;set;}}publicclassUser{publicintId{get;set;}publicstringFirstName{get;set;}publicstringLastName{get;set;}publicstringIPAddress{get;set;}publicGeoIpGeoIp{get;set;}}GeoIp是NEST库中用于地理数据的产品索引类,简称产品。我们这样创建和配置它:client.Indices.Create("products",index=>index.Map(x=>x.AutoMap()).Map(x=>x.AutoMap()).Map(x=>x.AutoMap()).Map(x=>x.AutoMap()).Map(x=>x.AutoMap()).Map(x=>x.AutoMap().Properties(props=>props.Keyword(t=>t.Name("fullname")).Ip(t=>t.Name(dv=>dv.IPAddress)).Object(t=>t.Name(dv=>dv.GeoIp)))))我们专门为ElasticSearch索引创建一个名为fullname的新属性,为名为fullname的User类创建一个新属性,并定义地理信息待处理。为了让我们的产品在索引之前进行处理,一个有用的方法是node.ingest,即进行文档预处理的节点。接收节点拦截所有索引请求,甚至批量索引请求,并在将文档发送回索引API之前将所有定义的转换应用于其内容。在配置文件elasticsearch.yml中必须传递以下参数。启用node.ingest:node.ingest:true在我们的示例中,我们使用相同的节点进行搜索和摄取,我们不需要编写代码来管理摄取节点,但是,如果我们想要一组专用的摄取节点,则ElasticSearch客户端必须配置如下:varpool=newStaticConnectionPool(new[]{newUri("http://ingestnode1:9200"),newUri("http://ingestnode2:9200"),newUri("http://ingestnode3:9200")});varsettings=newConnectionSettings(池);varclient=newElasticClient(设置);为了预处理文档,在索引之前定义了一个管道,它指定了一组可以转换文档的进程。有许多可用的默认过程。例如:GeoIP从IP地址获取地理信息,JSON将字符串转换为JSON对象,小写和大写,Drop删除匹配某些参数的文档。您还可以创建自定义过程。我们在项目中使用的管道是:client.Ingest.PutPipeline("product-pipeline",p=>p.Processors(ps=>ps.Uppercase(s=>s.Field(t=>t.姓名)).Uppercase(s=>s.Field(t=>t.Name)).Set(s=>s.Field("fullname").Value(s.Field(f=>f.FirstName)+""+s.Field(f=>f.LastName))).GeoIp(s=>s.Field(i=>i.IPAddress).TargetField(i=>i.GeoIp))));此管道处理文档,以便:Brand.Name和Category.Name将由大写输入以大写索引;User.fullname将包含名字和姓氏(设置摄取);User.IPAddress将地理定位的地理地址(GeoIp提取)。管道保存在ElasticSearch集群状态中,要使用它们,您必须在索引请求中指定管道参数,以便摄取节点知道必须使用哪个管道:client.Bulk(b=>b.Index("products").Pipeline("product-pipeline").Timeout("5m").Index(/*snip*/).Index(/*snip*/).Index(/*snip*/).RequestConfiguration(rc=>rc.RequestTimeout(TimeSpan.FromMinutes(5))));这样我们就定义了索引过程,这样我们就可以按需获取文档列表了。使用创建的管道索引文档后,我们可以通过浏览器访问http://localhost:9200/products/_search来检查它们。我们得到类似如下的结果:正如上一篇文章中提到的,搜索过程是基于文档分析的。这是标记化过程的第一阶段(将文本分成小块,称为标记),另一个是规范化过程(它允许您找到与搜索词不相等但足够相似的标记的匹配项相关项目)为搜索建立索引的文本。分析仪执行此过程。分析器由三个主要部分组成:1.0或更多的字符过滤器2.1标记器3.0或更多的标记过滤器有一些默认的分析器可以使用,但是,为了根据我们的需求性质提高搜索的准确性,我们创建了一个自定义分析器。自定义分析器允许我们在分析过程中控制标记化之前对文档的任何更改、如何将其转换为标记以及如何规范化。这是我们的自定义分析器:varan=newCustomAnalyzer();an.CharFilter=newList();an.CharFilter.Add("html_strip");an.Tokenizer="edgeNGram";an.Filter=newList();an.Filter.Add("标准");an.Filter.Add("小写");an.Filter.Add("停止");settings.Analysis.Tokenizers.Add("edgeNGram",newNest.EdgeNGramTokenizer{MaxGram=15,MinGram=3});settings.Analysis.Analyzers.Add("product-analyzer",an);我们的分析器创建3到15个字符的小写标记。我们可以将分析器添加到一个或多个字段的索引中,或者作为标准分析器。client.CreateIndex("products",c=>c//AnalyzeraddedonlyforthepropertyDescriptionofProduct.AddMapping(e=>e.MapFromAttributes().Properties(p=>p.String(s=>s.Name(f=>f.Description).Analyzer("product-analyzer"))))//Analyzeraddedasdefault.Analysis(analysis=>analysis.Analyzers(a=>a.Add("default",an))))创建自定义分析器时,可以使用测试API对其进行测试。即使是默认分析器也可以执行这些测试。varanalyzeResponse=client.Indices.Analyze(a=>a.Tokenizer("standard").Filter("lowercase","stop").Text("Loremipsumdolorsitamet,consectetur..."));我们还可以使用数据聚合来提供通过搜索查询聚合的数据,它基于可以组合以获得复杂聚合的简单块。有不同类型的聚合,每种都有定义的范围和输出。它们可以分为:Bucketed:带有键和条件的容器;指标:从一组文档计算得出的指标;矩阵:对不同文档字段执行的一系列操作,以矩阵形式生成数据;管道:聚合聚合较多。在我们的例子中,我们使用聚合来按品牌、类别、价格范围获取产品数量。在下面的示例中,我们找到了产品价格的聚合:s=>s.Query(...).Aggregations(aggs=>aggs.Average("average_price",avg=>avg.Field(p=>p.Price)).Max("max_price",avg=>avg.Field(p=>p.Price)).Min("min_price",avg=>avg.Field(p=>p.Price)))另一个有用的聚合是按品牌、商店或类别分组:s=>s.Query(...).Aggregations(aggs=>aggs.ValueCount("products_for_category",avg=>avg.Field(p=>p.Category.Name)).ValueCount("products_for_brand",avg=>avg.Field(p=>p.Brand.Name)).ValueCount("products_for_store",avg=>avg.Field(p=>p.Store.Name)))这样我们就可以实时获取类别、品牌和商店的搜索产品数量。聚合数据还可用于创建仪表板,甚至使用动态过滤器(类似于电子商务)组织搜索,显然用于统计目的。改进您的搜索如您所知,我们对任何搜索结果都有积分。排名是一个从0到1的数字,它确定搜索参数接近该结果的程度。得分主要取决于三个参数:搜索词的频率、倒排文档的频率和字段长度。要从分数中排除得分太低的人,我们可以使用MinScore:s=>s.MinScore(0.5).Query(...)这样,我们就可以排除得分低于0.5的所有结果。Suggestors允许您使用与搜索文本相似的术语来搜索ElasticSearch索引。CompletionSuggestor对于自动完成很有用,例如,它会在您键入文本时引导您获得最佳和更相关的结果。completionsuggester经过优化以尽快返回结果,但它使用支持快速查找的结构并且需要资源。在我们的例子中,我们实现了一个基于产品名称的自动完成方法,当在搜索框中键入以下内容时将调用该方法:s=>s.Query(...).Suggest(su=>su.Completion("name",cs=>cs.Field(f=>f.Name).Fuzzy(f=>f.Fuzziness(Fuzziness.Auto)).Size(5)))Bettersearch另一个有用的方法是indexboost。当您搜索更多索引时,您可以为这些索引分配一个乘数,以便显示一个索引的结果多于另一个索引。您可以将其用于商业目的、与供应商达成协议或区分我们的产品。索引提升的一个例子是:s=>s.Query(...).IndicesBoost(b=>b.Add("products-1",1.5).Add("products-2",1))这里In例子中,我们把1乘以1的结果,和1乘以2的结果相乘,这样乘以1的结果会更频繁的显示出来。改进搜索的另一种方法是按某些参数对它们进行排序。在我们的例子中,我们可以:s=>s.Query().Sort(ss=>ss.Descending(SortSpecialField.Score).Descending(p=>p.Price).Descending(p=>p.ReleaseDate)。Ascending(SortSpecialField.DocumentIndexOrder))我们将评级、价格、发布日期和最后的索引顺序设置为更高的优先级。运行项目我们的示例项目是一个.NETCoreMVCWebApi应用程序,它提供了一个搜索框和一个仪表板,可以根据键入的文本自动刷新数据。首次运行项目时,我们可以加载由Bogus插件创建的n个Product对象。还有其他假类可以为品牌、类别、商店、评论和用户构建随机对象。它允许您拥有一个数据库来执行我们的搜索。varproductFaker=newFaker().CustomInstantiator(f=>newProduct()).RuleFor(p=>p.Id,f=>f.IndexFaker).RuleFor(p=>p.Ean,f=>f.Commerce.Ean13()).RuleFor(p=>p.Name,f=>f.Commerce.ProductName()).RuleFor(p=>p.Description,f=>f.Lorem.Sentence(f.Random.Int(5,20)).RuleFor(p=>p.Brand,f=>f.PickRandom(品牌)).RuleFor(p=>p.Category,f=>f.PickRandom(类别)).RuleFor(p=>p.Store,f=>f.PickRandom(stores)).RuleFor(p=>p.Price,f=>f.Finance.Amount(1,1000,2)).RuleFor(p=>p.Currency,"").RuleFor(p=>p.Quantity,f=>f.Random.Int(0,1000)).RuleFor(p=>p.Rating,f=>f.Random.Float(0,1)).RuleFor(p=>p.ReleaseDate,f=>f.Date.Past(2)).RuleFor(p=>p.Image,f=>f.Image.PicsumUrl()).RuleFor(p=>p.Reviews,f=>reviewFaker.Generate(f.Random.Int(0,1000)))在页面的中间,有一个仪表板,我们使用本文中描述的过滤器、分析器和方法。当您在顶部的搜索框中键入一些文本时,系统会建议相关产品,并且仪表板内容会根据搜索文本进行更新。结论在本文中,我向您展示了如何使用Elasticsearch高效地处理、分析和搜索复杂的现实场景中的数据。希望我对这个话题感兴趣。[3]此处[2]中提供了包含本文中使用的代码的示例项目。文章中的参考文献[1]:https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70[2]此处:https://github.com/enricobencivenga/ProductElasticSearchAdvanced[3]:https://github.com/enricobencivenga/ProductElasticSearchAdvanced