30.03.2024 tarihinde yayınlandı
7 dakikalık okuma
Elasticsearch, java programlama dili ile apache lucane altyapısını kullanan bir search enginedir. Verileri JSON formatında tutan bir NoSQL’dir. RestfulAPI üzerinden çalıştığı için tüm programlama dilleriyle kullanılabilir. Blog sitemizdeki arama özelliği için bu teknoloji kullanılmıştır. Elasticsearch uygulaması docker ile ayağa kaldırılmıştır. Elasticsearch mimarisi, yetenekleri, temel bileşenleri (index , document , node , cluster , shard , replica , text analyzing , tokenization , normalization vb.) gibi konuları detaylandırmak çok fazla vakit alacaktır. Bu yüzden kodlama tarafını paylaşacağım.
Program.cs’de servislere eklemek için ufak bir eklenti hazırlıyoruz. Bu eklentide uygulamanın elasticsearch’e bağlantısını yapıyoruz.
namespace Elasticsearch.Extensions
{
public static class ElasticsearchExtension
{
public static void AddElastic(this IServiceCollection services,IConfiguration configuration)
{
string userName = configuration.GetSection("Elastic")["UserName"];
string password = configuration.GetSection("Elastic")["Password"];
var settings = new ElasticsearchClientSettings(new Uri(configuration.GetSection("Elastic")["Url"]!)).Authentication(new BasicAuthentication(userName, password));
var client = new ElasticsearchClient(settings);
services.AddSingleton(client); //Elastic Search singleton olarak kullanmanızı tavsiye ediyor.
}
}
}
Sonrasında Program.cs’ye servislerimizi ekliyoruz
builder.Services.AddElastic(builder.Configuration);
builder.Services.AddScoped<ArticleRepository>();
builder.Services.AddScoped<IElasticsearch, ElasticsearchService>();
Sonrasında veri yapımız için uygun modeli oluşturuyoruz.
namespace Elasticsearch.Models
{
public class ES_Article
{
[JsonPropertyName("_id")]
public string Id { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("content")]
public string Content { get; set; }
[JsonPropertyName("tags")]
public string Tags { get; set; }
[JsonPropertyName("created")]
public string Created { get; set; } //Format must "dd.MM.yyyy HH:mm:ss"
[JsonPropertyName("updated")]
public string Updated { get; set; } //Format must "dd.MM.yyyy HH:mm:ss"
}
}
Sonrasında makale verileri için insert, update, delete işlemlerinin elasticsearch tarafındaki kodu yazıyoruz. Sonrasında ise bizim asıl kullanacağımız özellik olan full text search metodunu hazırlıyoruz.
namespace Elasticsearch.Repositories
{
public class ArticleRepository
{
private readonly ElasticsearchClient _client;
private const string indexName = "blog";
public ArticleRepository(ElasticsearchClient client)
{
_client = client;
}
public bool Save(ES_Article article, int articleId)
{
article.Created = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss");
var response = _client.Index(article, x => x.Index(indexName).Id(articleId));
return response.IsValidResponse;
}
public bool Update(ES_Article article, int articleId)
{
article.Updated = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss");
var response = _client.Update<ES_Article, ES_Article>(indexName, articleId, x => x.Doc(article));
return response.IsValidResponse;
}
public bool Delete(int articleId)
{
var response = _client.Delete<ES_Article>(articleId, x => x.Index(indexName));
return response.IsValidResponse;
}
public async Task<bool> SaveAsync(ES_Article article, int articleId)
{
article.Created = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss");
var response = await _client.IndexAsync(article, x => x.Index(indexName).Id(articleId));
return response.IsValidResponse;
}
public async Task<bool> UpdateAsync(ES_Article article, int articleId)
{
article.Updated = DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss");
var response = await _client.UpdateAsync<ES_Article, ES_Article>(indexName, articleId, x => x.Doc(article));
return response.IsValidResponse;
}
public async Task<bool> DeleteAsync(int articleId)
{
var response = await _client.DeleteAsync<ES_Article>(articleId, x => x.Index(indexName));
return response.IsValidResponse;
}
public async Task<(List<ES_Article> list, long totalCount)> SearchAsync(string searchText, int page, int pageSize)
{
List<Action<QueryDescriptor<ES_Article>>> ListQuery = new();
Action<QueryDescriptor<ES_Article>> matchAll = (q) => q.MatchAll();
Action<QueryDescriptor<ES_Article>> matchContent = (q) => q.Match(m => m
.Field(f => f.Content)
.Fuzziness(new Fuzziness(1))
.Query(searchText));
Action<QueryDescriptor<ES_Article>> contentMatchBoolPrefix = (q) => q.MatchBoolPrefix(m => m
.Field(f => f.Content)
.Fuzziness(new Fuzziness(1))
.Query(searchText));
Action<QueryDescriptor<ES_Article>> matchTitle = (q) => q.Match(m => m
.Field(f => f.Title)
.Fuzziness(new Fuzziness(1))
.Query(searchText));
Action<QueryDescriptor<ES_Article>> titleMatchBoolPrefix = (q) => q.MatchBoolPrefix(m => m
.Field(f => f.Title)
.Fuzziness(new Fuzziness(1))
.Query(searchText));
if (string.IsNullOrEmpty(searchText))
{
ListQuery.Add(matchAll);
}
else
{
ListQuery.Add(matchContent);
ListQuery.Add(contentMatchBoolPrefix);
ListQuery.Add(matchTitle);
ListQuery.Add(titleMatchBoolPrefix);
}
var pageFrom = (page - 1) * pageSize;
var result = await _client.SearchAsync<ES_Article>(s => s.Index(indexName)
.Size(pageSize).From(pageFrom)
.Query(q => q
.Bool(b => b
.Should(ListQuery.ToArray()))));
if (!result.IsValidResponse) return new(new List<ES_Article>(), 0);
foreach (var hit in result.Hits) hit.Source.Id = hit.Id;
return (list: result.Documents.ToList(), result.Total);
}
}
}
SearchAsync metodunda aynı zamanda pagination işlemini de gerçekleştiriyoruz.
İlgili metotlar servisler aracılığı ile MVC uygulamamız tarafından çağırılmaktadır.
public async Task<IActionResult> IndexAsync(int page = 1, int pageSize = 5, string searchText = "")
{
ViewBag.Page = page;
ViewBag.FirstPage = false;
ViewBag.LastPage = false;
ViewBag.SearchText = searchText;
var articles = await _elasticsearch.SearchAsync(searchText, page, pageSize);
long totalCount = articles.totalCount;
if (page == 1)
{
ViewBag.FirstPage = true;
}
if (page * pageSize >= totalCount)
{
ViewBag.LastPage = true;
}
return View(articles.list);
}
Denemek için tıklayın
Daha fazla göster