This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
У попередніх частинах цієї серії ми ввели концепцію повноформатного пошуку тексту і того, як ним можна скористатися для пошуку тексту у базі даних. У цій частині ми познайомимося з використанням OpenSearch з ядром ASPNET.
Попередні частини:
У цій частині ми поговоримо про те, як почати використовувати новий блискучий екземпляр OpenSearch з ядром ASP.NET.
Після того, як буде запущено екземпляр OpenSearch up і ми зможемо почати взаємодіяти з ним. Ми використаємо Клієнт OpenSearch НЕТ. Спочатку ми налаштували клієнт у нашому розширенні Setup
var openSearchConfig = services.ConfigurePOCO<OpenSearchConfig>(configuration.GetSection(OpenSearchConfig.Section));
var config = new ConnectionSettings(new Uri(openSearchConfig.Endpoint))
.EnableHttpCompression()
.EnableDebugMode()
.ServerCertificateValidationCallback((sender, certificate, chain, errors) => true)
.BasicAuthentication(openSearchConfig.Username, openSearchConfig.Password);
services.AddSingleton<OpenSearchClient>(c => new OpenSearchClient(config));
Це встановлює клієнту кінцеву точку і посвідчення. Ми також вмикаємо режим зневаджування, щоб побачити, що відбувається. Крім того, оскільки ми не використовуємо сертифікати True SSL, ми вимикаємо перевірку сертифікатів (не робити цього у виробництві).
Основною концепцією OpenSearch є Індекс. Подумайте про індекс, наприклад, таблицю бази даних, це місце, де зберігаються всі ваші дані.
Для цього ми використаємо Клієнт OpenSearch НЕТ. Ви можете встановити це за допомогою NuGet:
Ви можете помітити двох таких: Opensearch.Net і Opensearch.Client. Перша - це такі речі низького рівня, як управління з'єднанням, друга - це такі речі високого рівня, як індексування та пошук.
Тепер, коли ми його встановили, ми можемо почати вивчати індексування даних.
Створення індексу є напівпрозорим. Ви просто визначаєте, як має виглядати ваш індекс, а потім створюєте його. У коді нижче ви можете побачити нашу модель індексу " map " (спрощену версію моделі бази даних блогу). Для кожного з полів цієї моделі ми визначаємо тип цього типу (текст, дата, ключове слово тощо) і який аналізатор слід використовувати.
Тип важливий, оскільки визначає спосіб зберігання даних і спосіб його пошуку. Наприклад, поле " text " аналізується і позначається, поле " keyword " - ні. Таким чином, ви очікуєте знайти поле ключових слів саме так, як зберігається, але текстове поле ви можете знайти частини тексту.
Крім того, у цьому розділі категорії є рядком[], але ключовий тип розуміє, як правильно ними користуватися.
public async Task CreateIndex(string language)
{
var languageName = language.ConvertCodeToLanguageName();
var indexName = GetBlogIndexName(language);
var response = await client.Indices.CreateAsync(indexName, c => c
.Settings(s => s
.NumberOfShards(1)
.NumberOfReplicas(1)
)
.Map<BlogIndexModel>(m => m
.Properties(p => p
.Text(t => t
.Name(n => n.Title)
.Analyzer(languageName)
)
.Text(t => t
.Name(n => n.Content)
.Analyzer(languageName)
)
.Text(t => t
.Name(n => n.Language)
)
.Date(t => t
.Name(n => n.LastUpdated)
)
.Date(t => t
.Name(n => n.Published)
)
.Date(t => t
.Name(n => n.LastUpdated)
)
.Keyword(t => t
.Name(n => n.Id)
)
.Keyword(t=>t
.Name(n=>n.Slug)
)
.Keyword(t=>t
.Name(n=>n.Hash)
)
.Keyword(t => t
.Name(n => n.Categories)
)
)
)
);
if (!response.IsValid)
{
logger.LogError("Failed to create index {IndexName}: {Error}", indexName, response.DebugInformation);
}
}
Коли ми маємо індекс, щоб додати до нього елементи, нам потрібно додати елементи до цього індексу. Тут, коли ми додаємо БУНК, ми використовуємо метод вставки громіздкості.
Ви можете побачити, що спочатку ми використовуємо метод, що називаєтьсяGetExistingPosts
те, що поверне всі дописи, які вже є у покажчику. Потім ми згрупуємо дописи за мовою і відфільтруємо мову "uk" (хоча не хочемо індексувати, що, оскільки потрібен додатковий додаток, у нас ще немає). Потім ми відфільтровуємо всі дописи, які вже є в індексі.
Ми використовуємо хеш і ІД, щоб визначити, чи вже є допис в індексі.
public async Task AddPostsToIndex(IEnumerable<BlogIndexModel> posts)
{
var existingPosts = await GetExistingPosts();
var langPosts = posts.GroupBy(p => p.Language);
langPosts=langPosts.Where(p => p.Key!="uk");
langPosts = langPosts.Where(p =>
p.Any(post => !existingPosts.Any(existing => existing.Id == post.Id && existing.Hash == post.Hash)));
foreach (var blogIndexModels in langPosts)
{
var language = blogIndexModels.Key;
var indexName = GetBlogIndexName(language);
if(!await IndexExists(language))
{
await CreateIndex(language);
}
var bulkRequest = new BulkRequest(indexName)
{
Operations = new BulkOperationsCollection<IBulkOperation>(blogIndexModels.ToList()
.Select(p => new BulkIndexOperation<BlogIndexModel>(p))
.ToList()),
Refresh = Refresh.True,
ErrorTrace = true,
RequestConfiguration = new RequestConfiguration
{
MaxRetries = 3
}
};
var bulkResponse = await client.BulkAsync(bulkRequest);
if (!bulkResponse.IsValid)
{
logger.LogError("Failed to add posts to index {IndexName}: {Error}", indexName, bulkResponse.DebugInformation);
}
}
}
Як тільки ми відфільтрували існуючі дописи і відсутній аналізатор, ми створюємо новий індекс (на основі назви, у моєму випадку "більш широковідомий-блог-
Ви побачите це в BulkRequest
ми встановимо Refresh
властивість до true
. Це означає, що після завершення вставки пучки буде оновлено індекс. Це не зовсім необхідно, але це корисно для зневаджування.
Чудовий спосіб перевірити те, що насправді було створено, це перейти до інструментів Dev на OpenSearch Dashboards і виконати пошуковий запит.
GET /mostlylucid-blog-*
{}
Цей запит поверне нам всі індекси, що відповідають шаблону mostlylucid-blog-*
. (Так що всі наші індекси поки що).
{
"mostlylucid-blog-ar": {
"aliases": {},
"mappings": {
"properties": {
"categories": {
"type": "keyword"
},
"content": {
"type": "text",
"analyzer": "arabic"
},
"hash": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"language": {
"type": "text"
},
"lastUpdated": {
"type": "date"
},
"published": {
"type": "date"
},
"slug": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "arabic"
}
}
},
"settings": {
"index": {
"replication": {
"type": "DOCUMENT"
..MANY MORE
Інструменти Dev у OpenSearch Dashboards - це чудовий спосіб перевірити ваші запити, перш ніж вводити їх у ваш код.
Тепер ми можемо почати шукати індекс. Ми можемо використати Search
метод роботи з клієнтом.
Ось тут з'являється справжня сила OpenSearch. Вона має, буквально, Десятки різних типів запитів ви можете використовувати для пошуку ваших даних. Все: від простого пошуку ключових слів до комплексного " експоненціального " пошуку.
public async Task<List<BlogIndexModel>> GetSearchResults(string language, string query, int page = 1, int pageSize = 10)
{
var indexName = GetBlogIndexName(language);
var searchResponse = await client.SearchAsync<BlogIndexModel>(s => s
.Index(indexName) // Match index pattern
.Query(q => q
.Bool(b => b
.Must(m => m
.MultiMatch(mm => mm
.Query(query)
.Fields(f => f
.Field(p => p.Title, boost: 2.0)
.Field(p => p.Categories, boost: 1.5)
.Field(p => p.Content)
)
.Type(TextQueryType.BestFields)
.Fuzziness(Fuzziness.Auto)
)
)
)
)
.Skip((page -1) * pageSize) // Skip the first n results (adjust as needed)
.Size(pageSize) // Limit the number of results (adjust as needed)
);
if(!searchResponse.IsValid)
{
logger.LogError("Failed to search index {IndexName}: {Error}", indexName, searchResponse.DebugInformation);
return new List<BlogIndexModel>();
}
return searchResponse.Documents.ToList();
}
Цей метод GetSearchResults
, розроблено для опитування певного покажчика OpenSearch для отримання дописів блогу. Вона потребує три параметри: language
, query
Параметри і параметри пагінії page
і pageSize
. Ось що він робить:
Вибір індексу:
GetBlogIndexName
Даний метод ґрунтується на поданій мові. Індекс буде динамічно обрано відповідно до мови.Пошук запиту:
Bool
запит за допомогою Must
Термін, щоб переконатися, що результат відповідає певним критеріям.Must
пункт, а MultiMatch
запит використовується для пошуку у декількох полях (Title
, Categories
, і Content
).
Title
поле надається стимуляції 2.0
, роблячи це важливішим у пошуку, і Categories
має посилення 1.5
. Це означає, що документи, у яких буде показано запит щодо пошуку у заголовку або категоріях, будуть вищими.BestFields
, який намагається знайти найкраще відповідне поле для запиту.Fuzziness.Auto
Параметр надає вам змогу приблизно відповідати (наприклад, обробляти малі друкарські копії).Pagination:
Skip
метод пропускає перший n
результати, залежно від номера сторінки, буде обчислено як (page - 1) * pageSize
. Це допомагає робити екскурсію по випущених результатах.Size
метод обмежує кількість повернених документів до вказаного pageSize
.Обробка помилок:
Результат:
BlogIndexModel
документи, що відповідають критеріям пошуку.Отже, ви можете бачити, що ми можемо бути дуже гнучкими щодо пошуку наших даних. Ми можемо шукати окремі поля, збільшувати певні поля, навіть шукати у багатьох індексах.
Одна перевага BIG - це легкий qith, який ми можемо підтримувати у багатьох мовах. У нас є різні індекси для кожної мови і можливість пошуку в межах цього індексу. Це означає, що ми можемо використати правильний аналізатор для кожної мови і отримати найкращі результати.
На відміну від API пошуку, який ми бачили у попередніх частинах цієї серії, ми можемо значно спростити процес пошуку за допомогою OpenSearch. Ми можемо просто написати текст до цього запиту і отримати чудові результати.
[HttpGet]
[Route("osearch/{query}")]
[ValidateAntiForgeryToken]
public async Task<JsonHttpResult<List<SearchResults>>> OpenSearch(string query, string language = MarkdownBaseService.EnglishLanguage)
{
var results = await indexService.GetSearchResults(language, query);
var host = Request.Host.Value;
var output = results.Select(x => new SearchResults(x.Title.Trim(), x.Slug, @Url.ActionLink("Show", "Blog", new{ x.Slug}, protocol:"https", host:host) )).ToList();
return TypedResults.Json(output);
}
Як бачите, ми маємо всі необхідні нам дані в індексі, щоб повернути результати. Потім ми можемо використати це, щоб створити URL для допису блогу. За допомогою цього пункту можна зняти навантаження на нашу базу даних і значно пришвидшити процес пошуку.
У цьому полі ми побачили, як написати клієнт C# для взаємодії з нашим екземпляром OpenSearch.