Lucene.Net 是什么
Lucene.Net 是一个高性能、全功能的全文检索搜索引擎框架库,它是 Apache Lucene 项目的.NET语言版本。完全使用C#开发,适用于微软.NET环境。Lucene.Net允许开发者在他们的应用程序中实现强大的搜索功能,包括文本分析、索引创建、搜索查询处理、排序和过滤等功能。
以下是一些Lucene.Net的主要特性和功能:
-
文本分析:支持对文本内容进行分词、去除停用词、词干提取等预处理操作,以提高搜索精度。
-
索引构建:能够高效地创建包含文档内容、元数据和其它相关信息的索引。
-
检索能力:提供丰富的查询语法,支持布尔查询、短语查询、模糊查询、通配符查询等多种查询类型。
-
排序和过滤:可以根据多个字段进行排序,并支持各种过滤条件来限制搜索结果。
-
高性能:设计为高度优化和快速,能够在大型数据集上执行快速的搜索操作。
-
扩展性:可以通过插件和模块化设计来扩展和定制功能,例如添加自定义的分词器或查询解析器。
-
多语言支持:包括对多种语言的分词和分析支持。
Lucene.Net已经被广泛应用于各种需要搜索功能的场景,包括企业内部搜索、网站搜索、文档管理系统、数据分析和大数据处理等。尽管上述信息可能基于较早的日期,但Lucene.Net的基本原理和主要功能依然保持其重要性和相关性。随着时间的推移,该项目可能会有新的版本发布和功能更新。
Lucene.Net 能用来做什么
Lucene.Net 主要用于构建和实现高性能的全文搜索功能在.NET应用程序中。以下是一些具体的用途和应用场景:
-
搜索引擎开发:Lucene.Net 可以作为构建自定义搜索引擎的核心组件,为网站、企业内部系统、文档管理系统等提供快速、准确的搜索服务。
-
数据索引和检索:它可以用来对大量文本数据进行索引,使得后续的查询操作能够快速找到相关的信息。这在处理大规模数据集时尤其有用。
-
文档管理和检索:在文档管理系统中,Lucene.Net 可用于索引和搜索各种类型的文档,如 PDF、Word、Excel、HTML 等。
-
日志分析和监控:对于大量的日志数据,Lucene.Net 可以帮助快速搜索和分析特定的日志事件或模式。
-
电子商务搜索:在线购物平台可以使用 Lucene.Net 实现商品搜索,支持按名称、描述、价格、评价等多种条件进行过滤和排序。
-
新闻和内容聚合:新闻网站和内容聚合平台可以利用 Lucene.Net 实现实时的新闻搜索和相关内容推荐。
-
数据分析和大数据处理:在大数据环境中,Lucene.Net 可以与其他工具和技术(如 Hadoop)结合,用于高效地搜索和分析大规模数据集。
-
本地化搜索:由于支持多语言分词和分析,Lucene.Net 也可用于实现对多种语言内容的搜索。
-
知识管理系统:在企业知识管理系统中,Lucene.Net 可以帮助员工快速查找和访问相关的知识文档和资料。
总的来说,任何需要在大量文本数据中进行快速、准确搜索的应用场景,都可能是 Lucene.Net 的用武之地。通过灵活的配置和扩展,开发者可以根据具体需求定制搜索功能,以满足不同项目和业务的需求。
安装
Lucene.Net.Analysis.Cn
是中文分词工具
dotnet add package Lucene.Net --prerelease
dotnet add package Lucene.Net.Analysis.Cn
常见数据类型
以下是在 Lucene.Net 中常见的一些字段类型:
-
TextField:用于存储全文搜索的文本数据。这种类型的字段会被分析器(Analyzer)处理,进行分词、去除停用词等操作。
-
StringField:用于存储不需要进行全文搜索的文本数据,如标题、作者名等。这些字段通常会被当作一个整体进行索引,不进行分词。
-
NumericField:用于存储数值类型的数据,如整数、长整数、浮点数和双精度数。Lucene.Net 提供了不同的子类来处理不同类型的数值。
-
DateField:用于存储日期和时间数据。日期通常被转换为 long 型数值表示,以便于索引和排序。
-
BinaryField:用于存储二进制数据,如图片、音频、视频等非文本内容。实际的二进制数据会被编码为字符串并存储在索引中。
-
StoredField:用于存储需要在搜索结果中返回的任何类型的数据。这些字段不会被索引,但会在检索时从原始文档中获取并返回。
在 Lucene.Net 中,字段的数据类型主要通过使用不同的 Field 类的实例来表示和处理。这些字段类型的选择和使用取决于你的应用程序的具体需求,例如你希望如何搜索和排序这些数据。需要注意的是,Lucene.Net 主要专注于文本搜索,对于复杂的数据类型和结构化数据,可能需要额外的设计和处理。
常见查询方法
以下是一些 Lucene.Net 中常见的查询类型及其示例:
-
TermQuery:
Term term = new Term("fieldName", "searchTerm"); Query query = new TermQuery(term);
这将搜索在 “fieldName” 字段中包含 “searchTerm” 的文档。
-
BooleanQuery:
TermQuery query1 = new TermQuery(new Term("fieldName1", "term1")); TermQuery query2 = new TermQuery(new Term("fieldName2", "term2")); BooleanClause clause1 = new BooleanClause(query1, Occur.MUST); BooleanClause clause2 = new BooleanClause(query2, Occur.MUST); BooleanQuery booleanQuery = new BooleanQuery.Builder() .add(clause1) .add(clause2) .build();
这将搜索在 “fieldName1” 字段中包含 “term1” 且在 “fieldName2” 字段中包含 “term2” 的文档。
-
PhraseQuery:
PhraseQuery phraseQuery = new PhraseQuery(); phraseQuery.add(new Term("fieldName", "term1")); phraseQuery.add(new Term("fieldName", "term2")); // 或者使用带位置的构造方式 PhraseQuery.Builder builder = new PhraseQuery.Builder(); builder.add(new Term("fieldName", "term1"), 0); builder.add(new Term("fieldName", "term2"), 1); PhraseQuery phraseQuery = builder.build();
这将搜索在 “fieldName” 字段中包含短语 “term1 term2”(按顺序)的文档。
-
WildcardQuery:
WildcardQuery wildcardQuery = new WildcardQuery(new Term("fieldName", "term*"));
这将搜索在 “fieldName” 字段中以 “term” 开头的所有文档。
-
PrefixQuery:
PrefixQuery prefixQuery = new PrefixQuery(new Term("fieldName", "prefix"));
这将搜索在 “fieldName” 字段中以 “prefix” 开头的所有文档。
-
FuzzyQuery:
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("fieldName", "fuzzyTerm"), 2);
这将搜索在 “fieldName” 字段中与 “fuzzyTerm” 相似(根据 Levenshtein 距离计算,参数 2 表示最大编辑距离为 2)的文档。
-
RangeQuery:
TermRangeQuery rangeQuery = TermRangeQuery.newStringRange( "fieldName", new BytesRef("lowerBound"), new BytesRef("upperBound"), true, // 是否包括下边界 true); // 是否包括上边界
这将搜索在 “fieldName” 字段中值在 “lowerBound” 和 “upperBound” 范围内的文档(包括边界)。
以上示例展示了如何创建不同类型的查询对象。在实际使用时,通常会将这些查询对象传递给 IndexSearcher
对象的 Search()
方法来执行搜索操作。
高级用法
Lucene.Net 提供了许多高级用法,以下是一些常见的例子:
-
使用 Analyzer 自定义文本分析:
// 创建自定义 Analyzer public class CustomAnalyzer : Analyzer { protected override TokenStreamComponents CreateComponents(String fieldName, TextReader reader) { Tokenizer tokenizer = new StandardTokenizer(LuceneVersion.LUCENE_48, reader); TokenFilter filter = new LowerCaseFilter(LuceneVersion.LUCENE_48, tokenizer); return new TokenStreamComponents(tokenizer, filter); } } // 在创建 IndexWriter 时使用自定义 Analyzer Analyzer analyzer = new CustomAnalyzer(); Directory directory = new RAMDirectory(); IndexWriterConfig config = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer); IndexWriter writer = new IndexWriter(directory, config);
-
使用 MultiFieldQueryParser 进行多字段搜索:
String[] fields = {"title", "content"}; Analyzer analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48); Query query = MultiFieldQueryParser.Parse( LuceneVersion.LUCENE_48, searchText, fields, analyzer);
-
使用 SpanQueries 进行精确的短语和 proximity 搜索:
SpanTermQuery term1 = new SpanTermQuery(new Term("fieldName", "term1")); SpanTermQuery term2 = new SpanTermQuery(new Term("fieldName", "term2")); SpanNearQuery spanQuery = new SpanNearQuery( new SpanQuery[] { term1, term2 }, // 子查询 0, // 距离(0表示相邻) true); // 是否包含边界 Query query = spanQuery;
-
使用 Boost 设置字段和文档的权重:
Document doc = new Document(); Field titleField = new TextField("title", "Document Title", Field.Store.YES); titleField.Boost = 2.0f; // 设置字段权重 doc.Add(titleField); Field contentField = new TextField("content", "Document Content", Field.Store.YES); doc.Add(contentField); IndexWriter writer = new IndexWriter(directory, config); writer.AddDocument(doc);
-
使用 Collector 自定义搜索结果收集:
TopScoreDocCollector collector = TopScoreDocCollector.Create(10, false); // 收集前10个最高分文档 searcher.Search(query, collector); ScoreDoc[] hits = collector.TopDocs().ScoreDocs; foreach (ScoreDoc hit in hits) { Document doc = searcher.Doc(hit.Doc); // 处理搜索结果 }
-
使用 Filter 进行过滤搜索:
Query query = new TermQuery(new Term("fieldName", "term")); Filter filter = new PrefixFilter(new Term("anotherField", "prefix")); IndexSearcher filteredSearcher = new IndexSearcher(reader); filteredSearcher.Similarity = new DefaultSimilarity(); // 可选:设置相似度算法 filteredSearcher.Search(query, filter, collector);
-
使用 Faceting 进行分类统计:
FacetsConfig config = new FacetsConfig(); config.SetHierarchical("category", true); // 设置 category 字段为层级结构 Directory directory = FSDirectory.Open(indexPath); IndexReader reader = DirectoryReader.Open(directory); TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir); IndexSearcher searcher = new IndexSearcher(reader); FacetsCollector fc = new FacetsCollector(); searcher.Search(query, fc); Facets facets = new FastTaxonomyFacetCounts(taxoReader, config, fc); List<FacetResult> results = facets.GetAllDims(10); // 获取前10个最频繁的分类 foreach (FacetResult result in results) { Console.WriteLine(result); }
相关示例代码
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Lucene.Net.Util;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using WebApplication4.Entities;
using LuceneDirectory = Lucene.Net.Store.Directory;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers.Classic;
using Lucene.Net.Search;
using System.Text;
using Lucene.Net.Analysis.Cn.Smart;
namespace WebApplication4.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("initDb")]
public IActionResult InitDb()
{
const LuceneVersion luceneVersion = LuceneVersion.LUCENE_48;
//Open the Directory using a Lucene Directory class
string indexName = "example_index";
string indexPath = Path.Combine(Environment.CurrentDirectory, indexName);
using LuceneDirectory indexDir = FSDirectory.Open(indexPath);
// Create an analyzer to process the text
Analyzer standardAnalyzer = new StandardAnalyzer(luceneVersion);
//Create an index writer
IndexWriterConfig indexConfig = new IndexWriterConfig(luceneVersion, standardAnalyzer);
indexConfig.OpenMode = OpenMode.CREATE_OR_APPEND; // create/overwrite index
IndexWriter writer = new IndexWriter(indexDir, indexConfig);
// 创建文档
var doc1 = new Document();
doc1.Add(new StringField("content", "Mobile Game Virginia Cloud Hosting Server", Field.Store.YES));
writer.AddDocument(doc1);
var doc2 = new Document();
doc2.Add(new StringField("content", "PC Game Virginia Cloud Hosting Server", Field.Store.YES));
writer.AddDocument(doc2);
var doc3 = new Document();
doc3.Add(new StringField("content", "Console Game Virginia Cloud Hosting Server", Field.Store.YES));
writer.AddDocument(doc3);
var doc4 = new Document();
doc4.Add(new StringField("content", "Game Testing Near Me", Field.Store.YES));
writer.AddDocument(doc4);
writer.Commit();
writer.Dispose();
return Ok();
}
[NonAction]
public List<string> GetKeyWords(string q)
{
List<string> keyworkds = new List<string>();
Analyzer analyzer = new SmartChineseAnalyzer(LuceneVersion.LUCENE_48);
using (var ts = analyzer.GetTokenStream(null, q))
{
ts.Reset();
var ct = ts.GetAttribute<Lucene.Net.Analysis.TokenAttributes.ICharTermAttribute>();
while (ts.IncrementToken())
{
StringBuilder keyword = new StringBuilder();
for (int i = 0; i < ct.Length; i++)
{
keyword.Append(ct.Buffer[i]);
}
string item = keyword.ToString();
if (!keyworkds.Contains(item))
{
keyworkds.Add(item);
}
}
}
return keyworkds;
}
public static IndexWriter _writer { get; set; }
const LuceneVersion luceneVersion = LuceneVersion.LUCENE_48;
Analyzer standardAnalyzer = new StandardAnalyzer(luceneVersion);
[NonAction]
public IndexWriter GetWrite()
{
if (_writer != null)
{
return _writer;
}
else
{
string indexName = "example_index";
string indexPath = Path.Combine(Environment.CurrentDirectory, indexName);
LuceneDirectory indexDir = FSDirectory.Open(indexPath);
IndexWriterConfig indexConfig = new IndexWriterConfig(luceneVersion, standardAnalyzer);
indexConfig.OpenMode = OpenMode.CREATE_OR_APPEND; // create/overwrite index
IndexWriter writer = new IndexWriter(indexDir, indexConfig);
_writer = writer;
return writer;
}
}
[HttpGet("keyword")]
public IActionResult KeyWord(string key)
{
using DirectoryReader reader = GetWrite().GetReader(applyAllDeletes: true);
IndexSearcher searcher = new IndexSearcher(reader);
QueryParser parser = new QueryParser(luceneVersion, "content", standardAnalyzer);
var wildcardQuery = new WildcardQuery(new Term("content", $"*{key}*"));
var hits = searcher.Search(wildcardQuery, 100);
var list = new List<string>();
// 遍历搜索结果
foreach (var hit in hits.ScoreDocs)
{
var doc = searcher.Doc(hit.Doc);
list.Add(doc.Get("content"));
}
return Ok(list);
}
}