Elasticsearch .net client NEST使用說明
目錄:
Elasticsearch.Net、NEST 交流群:523061899
demo源碼 https://github.com/huhangfei/NestDemos
Elasticsearch.Net與NEST是Elasticsearch為C#提供的一套客戶端驅動,方便C#調用Elasticsearch服務接口。Elasticsearch.Net是對Elasticsearch服務接口較基層的的實現,NEST是在前者基礎之上進行的封裝,方法更簡潔使用更方便。本文是針對NEST 2.X的使用的總結。
引用
引用dll
NEST.dll
Elasticsearch.Net.dll
Newtonsoft.Json.dll
注:僅支持.net framework>=4.5
概念
存儲結構:
在Elasticsearch中,文檔(Document)歸屬於一種類型(type),而這些類型存在於索引(index)中.
類比傳統關系型數據庫:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
客戶端語法
鏈式lambda 表達式( powerful query DSL)語法
s => s.Query(q => q .Term(p => p.Name, "elasticsearch") )
對象初始化語法
var searchRequest = new SearchRequest<VendorPriceInfo> { Query = new TermQuery { Field = "name", Value = "elasticsearch" } };
Connection鏈接
//單node Var node = new Uri(“……”); var settings = new ConnectionSettings(node); //多uris Var uris = new Uri [] { new Uri(“……”), new Uri(“……”) }; var pool = new StaticConnectionPool(uris); //多node Var nodes = new Node [] { new Node (new Uri(“……”)), new Node (new Uri(“……”)) }; var pool = new StaticConnectionPool(nodes); var settings = new ConnectionSettings(pool); var client = new ElasticClient(settings);
注:nest默認字段名首字母小寫,如果要設置為與Model中一致按如下設置。(強烈建議使用該設置)
var settings = new ConnectionSettings(node).DefaultFieldNameInferrer((name) => name);
連接池類型
//單節點 IConnectionPool pool = new SingleNodeConnectionPool(urls.FirstOrDefault()); //請求時隨機發送請求到各個正常的節點,不請求異常節點,異常節點恢復后會重新被請求 IConnectionPool pool = new StaticConnectionPool(urls); IConnectionPool pool = new SniffingConnectionPool(urls); //false.創建客戶端時,隨機選擇一個節點作為客戶端的請求對象,該節點異常后不會切換其它節點 //true,請求時隨機請求各個正常節點,不請求異常節點,但異常節點恢復后不會重新被請求 pool.SniffedOnStartup = true; //創建客戶端時,選擇第一個節點作為請求主節點,該節點異常后會切換其它節點,待主節點恢復后會自動切換回來 IConnectionPool pool = new StickyConnectionPool(urls);
索引選擇
方式1
//鏈接設置時設置默認使用索引 var settings = new ConnectionSettings().DefaultIndex("defaultindex");
方式2
//鏈接設置時設置索引與類型對應關系,操作時根據類型使用對應的索引 var settings = new ConnectionSettings().MapDefaultTypeIndices(m => m.Add(typeof(Project), "projects") );
方式3
//執行操作時指定索引 client.Search<VendorPriceInfo>(s => s.Index("test-index")); client.Index(data,o=>o.Index("test-index"));
優先級:方式3 > 方式2 > 方式1
索引(Index)
數據唯一Id
1) 默認以“Id”字段值作為索引唯一Id值,無“Id”屬性,Es自動生成唯一Id值,添加數據時統一類型數據唯一ID已存在相等值,將只做更新處理。 注:自動生成的ID有22個字符長,URL-safe, Base64-encoded string universally unique identifiers, 或者叫UUIDs。
2) 標記唯一Id值
[ElasticsearchType(IdProperty = "priceID")] public class VendorPriceInfo { public Int64 priceID { get; set; } public int oldID { get; set; } public int source { get; set; } }
3) 索引時指定
client.Index(data, o => o.Id(data.vendorName));
優先級: 3) > 2) > 1)
類型(Type)
1) 默認類型為索引數據的類名(自動轉換為全小寫) 2) 標記類型
[ElasticsearchType(Name = "datatype")] public class VendorPriceInfo { public Int64 priceID { get; set; } public int oldID { get; set; } public int source { get; set; } }
3) 索引時指定
client.Index(data, o => o.Type(new TypeName() { Name = "datatype", Type = typeof(VendorPriceInfo) }));
或
client.Index(data, o => o.Type<MyClass>());//使用 2)標記的類型
優先級:3)> 2) > 1)
創建
client.CreateIndex("test2"); //基本配置 IIndexState indexState=new IndexState() { Settings = new IndexSettings() { NumberOfReplicas = 1,//副本數 NumberOfShards = 5//分片數 } }; client.CreateIndex("test2", p => p.InitializeUsing(indexState)); //創建並Mapping client.CreateIndex("test-index3", p => p.InitializeUsing(indexState).Mappings(m => m.Map<VendorPriceInfo>(mp => mp.AutoMap())));
注:索引名稱必須小寫
判斷
client.IndexExists("test2");
刪除
client.DeleteIndex("test2");
映射
概念
每個類型擁有自己的映射(mapping)或者模式定義(schema definition)。一個映射定義了字段類型、每個字段的數據類型、以及字段被Elasticsearch處理的方式。
獲取映射
var resule = client.GetMapping<VendorPriceInfo>();
特性
/// <summary> /// VendorPrice 實體 /// </summary> [ElasticsearchType(IdProperty = "priceID", Name = "VendorPriceInfo")] public class VendorPriceInfo { [Number(NumberType.Long)] public Int64 priceID { get; set; } [Date(Format = "mmddyyyy")] public DateTime modifyTime { get; set; } /// <summary> /// 如果string 類型的字段不需要被分析器拆分,要作為一個正體進行查詢,需標記此聲明,否則索引的值將被分析器拆分 /// </summary> [String(Index = FieldIndexOption.NotAnalyzed)] public string pvc_Name { get; set; } /// <summary> /// 設置索引時字段的名稱 /// </summary> [String(Name = "PvcDesc")] public string pvc_Desc { get; set; } /// <summary> /// 如需使用坐標點類型需添加坐標點特性,在maping時會自動映射類型 /// </summary> [GeoPoint(Name = "ZuoBiao",LatLon = true)] public GeoLocation Location { get; set; } }
映射
//根據對象類型自動映射 var result= client.Map<VendorPriceInfo>(m => m.AutoMap()); //手動指定 var result1 = client.Map<VendorPriceInfo>(m => m.Properties(p => p .GeoPoint(gp => gp.Name(n => n.Location)// 坐標點類型 .Fielddata(fd => fd .Format(GeoPointFielddataFormat.Compressed)//格式 array doc_values compressed disabled .Precision(new Distance(2, DistanceUnit.Meters)) //精確度 )) .String(s => s.Name(n => n.b_id))//string 類型 )); //在原有字段下新增字段(用於存儲不同格式的數據,查詢使用該字段方法查看【基本搜索】) //eg:在 vendorName 下添加無需分析器分析的值 temp var result2 = client.Map<VendorPriceInfo>( m => m .Properties(p => p.String(s => s.Name(n => n.vendorName).Fields(fd => fd.String(ss => ss.Name("temp").Index(FieldIndexOption.NotAnalyzed))))));
注:映射時已存在的字段將無法重新映射,只有新加的字段能映射成功。所以最好在首次創建索引后先進性映射再索引數據。
注:映射時同一索引中,多個類型中如果有相同字段名,那么在索引時可能會出現問題(會使用第一個映射類型)。
數據
數據操作
說明
-
添加數據時,如果文檔的唯一id在索引里已存在,那么會替換掉原數據;
-
添加數據時,如果索引不存在,服務會自動創建索引;
-
如果服務自動創建索引,並索引了數據,那么索引的映射關系就是服務器自動設置的;
-
通常正確的使用方法是在緊接着創建索引操作之后進行映射關系的操作,以保證索引數據的映射是正確的。然后才是索引數據;
-
文檔在Elasticsearch中是不可變的,執行Update事實上Elasticsearch的處理過程如下:
-
從舊文檔中檢索JSON
-
修改JSON值
-
刪除舊文檔
-
索引新文檔
-
所以我們也可以使用Index來更新已存在文檔,只需對應文檔的唯一id。
添加索引數據
添加單條數據
var data = new VendorPriceInfo() { vendorName = "測試"}; client.Index(data);
添加多條數據
var datas = new List<VendorPriceInfo> { new VendorPriceInfo(){priceID = 1,vendorName = "test1"}, new VendorPriceInfo(){priceID = 2,vendorName = "test2"}}; client.IndexMany(datas);
刪除數據
單條數據
DocumentPath<VendorPriceInfo> deletePath=new DocumentPath<VendorPriceInfo>(7); client.Delete(deletePath);
或
IDeleteRequest request = new DeleteRequest("test3", "vendorpriceinfo", 0); client.Delete(request);
注:刪除時根據唯一id刪除
集合數據
Indices indices = "test-1"; Types types = "vendorpriceinfo"; //批量刪除 需要es安裝 delete-by-query插件 var result = client.DeleteByQuery<VendorPriceInfo>(indices, types, dq => dq.Query( q => q.TermRange(tr => tr.Field(fd => fd.priceID).GreaterThanOrEquals("5").LessThanOrEquals("10"))) );
更新數據
更新所有字段
DocumentPath<VendorPriceInfo> deletePath=new DocumentPath<VendorPriceInfo>(2); Var response=client.Update(deletePath,(p)=>p.Doc(new VendorPriceInfo(){vendorName = "test2update..."})); //或 IUpdateRequest<VendorPriceInfo, VendorPriceInfo> request = new UpdateRequest<VendorPriceInfo, VendorPriceInfo>(deletePath) { Doc = new VendorPriceInfo() { priceID = 888, vendorName = "test4update........" } }; var response = client.Update<VendorPriceInfo, VendorPriceInfo>(request);
更新部分字段
IUpdateRequest<VendorPriceInfo, VendorPriceInfoP> request = new UpdateRequest<VendorPriceInfo, VendorPriceInfoP>(deletePath) { Doc = new VendorPriceInfoP() { priceID = 888, vendorName = "test4update........" } }; var response = client.Update(request);
更新部分字段
IUpdateRequest<VendorPriceInfo, object> request = new UpdateRequest<VendorPriceInfo, object>(deletePath) { Doc = new { priceID = 888, vendorName = " test4update........" } }; var response = client.Update(request); //或 client.Update<VendorPriceInfo, object>(deletePath, upt => upt.Doc(new { vendorName = "ptptptptp" }));
注:更新時根據唯一id更新
更新時使用本版號加鎖機制
//查詢到版本號 var result = client.Search<MessageInfo>( s => s.Index("ep_epdb8buddy_imchatinfo_messageinfo_nopaging_msguid_index").Type("MessageInfo") .Query(q => q.Term(tm => tm.Field("MsgUID").Value("5DCC-A8C4-44NV-VKGU"))).Size(1).Version()); foreach (var s in result.Hits) { Console.WriteLine(s.Id+" - "+s.Version); } var path = new DocumentPath<MessageInfo>("5DCC-A8C4-44NV-VKGU"); //更新時帶上版本號 如果服務端版本號與傳入的版本好相同才能更新成功 var response = client.Update(path, (p) => p.Index("ep_epdb8buddy_imchatinfo_messageinfo_nopaging_msguid_index-2017.03").Type("MessageInfo") .Version(2).Doc(new MessageInfo() { MsgUID = "5DCC-A8C4-44NV-VKGU", FromUserName = "測測測" + DateTime.Now }));
獲取數據
var response = client.Get(new DocumentPath<VendorPriceInfo>(0)); //或 var response = client.Get(new DocumentPath<VendorPriceInfo>(0),pd=>pd.Index("test4").Type("v2")); //多個 var response = client.MultiGet(m => m.GetMany<VendorPriceInfo>(new List<long> { 1, 2, 3, 4 }));
注:獲取時根據唯一id獲取
批處理操作
var listOps = new List<IBulkOperation> { //更新 new BulkUpdateOperation<VendorOfferNewsByCsId, object>(new VendorOfferNewsByCsId() {}, new {Name = "new-project2"}) {Id = "3"}, //刪除 new BulkDeleteOperation<VendorOfferNewsByCsId>(new Id(1)) //... }; IBulkRequest bulkRequest=new BulkRequest("indexname","typename"); var request = new BulkRequest() { Operations = listOps }; client.Bulk(request);
搜索
說明
-
搜索分頁時隨着分頁深入,資源花費是成倍增長的。限制from+size⇐10000分頁說明資料 資料
-
需要掃描所有數據時,使用滾屏掃描。 掃描與滾屏資料
-
搜索中提供了查詢(Query)和過濾(Filter):
-
query是要計算相關性評分的,filter不要;
-
query結果不緩存,filter緩存。
-
全文搜索、評分排序,使用query;
-
是非過濾,精確匹配,使用filter。
基本搜索
var result = client.Search<VendorPriceInfo>( s => s .Explain() //參數可以提供查詢的更多詳情。 .FielddataFields(fs => fs //對指定字段進行分析 .Field(p => p.vendorFullName) .Field(p => p.cbName) ) .From(0) //跳過的數據個數 .Size(50) //返回數據個數 .Query(q => q.Term(p => p.vendorID, 100) // 主要用於精確匹配哪些值,比如數字,日期,布爾值或 not_analyzed的字符串(未經分析的文本數據類型): && q.Term(p => p.vendorName.Suffix("temp"), "姓名") //用於自定義屬性的查詢 (定義方法查看MappingDemo) && q.Bool( //bool 查詢 b => b .Must(mt => mt //所有分句必須全部匹配,與 AND 相同 .TermRange(p => p.Field(f => f.priceID).GreaterThan("0").LessThan("1"))) //指定范圍查找 .Should(sd => sd //至少有一個分句匹配,與 OR 相同 .Term(p => p.priceID, 32915), sd => sd.Terms(t => t.Field(fd => fd.priceID).Terms(new[] {10, 20, 30})),//多值 //|| //sd.Term(p => p.priceID, 1001) //|| //sd.Term(p => p.priceID, 1005) sd => sd.TermRange(tr => tr.GreaterThan("10").LessThan("12").Field(f => f.vendorPrice)) ) .MustNot(mn => mn//所有分句都必須不匹配,與 NOT 相同 .Term(p => p.priceID, 1001) , mn => mn.Bool( bb=>bb.Must(mt=>mt .Match(mc=>mc.Field(fd=>fd.carName).Query("至尊")) )) ) ) )//查詢條件 .Sort(st => st.Ascending(asc => asc.vendorPrice))//排序 .Source(sc => sc.Include(ic => ic .Fields( fd => fd.vendorName, fd => fd.vendorID, fd => fd.priceID, fd => fd.vendorPrice))) //返回特定的字段 ); //TResult var result1 = client.Search<VendorPriceInfo, VendorPriceInfoP>(s => s.Query( q => q.MatchAll() ) .Size(15) );