前言
提起全文搜索,程序员往往首先想到ElasticSearch,ElasticSearch确实是目前最热门的全文搜索引擎了。
但是ElasticSearch太重量级了,如果你维护的只是一个小项目,数据量很微小(例如个人博客),或者只是某个小场景中需要用到文本相似度匹配,那么因为这么个小功能而去选择接入ElasticSearch,这好比用大炮打蚊子,大材小用,还会额外增加系统的复杂度及运维成本。
这里我介绍一种非常轻量级的方案供参考,该方案不需要接入任何外部系统,非常适合数据量小的场景。如果你是数据量大的场景,还是推荐上ElasticSearch。
ElasticSearch的底层是基于Lucene的,通常情况下,使用Lucene需要建立基于物理存储路径的索引,但是Lucene还提供了基于内存的RAMDirectory索引,使用内存索引,你可以将数据加载到内存中,并在内存中进行搜索和匹配操作,不需要将数据持久化到磁盘上。
使用方法
首先引入相关的maven依赖,这里的版本我强烈建议用低版本4.10.2,我曾拿4.x版本对比过8.x版本,结果是前者明显优于后者,原因不清楚。
4.10.2
org.apache.lucene
lucene-core
${lunece.version}
org.apache.lucene
lucene-queryparser
${lunece.version}
org.apache.lucene
lucene-analyzers-common
${lunece.version}
下面是demo
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
public class LuceneRAMDirectoryExample {
public static void main(String[] args) {
try {
// 创建内存索引目录
RAMDirectory ramDirectory = new RAMDirectory();
// 创建分析器
Analyzer analyzer = new StandardAnalyzer();
// 创建索引写入器配置
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
// 创建索引写入器
IndexWriter indexWriter = new IndexWriter(ramDirectory, indexWriterConfig);
// 创建文档并添加到索引中
Document doc1 = new Document();
doc1.add(new TextField("content", "This is the first document", Field.Store.YES));
indexWriter.addDocument(doc1);
Document doc2 = new Document();
doc2.add(new TextField("content", "This is the second document", Field.Store.YES));
indexWriter.addDocument(doc2);
// 提交索引并关闭写入器
indexWriter.commit();
indexWriter.close();
// 创建索引搜索器
DirectoryReader directoryReader = DirectoryReader.open(ramDirectory);
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
// 创建查询解析器
QueryParser queryParser = new QueryParser("content", analyzer);
// 创建查询
Query query = queryParser.parse("first");
// 执行搜索
TopDocs topDocs = indexSearcher.search(query, 10);
// 遍历搜索结果
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println("Score: " + scoreDoc.score);
System.out.println("Content: " + document.get("content"));
}
// 关闭索引搜索器和内存索引目录
directoryReader.close();
ramDirectory.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的demo中,索引中存在两个文档,
文档一:This is the first document
文档二:This is the second document
搜索关键词为“first”,程序输出结果如下:
Score: 0.625
Content: This is the first document
Lucene会对每个文档基于匹配度进行打分,得分越高,匹配度越高。在实际业务中,可以根据匹配度排序,筛选出前N条作为搜索结果;也可以再进一步优化,按照重要程度给不同的文档做不同的权重,计算加权之后的匹配度。
高谈阔论