本文來自:https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70

圖片

我敢打賭,您肯定會被要求向Web應用程序中添加高級搜索功能,而且通常是全文的類似Google的搜索。

在技術電子商務的開發過程中,我們被要求允許用戶對產品進行高級研究,以便他們可以高效,完全地找到所需的內容。

我們基於對象的所有字段上給定字符串的搜索嘗試了自定義搜索的實現。爲了優化時間,我們嘗試在服務和數據庫級別之間添加一個緩存層,以避免對數據庫造成過多壓力,但是我們對結果不滿意。然後,我們在市場上搜索了可以滿足我們需求的第三方產品,經過深入分析,我們選擇採用ElasticSearch:一個基於REST協議的,可管理研究和分析的分佈式,易於適應的搜索引擎同樣,也方便了數據的外推和轉換。

具體來說,我們正在談論基於Apache Lucene的開源全文搜索引擎,該引擎可用於管理文檔的索引和研究。讓我們嘗試瞭解基本概念。

ElasticSearch將數據存儲在一個或多個索引中。ES的索引與SQL DB的索引非常相似,因爲我們使用它來存儲和讀取文檔。

文檔是ElasticSearch世界的主要實體。它由一組具有名稱和一個或多個值的字段組成。每個文檔可能具有一組字段,並且沒有給出任何架構或定義的結構。這只是一個JSON對象。

所有文檔在存儲之前都經過分析。這種分析過程(稱爲映射)是通過過濾數據內容(例如,刪除HTML標籤)並將其標記化來執行的,以便將文檔拆分爲標記。

ElasticSearch中的每個文檔都有一個類型。這樣就可以將各種文檔類型存儲在同一索引上,併爲幾種類型獲取幾種映射。

ElasticSearch服務器的單個實例稱爲Node。在很多情況下,單個節點就足夠了,但是有時您需要管理故障,或者您有太多數據無法使用單個節點進行管理。在這種情況下,您可以使用多節點集羣,這是一組協同工作的節點來管理比單個實例無法處理的更大的負載。您可以配置羣集,以便即使某些節點不可用,也可以保證搜索和管理功能。

爲了使羣集正常運行,ElasticSearch將數據分佈在Apache Lucene的多個物理索引上。這些索引稱爲“ 碎片”,而擴展過程稱爲“ 碎片”。ElasticSearch自動管理分片,因此最終用戶似乎只是一個大索引。

副本是分片的副本,可用於以原始分片的相同模式進行查詢。

副本可減輕無法處理所有請求的單個節點上的負載,並提供更高的數據安全性,因爲如果您丟失了原始分片中的數據,則可以在副本上對其進行恢復。

ElasticSearch收集了大量有關集羣狀態,索引設置的信息,並將它們存儲到網關中。

從結構上講,ElasticSearch基於一些簡單的關鍵概念:

默認設置和值使得默認配置足以立即使用ElasticSearch。 它以分佈式方式工作。節點自動成爲集羣的一部分,並且在設置過程中,節點嘗試加入集羣。 沒有SPOF的P2P體系結構(單點故障)。節點自動連接到羣集的其他計算機以更改數據和相互監視; 只需在集羣中添加新節點,就可以輕鬆地進行擴展,無論是在數據量上還是在容量上。 在組織索引中的數據方面沒有任何限制。允許用戶修改數據模型而不會對搜索產生任何影響; NRT(近實時)搜索和版本控制。由於其分佈式特性,無法避免延遲和位於不同節點上的數據之間的差異。因此,它提供了版本控制機制。

當ElasticSearch節點啓動時,它使用多播(或單播,如果已配置)來查找同一集羣中的其他節點並連接到它們。

圖片

在羣集中,選擇一個節點作爲主節點。該節點負責管理集羣狀態和將分片分配給節點的過程。主節點讀取集羣狀態,並在需要時啓動恢復模式,該模式允許知道哪些分片可用,並指定其中一個作爲主分片。這樣,即使羣集沒有可用的全部資源,它也似乎可以正常工作。然後,主節點查找重複的分片,並將其作爲副本處理。

在標準運行期間,主節點檢查所有可用節點是否正常工作。如果其中之一在配置的時間範圍內不可用,則將該節點視爲已損壞,並運行容錯過程。容錯的主要活動是平衡已損壞節點的羣集和碎片,並分配一個負責這些碎片的新節點。然後,對於每個主分片丟失,將定義一個在可用副本之間選擇的新主分片。

