Lucene 是一个高效而强大的搜索引擎,支持 .NET 中的全文搜索。 它使文本搜索变得简单,并为您提供了一组丰富的 API,以实现快速且用户友好的文本搜索技术。
此外,Lucene 没有提供一件事,那就是可伸缩性。 Lucene 应用程序通常将数据写入文件并存储在磁盘上,从而导致大量内存分配。 NCache 提供了一个 分布式 Lucene 使您的 Lucene 应用程序可扩展和分布式的功能。 它还为单点故障问题提供了一种线性可扩展和分布式的替代方案。
NCache 更多信息 分布式Lucene的工作 分布式 Lucene 文档
有关分布式 Lucene 的更多信息,它正在运行,以及为什么它应该成为您的首选选项,请查看此内容 分布式 Lucene:.NET 中的全文搜索以实现可扩展性. 在深入了解分布式 Lucene 的细节之前,让我们浏览一下演示基本工作流程的分布式 Lucene 解决方案。
下面的代码示例显示了一个主要执行这三个步骤的分布式 Lucene:
- 初始化 NCache 目录。
- 索引文档以对其执行搜索。
- 对索引文档执行搜索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
{ // Specify the cache name that is used for Lucene string cache = "LuceneCache"; // Specify the index name to create the indexes string indexName = "ProductIndex"; // Create a directory and open it on the cache and the index path Directory directory = NCacheDirectory.Open(cache, indexName); // Specify the analyzer used to analyze data Analyzer analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48); // Create an indexWriterConfig which holds all the configurations to create an instance of the writer IndexWriterConfig config = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer); // Create the indexWriter with the analyzer and the configuration IndexWriter indexWriter = new IndexWriter(directory, config); // Add the products information that is to be indexed Product[] products = FetchProductsFromDB(); foreach (var prod in products) { // Create a document and add fields to it Document doc = new Document(); doc.Add(new TextField("id", prod.ProductID(), Field.Store.YES)); doc.Add(new TextField("name", prod.ProductName, Field.Store.NO)); doc.Add(new TextField("category", prod.Category, Field.Store.YES)); doc.Add(new TextField("description", prod.Description, Field.Store.YES)); // Writer is created previously indexWriter.AddDocument(doc); // Call ‘Commit’ to save the changes indexWriter.Commit(); } // Open a new reader instance IndexReader reader = indexWriter.GetReader(true); // A searcher is opened to perform searching IndexSearcher indexSearcher = new IndexSearcher(reader); // Specify the searchTerm and the fieldName string searchTerm = "Beverages"; string fieldName = "Category"; LuceneVersion version = LuceneVersion.LUCENE_48; // Create a query parser and parse the query with the parser // Analyzer is whitespace analyzer as specified beforehand QueryParser parser = new QueryParser(version, fieldName, analyzer); Query query = parser.Parse(searchTerm); // Returns the top 10 hits from the result set ScoreDoc[] docsFound = indexSearcher.Search(query, 10).ScoreDocs; // Closes all the files associated with this index reader.Dispose(); } catch(Exception ex) { // Handle Lucene exceptions } |
在这篇博客中,我们将重点介绍分布式 Lucene 进行全文搜索的详细步骤和工作。
NCache 更多信息 分布式Lucene的工作 Lucene 组件和概述
如何迁移到分布式 Lucene?
分布式 Lucene 的工作方式与 Lucene 一样。 使用分布式 Lucene 的一大好处是它为您提供与 Lucene 相同的 API。 作为 Lucene 用户,您可以通过一个单行代码更改的附加组件获得您想要的可扩展性。 你只需要使用 NCache 目录和您的应用程序很好。 文档中列出的分布式 Lucene 中的行为和 API 更改很少 点击此处.
让我们从技术方面仔细看看这些步骤。
第 1 步:连接到 NCache 目录
让我们开始介绍 NCache 在使用 Lucene 的 .NET 应用程序中。 首要步骤是将库中的 Lucene.NET Nuget 包替换为 NCache的 Nuget 包。 NCache 目录,顾名思义,是一个用于存储索引的基类,用于使索引具有可伸缩性。 Lucene.NET 有多个用于此目的的目录实现,但最常用的是 FSDirectory。 因此,第一步是与 NCache 目录。
下面是将您连接到名为 “lucene缓存” 并在所有现有服务器上打开提供的目录,前提是该目录已经存在。 否则,它只会使用提供的名称创建一个新目录。
1 |
var indexDirectory = NCacheDirectory.Open("luceneCache", new DirectoryInfo("lucene-index-path")); |
有了这个, NCache 使用 Distributed Lucene 还需要一小步。 使用任何管理工具,为使用分布式 Lucene 的缓存启用 Lucene 索引。 请参阅 Free Introduction 一章 NCache 有关执行此操作的指南的文档。
设置 Lucene 用于企业搜索的分布式 Lucene 分布式 Lucene 文档
第 2 步:创建分布式 Lucene 索引 NCache
现在, NCache 被合并到您的 Lucene 应用程序中,进入数据写入阶段。 如前所述,Lucene 以索引的形式索引您的记录。 这些索引在节点上单独维护,提供线性可伸缩性。 缓存节点之间的文档分布由自动处理 NCache.
文档由作为键值对的字段组成。 每个字段都包含可供您搜索的文本。 字段构造函数的其他部分包含处理单个字段的说明。
这些文件存储在 NCache Lucene 倒排索引形式的目录。 Lucene 以标记的形式分解文本。 这个分解文本以进行索引的过程是通过使用分析器完成的。 有多种类型的分析器,例如 空白分析器 or 标准分析仪. 这种数据标记化有助于更快的数据搜索,因为分析器的主要目的是去除噪声词并为执行搜索的其余数据编制索引。 有关分析仪的完整知识,请参阅此页面。
使用 IndexWriter 在目录中对文档进行索引。 IndexWriter 负责执行所有对缓存的写操作。 更准确地说,您可以添加其他字段属性以提高搜索效率,例如 FieldStore。 下面的代码彻底解释了在缓存中写入文档(批量),并因此在所有缓存服务器上创建索引。 请确保在每次写入操作后调用 Commit,否则不会记录该操作以执行搜索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public int AddDocuments () { var analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48); var writerConfig = new IndexWriterConfig (LuceneVersion.LUCENE_48, analyzer); var indexWriter = new IndexWriter (indexDirectory, writerConfig); var docCount = 0; // Fetching repository info from data source using (var enumerator = dataProvider.GetProductFTSEnumerator ()) { while (enumerator.MoveNext ()) { docs.Add (enumerator.Current.GetLuceneDocument ()); docCount++; // Perform commit after the chunk of code if (docs.Count == BULK_SIZE) { indexWriter.AddDocuments (docs); //Flush and make it ready for search indexWriter.Commit (); docs.Clear (); //Remove the added documents } } } // Return the count of added documents return docCount; } |
NCache 更多信息 分布式Lucene的工作 分布式 Lucene 的地理空间索引
第 3 步:全文搜索具有可扩展性的数据
使用分布式 Lucene 在缓存中搜索数据的实际任务出现在这一步。 与数据索引类似,读取也在缓存的所有节点上执行,最终合并搜索结果。 这也增加了读取的可扩展性并使搜索速度更快。
与 Lucene 中的 IndexWriter 类似,有一个 索引搜索器 它完成了基于搜索执行数据读取操作的所有繁重工作。 IndexSearcher通过一个分析器,强烈建议使用之前写入数据时使用的同一个分析器进行搜索,否则结果不一致。
Lucene 用于执行搜索的下一件事是 查询. 根据您的需要,Lucene 中有大量内置查询类以及特定于 Lucene 的语法。 这些查询使您的搜索非常高效,例如,有一个通配符查询使用通配符执行搜索并相应地呈现搜索结果。 其他一些查询类型是 TermQuery、BooleanQuery 和 SpanQuery。
搜索结果以命中的形式返回,命中是作为搜索结果返回的文档列表。 然后可以迭代这些 Hits 以获得与查询匹配的实际文档。
下面提供的代码将帮助您见证对 Lucene 索引执行的搜索。 IndexSearcher 使用 IndexReader 获取结果。 查询用于对索引数据执行实际搜索。 QueryParser 根据分析器和搜索词解析用户提供的文本查询,并以 TopDocs 的形式返回结果。
在下面给出的示例中,QueryWrapperFilter 应用于产品的类别,搜索结果按相关性排序。 该查询将模糊性应用于所有术语以提供最佳结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public Tuple<long, List<RepositoryInfo>> Search (string searchTerm, int top = 100, string category = null) { long totalHints = 0; var repoList = new List<ProductFTS> (); // Now create the IndexSearcher instance to search the data // directoryReader is the IndexReader instance which uses the IndexWriter var searcher = new IndexSearcher (directoryReader); try { TopDocs topDocs = null; var queryParser = new MultiFieldQueryParser (Version, Fields, analyzer); var query = new BooleanQuery (); // Split the search term into multiple search words to make fuzzy query string[] terms = searchTerm.Split (new [] { " " }, StringSplitOptions.RemoveEmptyEntries); foreach (string term in terms) query.Add (queryParser.Parse (term.Replace ("~", "") + "~"), Occur.MUST); // remove the duplicate ~, if already exits if (!string.IsNullOrEmpty (category)) { var filter = new QueryWrapperFilter (new TermQuery (new Term (CATEGORY_FIELD, category))); // Performs the search based on the filter applied topDocs = searcher.Search (query, filter, top, Sort.RELEVANCE); } else { topDocs = searcher.Search (query, top, sort : Sort.RELEVANCE); } totalHits = topDocs.TotalHits; repoList = GetSearchedDocs (searcher, topDocs); } return new Tuple<long, List<ProductFTS>> (totalHits, repoList); } |
然后,您可以实现您的业务逻辑以迭代来自命中的搜索结果。
结论
Lucene 是一种高效的搜索引擎,可对您的数据执行全文搜索,但缺乏可扩展性。 NCache 可以与 Lucene 一起使用,只需很少的努力即可使其可扩展。 可扩展的分布式 Lucene 不仅使您的应用程序更快,而且还帮助您应对单点故障的重大挫折。 NCache 可以通过单行代码更改轻松插入您的 .NET 应用程序,因此认为它是可扩展 Lucene 应用程序的最佳选择。