目录
1. Lucene 简介
1.1 什么是 Lucene
Lucene 是一个全文搜索框架,不是现成的应用产品。是一全文检索的工具包,不是应用,只是个类库,完成了全文检索的功能。就是把数据拆分,它提供工具让你能实现类似百度或Google的搜索功能,但并不像这些产品那样开箱即用。
官网: https://lucenenet.apache.org/
1.2 Lucene 能做什么
Lucene 的核心功能简单来说就是:给它一些字符串,它能提供一个全文搜索服务,告诉你搜索关键词出现在哪些地方。基于此,你可以:
- 索引站内新闻,建立资料库
- 索引数据库表的字段,避免使用"%like%"导致的锁表问题
- 开发自己的搜索引擎
1.3 选择 Lucene.Net的理由
a、全文搜索能力强
- 支持复杂查询(模糊匹配、通配符、短语搜索、范围查询等)
- 高性能索引和检索(尤其适合大规模文本数据)
- 支持多种语言分词(中文需搭配如 Paoding、IKAnalyzer 等分词器)
b、适用场景
- 站内搜索(论坛、博客、CMS)
- 文档/日志分析(基于内容的快速检索)
- 数据库替代
LIKE '%keyword%'
(避免全表扫描) - 自定义搜索引擎(比如企业内部知识库)
c、性能优秀
- 纯 .NET 实现,内存管理优化
- 索引 增量更新(避免全量重建)
- 测试数据:
✅ 250万条记录(300MB文本)→ 索引380MB,800并发下平均 300ms
✅ 3.7万条记录(2个varchar字段)→ 索引2.6MB,800并发下 1.5ms
d、社区与生态环境
- Lucene 是 Apache 顶级项目,稳定性高
- .NET 社区有 Elasticsearch / Solr(基于 Lucene)可扩展方案
- NuGet 可安装:Lucene.Net
2. Lucene 的工作方式
2.1 写入流程
- 源字符串经过Analyzer处理(分词、去除stopword)
- 信息添加到Document的各个Field中
- 将需要索引的Field索引,需要存储的Field存储
- 索引写入存储器(内存或磁盘)
2.2 读出流程
- 用户搜索关键词经过Analyzer处理
- 搜索索引找出对应Document
- 从Document提取所需Field
3. 核心概念
3.1 Analyzer (分析器)
将字符串按规则划分成词语并去除无效词(如英文"of/the"、中文"的/地")。例如:
WhitespaceAnalyzer
:按空白字符分词StopAnalyzer
:添加stopword过滤StandardAnalyzer
:最常用的分析器
3.2 Document (文档)
用户提供的一条条记录(文本/字符串/数据库记录等)经过索引后以Document形式存储。
3.3 Field (域)
Document可包含多个信息域(如文章的"标题"、"正文"、"修改时间")。Field有两个重要属性:
属性 | 说明 |
---|---|
存储 | 控制是否存储Field内容 |
索引 | 控制是否对Field建立索引 |
示例组合:
- 标题域:存储=YES,索引=YES(可搜索并直接显示)
- 正文域:存储=NO,索引=YES(可搜索但需从文件读取内容)
- 时间域:存储=YES,索引=NO(可直接显示但不用于搜索)
3.4 Term
搜索的最小单位,表示文档中的一个词语,由两部分组成:
- 词语本身
- 该词语出现的Field
3.5 Token
Term的一次出现实例,包含:
- Term文本
- 起止偏移位置
- 类型字符串
3.6 Segment
索引时Document先写入小文件(Segment),再合并成大索引文件。
4. Lucene 结构
包名 | 功能描述 |
---|---|
analysis | 内建分析器 |
document | 文档数据结构 |
index | 索引读写类 |
queryParser | 查询语句解析 |
search | 搜索结果相关类 |
store | 索引存储类 |
util | 公共工具类 |
5. 如何建立索引
5.1 基本索引操作
// Ensures index backward compatibility
const LuceneVersion AppLuceneVersion = LuceneVersion.LUCENE_48;
// Construct a machine-independent path for the index
var basePath = Environment.GetFolderPath(
Environment.SpecialFolder.CommonApplicationData);
var indexPath = Path.Combine(basePath, "index");
using var dir = FSDirectory.Open(indexPath);
// Create an analyzer to process the text
var analyzer = new StandardAnalyzer(AppLuceneVersion);
// Create an index writer
var indexConfig = new IndexWriterConfig(AppLuceneVersion, analyzer);
using var writer = new IndexWriter(dir, indexConfig);
5.2 内存索引
Directory dir = new RAMDirectory();
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
Document doc = new Document();
doc.Add(new Field("title", "Lucene introduction", Field.Store.YES, Field.Index.TOKENIZED));
writer.AddDocument(doc);
writer.Optimize();
writer.Close();
5.3 索引文本文件
Field field = new Field("content", new FileReader(file));
6. 索引维护
6.1 删除索引
Directory dir = FSDirectory.GetDirectory(PATH, false);
IndexReader reader = IndexReader.Open(dir);
Term term = new Term(field, key);
reader.DeleteDocument(term);
reader.Close();
6.2 更新索引
实际上是先删除旧Document再添加新Document:
// 先删除
IndexReader reader = IndexReader.Open(dir);
Term term = new Term("title", "Lucene introduction");
reader.DeleteDocument(term);
reader.Close();
// 再添加
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
Document doc = new Document();
doc.Add(new Field("title", "Lucene introduction", Field.Store.YES, Field.Index.TOKENIZED));
writer.AddDocument(doc);
writer.Optimize();
writer.Close();
7. 搜索功能
7.1 各种 Query 类型
查询类型 | 用途 | 示例 |
---|---|---|
TermQuery | 精确词查询 | new TermQuery(new Term("content", "lucene")) |
BooleanQuery | 布尔组合查询 | 使用SHOULD 表示OR,MUST 表示AND |
WildcardQuery | 通配符查询 | new WildcardQuery(new Term("content", "use*")) |
PhraseQuery | 短语邻近查询 | 设置Slop 控制词间距 |
PrefixQuery | 前缀查询 | new PrefixQuery(new Term("content", "中")) |
FuzzyQuery | 模糊查询 | 使用Levenshtein算法 |
RangeQuery | 范围查询 | new RangeQuery(new Term("time","20060101"), new Term("time","20060130"), true) |
7.2 QueryParser
使用类似SQL的查询语法:
content:lucene
- TermQuerycontent:java content:perl
- BooleanQuery ORcontent:use*
- WildcardQuerycontent:"中日"~5
- PhraseQuery中*
- PrefixQuerycontent:wuzza~
- FuzzyQuerytime:[20060101 TO 20060130]
- RangeQuery
复合查询示例:
QueryParser parser = new QueryParser("content", new StandardAnalyzer());
Query query = parser.Parse("+(title:lucene content:lucene) +time:[20060101 TO 20060130]");
IndexSearcher searcher = new IndexSearcher(dir);
Hits hits = searcher.Search(query);
7.3 Filter
限制查询的索引子集,常用有:
RangeFilter
:限定搜索范围QueryFilter
:在上次结果中搜索
注意:Filter执行预处理而非结果过滤,可能显著增加查询时间。
8、文章总结
Lucene.Net适合项目需要轻量、高性能的全文搜索,不希望引入外部依赖(如 Elasticsearch)。数据规模单机可支持(百万级)。如果需要 分布式搜索(直接用 Elasticsearch)。现有的轮子已经很多了,该项目适合需要详细了解搜索引擎底层实现的人进行学习。
引用:lucene.NET详细使用与优化详解 - 咸鱼公子 - 博客园
从零开始搭建.NET Core版搜索引擎(一)--Lucene基础概念及示例_netcore 开源搜索引擎 lucene-CSDN博客