圖片

如前所述,ElasticSearch提供了一些API REST,可供每個能夠發送HTTP請求和接收HTTP響應的系統使用(大多數開發框架的所有瀏覽器和庫)。

ElasticSearch請求由一些包含的已定義URL發送。最終是JSON主體。響應也是JSON文檔。

ElasticSearch提供了四種索引數據的方式。

1. 索引API:它允許將文檔發送到已定義的索引; 2. 批量API:它允許通過HTTP協議發送多個文檔; 3. UDP批量API:它允許通過任何協議發送多個文檔(更快但更不可靠); 4. 插件:在節點上執行,它們從外部系統獲取數據。

重要的是要記住,索引只是在主分片上而不是在其副本上,因此,如果將索引請求發送到不包含主分片或可能包含其副本的節點,則該請求將轉發到主分片。

圖片

使用 Query API 執行搜索。使用 查詢DSL (基於JSON的語言來構建複雜的查詢),可以:

使用各種類型的查詢,包括簡單查詢,短語,範圍,布爾值,空間查詢和其他查詢; 通過組合簡單查詢來構建複雜查詢; 通過排除不符合選定條件的文檔而不影響其分數來過濾文檔; 查找與其他文件相似的文件; 查找給定短語的建議或更正; 查找與給定文檔匹配的查詢。

搜索不是一個單階段的簡單過程,但是,通常可以將其分爲兩個階段: scatter(分散) ,在其中查詢索引的所有相關分片; gather(收集) ,在其中收集,處理和排序所有寶貴的結果。

圖片

弄髒你的手!

