好久没写Elasticsearch相关的文了,之前写过2篇关于ES的文章,如下:
xx
xx
今天其实不算是写的ES文章,是有关Lucene的。要研究ES的源码,我们先研究一下ES底层用Lucene的原理,就从Lucene最简单的开始吧。
在ES中,我们要做搜索通常比较简单,部署好ES的服务,采用REST接口的方式和ES server通讯。文档的增删改查都采用ES定义好的领域语言,用JSON的方式传输。这样我们不需要知道ES底层是如何工作的(当然要实现不同搜索需要还是要明白一些term,match等等不同的处理方式)。
这里我们就看下,假如不用Elasticsearch实现搜索,而是直接用Lucene做一个搜索引擎,需要有哪些工作要做。
首先,先来了解一下Lucene中的一些核心概念和关键的API。
1、IndexWriter
lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。
2、Analyzer
分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
3、Directory
索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。
4、Document
文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。
5、Field
类似于数据库中的一个字段,存储了key-value值。
6、IndexSearcher
是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;
7、Query
Query类似关系型数据库中的SQL语句。与关系型数据库类似,Lucene提供了以下的基本查询:精确查询xxx = ? TermQuery、范围查询 xxx BETWEEN? AND ? PointRangeQuery、模糊查询 xxx LIKE '%?%' PrefixQuery、RegexpQuery、组合查询 (...) AND (...) OR (...) BooleanQuery
8、QueryParser
是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。
9、Hits
在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在Lucene中,搜索的结果的集合是用Hits类的实例来表示的。
了解了Lucene的这些概念之后,我们尝试自己动手用Lucene实现一个最简单的搜索功能。这里只做最核心的2个功能:1是将一个文档加入索引,2是用个query词从索引检索文档。其他辅助的功能暂时不管。涉及的核心代码如下(这里我们用的是Lucene ):
一、建立索引
// 注:这里Doc是我们自己定义的一个类, 表示文档,包含id,title,content 3个字段
public void indexDocs(List<Doc> docs, String indexDir) throws Exception {
long startTime = System.currentTimeMillis();//记录索引开始时间
Analyzer analyzer = new StandardAnalyzer();
Directory directory = FSDirectory.open(Paths.get(indexDir));
IndexWriterConfig config = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(directory, config);
for (int i = 0; i < docs.size(); i++) {
Document doc = new Document();
//添加字段
doc.add(new IntField("id", docs.get(i).getId(), Field.Store.YES));
doc.add(new TextField("title", docs.get(i).getTitle(), Field.Store.YES));
doc.add(new TextField("content", docs.get(i).getContent(), Field.Store.YES));
indexWriter.addDocument(doc);
}
indexWriter.commit();
System.out.println("共索引了" + indexWriter.numDocs() + "个文件");
indexWriter.close();
System.out.println("创建索引所用时间:" + (System.currentTimeMillis() - startTime) + "毫秒");
},
这里涉及到上面说的多个Lucene的核心概念:
StandardAnalyzer:比较常用的一种Analyzer
Directory: 索引保存的位置
IndexWriter: 用于将文档加入索引
Document: 文档, 包含多个Field
IntField, TextField:文档的字段
二、搜索索引
public void doSearch(String indexDir, String queryStr) throws Exception {
Directory directory = FSDirectory.open(Paths.get(indexDir));
DirectoryReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser("content", analyzer);
Query query = parser.parse(queryStr);
long startTime = System.currentTimeMillis();
TopDocs docs = searcher.search(query, 10);
System.out.println("查找" + queryStr + "所用时间:" + (System.currentTimeMillis() - startTime));
System.out.println("查询到" + docs.totalHits + "条记录");
//遍历查询结果
for (ScoreDoc scoreDoc : docs.scoreDocs) {
Document doc = searcher.doc(scoreDoc.doc);
String content = doc.get("content");
System.out.println(content);
}
reader.close();
}
这里又出现了几个关键的概念:
IndexSearcher:用于检索一个索引
Query:表示一个检索
QueryParser:利用Analyzer解析一个用户的输入,生成一个Query
ScoreDoc,Hit:表示命中的文档
调用的逻辑比较简单,如下:
private void indexDocs() throws Exception {
Doc doc1 = new Doc(1, "java", "hello java");
Doc doc2 = new Doc(2, "python", "hello python");
Doc doc3 = new Doc(3, "php", "hello php");
List<Doc> docs = new ArrayList<Doc>();
docs.add(doc1);
docs.add(doc2);
docs.add(doc3);
docIndexer.indexDocs(docs, indexDir);
}
private void searchDocs() throws Exception {
System.out.println("==========search 'hello'=============");
docSearcher.doSearch(indexDir, "hello");
System.out.println("==========search 'java'=============");
docSearcher.doSearch(indexDir, "java");
}
至此,一个最简单搜索就实现了,其中涉及了很多Lucene的概念和API,可以看到还是比较容易的,当然这里了只实现了最简单的搜索,要做到ES的分布式、高可靠等特性还是很难的。
后面我们将陆续分析上面代码涉及的各个模块,希望能从底层了解Lucene的工作原理。
下一篇我们将分析Lucene检索中比较核心的Analyzer模块,Analyzer 分词器,实际上就是一个文本的分析过程,或者说是将输入文本转化为文本特征过程。这里我们使用的最简单StandardAnalyzer,实际上Lucene的Analyzer模块有很多实现类,也是关键的扩展点,比如中文常用的IKAnalyzer。
本文涉及到完整代码的github地址如下
git@github.com:guangyuanyu/lucene-demo.git