Hãy tưởng tượng bạn đang xây dựng một hệ thống tìm kiếm sản phẩm cho một trang thương mại điện tử. Bạn bắt đầu với Lucene, nhưng sau một thời gian, hệ thống của bạn cần mở rộng, nhóm của bạn cần thử nghiệm với Solr hay Elasticsearch để tối ưu hiệu suất.
Nếu không sử dụng Dependency Injection (DI), bạn sẽ phải sửa đổi rất nhiều mã nguồn để thay đổi công cụ tìm kiếm. Nhưng với DI, bạn chỉ cần thay đổi một vài dòng cấu hình mà không cần đụng đến logic cốt lõi.
Bài viết này sẽ giúp bạn hiểu cách áp dụng DI để thiết kế một hệ thống tìm kiếm linh hoạt và dễ bảo trì trong .NET, cùng với các ví dụ thực tế sử dụng Lucene, Solr và Elasticsearch.
Dependency Injection (DI) là một kỹ thuật lập trình giúp giảm phụ thuộc giữa các thành phầnbằng cách cung cấp các phụ thuộc từ bên ngoài thay vì tạo chúng bên trong lớp.
Trong hệ thống tìm kiếm, điều này có nghĩa là bạn có thể trừu tượng hóa logic tìm kiếm qua một giao diện (interface), chẳng hạn ISearchService, và triển khai riêng cho Lucene, Solr, hoặc Elasticsearch. Điều này sẽ giúp chúng ta:
✅ Dễ dàng thay đổi công cụ tìm kiếm: Bạn có thể chuyển từ Lucene sang Solr hoặc Elasticsearch chỉ bằng cách thay đổi cấu hình mà không cần chỉnh sửa code lõi.
✅ Dễ kiểm thử: Bạn có thể mock dịch vụ tìm kiếm (ISearchService) để kiểm thử mà không cần kết nối thực tế.
✅ Mã nguồn sạch hơn: Giúp tách biệt trách nhiệm giữa các thành phần, tăng khả năng bảo trì.
❌ Nhược điểm của DI:
Chúng ta sẽ triển khai DI để xây dựng một dịch vụ tìm kiếm bài viết (Article) trong ASP.NET Core với Lucene, Solr và Elasticsearch.
Để đảm bảo tính linh hoạt, chúng ta sẽ trừu tượng hóa logic tìm kiếm thông qua interface ISearchService:
public interface ISearchService
{
void IndexArticle(Article article);
List<Article> SearchArticles(string query);
}
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Lucene.NET là một thư viện tìm kiếm mạnh mẽ dành cho .NET. Trước tiên, cài đặt gói NuGet:
dotnet add package Lucene.Net
Sau đó, tạo dịch vụ LuceneSearchService:
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
public class LuceneSearchService : ISearchService
{
private readonly string _indexPath;
private readonly IndexWriter _writer;
public LuceneSearchService(string indexPath = "lucene_index")
{
_indexPath = indexPath;
var directory = FSDirectory.Open(_indexPath);
var analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48);
_writer = new IndexWriter(directory, new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer));
}
public void IndexArticle(Article article)
{
var doc = new Document
{
new StringField("Id", article.Id.ToString(), Field.Store.YES),
new TextField("Title", article.Title, Field.Store.YES),
new TextField("Content", article.Content, Field.Store.YES)
};
_writer.AddDocument(doc);
_writer.Commit();
}
public List<Article> SearchArticles(string query)
{
var directory = FSDirectory.Open(_indexPath);
var reader = DirectoryReader.Open(directory);
var searcher = new IndexSearcher(reader);
var parser = new MultiFieldQueryParser(LuceneVersion.LUCENE_48, new[] { "Title", "Content" }, new StandardAnalyzer(LuceneVersion.LUCENE_48));
var luceneQuery = parser.Parse(query);
var hits = searcher.Search(luceneQuery, 10).ScoreDocs;
var results = new List<Article>();
foreach (var hit in hits)
{
var doc = searcher.Doc(hit.Doc);
results.Add(new Article
{
Id = int.Parse(doc.Get("Id")),
Title = doc.Get("Title"),
Content = doc.Get("Content")
});
}
reader.Dispose();
return results;
}
}
Để sử dụng DI, hãy đăng ký dịch vụ tìm kiếm trong Program.cs:
builder.Services.AddSingleton<ISearchService>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
return config["SearchEngine"] switch
{
"Lucene" => new LuceneSearchService(),
"Solr" => new SolrSearchService(config),
"Elastic" => new ElasticSearchService(config),
_ => throw new NotSupportedException("Unsupported search engine")
};
});
Chúng ta chưa có SolrSearchService và ElasticSearchService, và ta sẽ tiếp tục triển khai các search service ở bên dưới.
Cài đặt gói SolrNet qua NuGet. Đảm bảo Solr server đã chạy và có core "articles" với schema phù hợp. Lớp Article cần được đánh dấu với thuộc tính Solr:
[SolrDocument("articles")]
public class Article
{
[SolrField("id", true)]
public int Id { get; set; }
[SolrField("title")]
public string Title { get; set; }
[SolrField("content")]
public string Content { get; set; }
}
Sau đó, tạo dịch vụ SolrSearchService
using SolrNet;
public class SolrSearchService : ISearchService
{
private readonly ISolrOperations<Article> _solr;
public SolrSearchService(IConfiguration config)
{
var solrUrl = config["Solr:Url"];
var connection = new SolrConnection(solrUrl);
_solr = connection.GetSolrOperations<Article>();
}
public void IndexArticle(Article article)
{
_solr.Add(article);
_solr.Commit();
}
public List<Article> SearchArticles(string query)
{
var results = _solr.Query(new SolrQuery(query));
return results.ToList();
}
}
Cài đặt gói Nest qua NuGet. Đảm bảo Elasticsearch server đã chạy và index "articles" được tạo.
Triển khai ElasticSearchService
using Nest;
public class ElasticSearchService : ISearchService
{
private readonly ElasticClient _client;
public ElasticSearchService(IConfiguration config)
{
var elasticUrl = config["Elastic:Url"];
var connection = new ConnectionSettings(new Uri(elasticUrl));
_client = new ElasticClient(connection);
}
public void IndexArticle(Article article)
{
_client.Index(article, i => i.Index("articles").Id(article.Id.ToString()));
_client.Refresh("articles");
}
public List<Article> SearchArticles(string query)
{
var response = _client.Search<Article>(s => s
.Index("articles")
.Query(q => q
.MultiMatch(m => m
.Fields(f => f
.Field("title")
.Field("content")
)
.Query(query)
)
)
.Size(10)
);
return response.Documents.ToList();
}
}
Phương án triển khai thực tế
Với Dependency Injection, chúng ta có thể xây dựng một hệ thống tìm kiếm linh hoạt, dễ mở rộng, và dễ kiểm thử. Dù chọn Lucene, Solr hay Elasticsearch, DI giúp bạn dễ dàng chuyển đổi mà không ảnh hưởng đến logic ứng dụng, đồng thời giúp bạn chuyển đổi và mở rộng hệ thống theo từng bài toán triển khai.
Nguồn tham khảo
🚀 Bạn đã từng triển khai hệ thống tìm kiếm chưa? Hãy chia sẻ kinh nghiệm của bạn trong phần bình luận nhé!
#search #Data #SearchEngine #Lucene #Elasticsearch #Solr #FullTextSearch #NETDeveloper #TechBlog
#wecommit100xshare #1PercentBetter
Tôi đi xây dựng Hệ thống Tìm kiếm - bài 4
/Son Do, 1 ông dev thích xây dựng Hệ thống Tìm kiếm
Công nghệ - 19/08/2025 21:13:07
Tìm hiểu cách xây dựng hệ thống phát hiện ngôn ngữ ký hiệu theo thời gian thực bằng AI, sử dụng DETR để tăng cường khả năng tiếp cận và đổi mới. Kết nối lời nói và cử chỉ.
Công nghệ - 18/08/2025 13:38:25
Tối ưu hóa các hệ thống RAG bằng cách tận dụng siêu dữ liệu để truy xuất thông tin chính xác và nhanh chóng hơn, giải quyết các thách thức về dữ liệu dư thừa hoặc lỗi thời với công cụ LangExtract nguồn mở. Khám phá cách LangExtract sử dụng các mô hình ngôn ngữ tiên tiến để trích xuất và cấu trúc siêu dữ liệu, tạo ra một quy trình truy xuất hợp lý và hiệu quả.
Công nghệ - 01/08/2025 07:00:00
Gỡ lỗi LLM rất quan trọng vì quy trình làm việc của chúng phức tạp và liên quan đến nhiều phần như chuỗi, lời nhắc, API, công cụ, trình truy xuất, v.v.
Công nghệ - 19/06/2025 03:05:09
Code xong chạy được là chưa đủ – phải biết khi nào nó "chết" nữa chứ 😅
Bạn đang triển khai ứng dụng trên Kubernetes, Docker hay môi trường production nào? Và bạn từng "toát mồ hôi" vì service chết mà không ai báo?
Công nghệ - 16/07/2025 13:41:17
Công nghệ - 27/06/2025 03:15:44
Công nghệ - 11/12/2025 15:05:29
[Góc chuyện nghề] bán account game để đi học nghệ - bạn dám không?
Làm nghề 20 năm, gặp nhiều sinh viên, nhưng chiều qua tôi khá bất ngờ với một cậu em tên Quang. Em Quang muốn theo nghề BA và mong muốn lương 20 triệu sau khi làm việc 1.5 năm tới 2 năm trong nghề.
Công nghệ - 22/09/2025 08:59:20
Dừng ngay việc dùng DateTime.Now trong APIs, đó là ổ lỗi tiềm ẩn trong hệ thống của bạn
⏱️ Tôi từng nghĩ DateTime.Now là một thứ vô hại, đơn giản và tiện lợi, cho đến khi gặp những vấn đề về múi giờ. Những lỗi "tưởng chừng nhỏ" này lại chính là nguồn cơn của sự thất vọng và tốn kém thời gian cho nhiều đội ngũ phát triển.
Công nghệ - 14/03/2025 04:30:32