ES提供了雲和本地兩種使用方式。如果要在Windows計算機上安裝它,則需要具有Java虛擬機的更新版本(https://www.elastic.co/support/matrix#matrix_jvm),然後可以從ElasticSearch下載中下載一個zip文件。頁面(https://www.elastic.co/downloads/elasticsearch)並將其提取到磁盤上的文件夾中,例如 C: \ Elasticsearch

要執行它,您可以運行 C: \ Elasticsearch \ bin \ elasticsearch.bat

如果要將ElasticSearch用作服務,以便可以使用Windows工具啓動或停止它,則需要在文件 C: \ Elasticsearch \ config \ jvm.options中 添加一行。

對於32位系統,您必須鍵入 -Xss320k*,對於64位系統 -Xss1m。 *

更改此設置後,您必須打開命令提示符或Powershell並執行

C:\ Elasticsearch \ bin \ elasticsearch-service.bat

。可用的 命令包括 install, remove, start, stop 和manager.。

要創建服務,我們必須輸入:

C:\ Elasticsearch \ bin \ elasticsearch-service.bat install

要管理服務,我們鍵入:

C:\ Elasticsearch \ bin \ elasticsearch-service.bat manager

器,該管理器打開Elastic Service Manager,這是一個GUI,可通過該GUI進行有關服務的自定義設置並管理其狀態。

默認cluster.name和node.name是 elasticsearch 分別和你的主機名。如果您打算繼續使用該羣集或添加更多節點,則最好通過在 elasticsearch.yml

文件中對其進行修改來將這些默認值更改爲唯一名稱。

我們可以通過瀏覽http:// localhost:9200 /來驗證ElasicSearch的正確執行。如果一切正常,我們將得到以下結果:

圖片

爲了實現基於.NET Core的解決方案,我們使用了 NEST 軟件包,可以通過以下命令安裝該軟件包:

dotnet add package NEST

NEST允許我們在索引和搜索文檔以及節點和分片的管理中本地使用所有ElasticSearch功能。爲了管理NEST插件,我們創建了ElasticsearchExtensions類:

public static class ElasticsearchExtensions

{

public static void AddElasticsearch(this IServiceCollection services, IConfiguration configuration)

{

var url = configuration["elasticsearch:url"];

var defaultIndex = configuration["elasticsearch:index"];


var settings = new ConnectionSettings(new Uri(url))

.DefaultIndex(defaultIndex);


AddDefaultMappings(settings);


var client = new ElasticClient(settings);


services.AddSingleton(client);


CreateIndex(client, defaultIndex);

}


private static void AddDefaultMappings(ConnectionSettings settings)

{

settings

DefaultMappingFor<Product>(m => m

.Ignore(p => p.Price)

.Ignore(p => p.Quantity)

.Ignore(p => p.Rating)

);

}


private static void CreateIndex(IElasticClient client, string indexName)

{

var createIndexResponse = client.Indices.Create(indexName,

index => index.Map<Product>(x => x.AutoMap())

);

}

}

在其中我們找到對象的配置和映射,在本例中爲Product類。在此類中,我們決定忽略在索引階段存儲價格,數量和評級。通過以下指令在Startup.cs中調用此類:

public void ConfigureServices(IServiceCollection services)

{

// ...

services.AddElasticsearch(Configuration);

}

這使我們能夠在啓動時加載的所有設置,在修改它們 elasticsearch 的第

appsettings.json

文件,在其中我們插入如下一行:

"elasticsearch": {

"index": "products",

"url": "http://localhost:9200/"

}

索引表示選擇用來存儲文檔的默認索引,而 url 是我們的ElasticSearch實例的地址。我們的產品對象定義如下:

public class Product

{

public int Id { get; set; }

public string Ean { get; set; }

public string Name { get; set; }

public string Description { get; set; }

public string Brand { get; set; }

public string Category { get; set; }

public string Price { get; set; }

public int Quantity { get; set; }

public float Rating { get; set; }

public DateTime ReleaseDate { get; set; }

}

如前所述,可以分別或在列表中爲產品建立索引。在我們的產品服務中,我們實現了兩種方式:

public async Task SaveSingleAsync(Product product)

{

if (_cache.Any(p => p.Id == product.Id))

{

await _elasticClient.UpdateAsync<Product>(product, u => u.Doc(product));

}

else

{

_cache.Add(product);

await _elasticClient.IndexDocumentAsync(product);

}

}


public async Task SaveManyAsync(Product[] products)

{

_cache.AddRange(products);

var result = await _elasticClient.IndexManyAsync(products);

if (result.Errors)

{

// the response can be inspected for errors

foreach (var itemWithError in result.ItemsWithErrors)

{

_logger.LogError("Failed to index document {0}: {1}",

itemWithError.Id, itemWithError.Error);

}

}

}


public async Task SaveBulkAsync(Product[] products)

{

_cache.AddRange(products);

var result = await _elasticClient.BulkAsync(b => b.Index("products").IndexMany(products));

if (result.Errors)

{

// the response can be inspected for errors

foreach (var itemWithError in result.ItemsWithErrors)

{

_logger.LogError("Failed to index document {0}: {1}",

itemWithError.Id, itemWithError.Error);

}

}

}

在這裏我們使用_cache數組來進一步緩存產品列表。對於多模式,我們也實現了批量版本,這使我們能夠在更短的時間內索引大量文檔,並且我們已經處理了日誌插入中的任何錯誤。

請注意,SaveSingleAsync方法通過檢查緩存數組來管理文檔的插入和修改。

對於文檔刪除,我們實現了DeleteAsync方法:

public async Task DeleteAsync(Product product)

{

await _elasticClient.DeleteAsync<Product>

(product);


if (_cache.Contains(product))

{

_cache.Remove(product);

}

}

GetSearchUrl方法允許我們獲取用於管理頁面調度的URL。出於開發目的,我們實現了ReIndex方法,該方法允許我們刪除索引上的所有文檔,然後一次又一次地導入它們。這對於導入現有和未加載文檔的列表很有用。

//Only for development purpose

[HttpGet("/search/reindex")]

public async Task<IActionResult>ReIndex()

{

await _elasticClient.DeleteByQueryAsync<Product>(q => q.MatchAll());


var allProducts = (await _productService.GetProducts(int.MaxValue)).ToArray();


foreach (var product in allProducts)

{

await _elasticClient.IndexDocumentAsync(product);

}


return Ok($"{allProducts.Length} product(s) reindexed");

}

出於示例目的,我們創建了一個界面,該界面允許我們通過 Bogus 插件添加N個動態生成的產品,並管理產品的CRUD。運行項目後,我們將看到以下屏幕:

圖片

例如,如果我們嘗試將10種產品添加到索引中,在文本框中輸入 10 ,然後單擊“  導入文檔” 按鈕,則可以使用搜索框查看結果,也可以直接從瀏覽器中瀏覽到http頁面:// localhost:9200 / products / _search,我們將在其中得到這樣的結果:

圖片

本文中使用的代碼可 在此處獲得 [2]

References

[1] 查看原文:  https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70

[2] 在此處獲得:  https://github.com/enricobencivenga/ElasticSearch

相關文章