Lucene是方便用户来搜索数据的工具,当数据量很大时,使用Lucene来为数据创建索引后,在相关的域中进行查询可以大大的缩短时间,这很好的运用了倒排索引的思想。倒排索引简单且粗糙的描述就是将用户的输入信息进行分词处理,得到一个个的词,然后使用这些词分别去匹配相关的数据文档,而非如传统一般的将输入的字段信息全部组合起来一起去匹配信息文档,倒排索引关键就是将输入的一段输入信息分开进行了处理,最后得到分值最高也就是各个词匹配之后最契合的文档数据。
1.导入jar包
<!-- 引入Lucene全文搜索的jar包 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>8.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>8.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>8.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>8.8.1</version>
</dependency>
<!-- 分词器,中文分词,这个对中文较为ok,当然使用Lucene的标准分词器也行 -->
<dependency>
<groupId>com.jianggujin</groupId>
<artifactId>IKAnalyzer-lucene</artifactId>
<version>8.0.0</version>
</dependency>
2.创建索引
/* 这个便是一个最基本的创建索引的方法 */
public static void createIndex() throws IOException {
//1.创建索引库位置对象
Path path = Paths.get("D:\\LuceneSource\\index");
//2.打开索引库
FSDirectory directory = FSDirectory.open(path);
//3.创建一个分词器的对象
Analyzer analyser = new IKAnalyzer();
//4.创建写索引的对象配置
IndexWriterConfig config = new IndexWriterConfig(analyser);
//5.创建写索引对象
IndexWriter writer = new IndexWriter(directory,config);
//6.获得文件,这里也可以用数据库的数据来代替也ok,当然文件内容,就直接一个个获取即可
File file = new File("D:\\LuceneSource\\searchsourceTwo");
File[] files = file.listFiles();
for(File f:files)
{
String fileName = f.getName();
String filePath = f.getPath();
String content = FileUtils.readFileToString(f);
long size = FileUtils.sizeOf(f);
//创建一个文档对象,多个文件每次都进行创建
Document doc = new Document();
//StringField不分词,直接存了文件名称
//使用TextField可以分词,如文件名称为 “逆天的操作是一个神秘人” 会分为:
// 逆天,操作,一个,神秘,人,神秘人;等等分词
//需要知道现在的版本操作int,long使用的为LongPoint IntPoint
doc.add(new IntPoint("size",size); //这里即建立了size的索引
doc.add(new StoredField("size",size)); //这里即保存了 size 信息
doc.add(new TextField("fileName",fileName,Field.Store.YES));
doc.add(new StringField("filePath",filePath,Field.Store.YES));
doc.add(new TextField("content",content,Field.Store.NO));
//把文档doc写入索引库
writer.addDocument(doc);
}
writer.commit(); //提交事务
writer.close(); //关闭
}
3.简单搜索过程
/* 方法的返回值自己规定就好,如封装到List中也ok */
public static void searchFile() throws ParseException, IOException,
InvalidTokenOffsetsException {
//1.创建分词器,对搜索的关键词进行分词使用,且分词器与创建索引时的保持一致
Analyzer analyser = new IKAnalyzer();
//2.创建查询对象
// 单个域中查询数据
//QueryParser queryParser = new
QueryParser("content",analyser); //在content域中查找
//多域中查找关键字,在fileName域中查询和content域中查询,
QueryParser queryParser = new MultiFieldQueryParser(new String[]
{"fileName","content"},analyser);
//3.设置搜索关键词
Query query = queryParser.parse("不让江山");
//4.创建Directory目录对象,指定索引库的位置
Directory dir = FSDirectory.open(Paths.get("D:\\LuceneSource\\index"));
//5.创建输入流对象
IndexReader indexReader = DirectoryReader.open(dir);
//6.创建搜索对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//7.搜索,并返回结果,10条数据即可
TopDocs topDocs = indexSearcher.search(query,10);
//进行高亮显示的代码:如下:这个其实也就是对应着html的语句,以便在网页上显示
Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
Scorer scorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(formatter,scorer);
//获取查询到的结果集总数
System.out.println("查询到的结果集总数: "+topDocs.totalHits);
//8.获取结果集
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//9.遍历结果集
if(scoreDocs!=null)
{
for(ScoreDoc scoreDoc : scoreDocs)
{ //获取查询到的文档的唯一标识,文档id,这个id是Lucene在创建文档的时候自动分配的
int docId = scoreDoc.doc;
//通过文档id,读取文档
Document doc = indexSearcher.doc(docId);
System.out.println("======================");
//1.关键词添加高亮
String fileName =
highlighter.getBestFragment(analyser,
"fileName",doc.get("fileName"));
//2.可以将关键字封装到相关集合中,如封装这个fileName到一个类中,然后添加到
// List中,比较这些数据都是需要用的
System.out.println("=======fileName==" + doc.get("fileName"));
System.out.println("=======filePath==" + doc.get("filePath"));
System.out.println("=======content===" + doc.get("content"));
System.out.println("得分:" + scoreDoc.score);
}
}
}
4.数值搜索过程
//单独的数值范围查询,将简单搜索过程的 query 换一下,即使用如下:
//表示在 2020到2022的这个数值范围内搜索数据
Query query = IntPoint.newRangeQuery("establishYear",2020,2022);
5.数值范围和其他文本域的组合查询,因为有时候,我们会想要搜索,如:在2020年到2022年这个区间内的 名称fileName 为 “Lucene的教程” 这样的需求
Query query1 = IntPoint.newRangeQuery("establishYear",2020,2022);
//QueryParser queryParser = new QueryParser("fileName",analyser);
QueryParser queryParser = new
MultiFieldQueryParser(new String[]
{"fileName"},analyser);
Query query2 = queryParser.parse(searchKey);
//这里便是其中关键了
//创建布尔查询对象(组合查询对象),and,or,not
//Occur.Must相当于and,条件必须满足
//Occur.SHOULD 类似于 or,满足其一
//Occur.MUST_NOT 类似 非,也就是 查询的数据 不能包含它,
// 如选择了时间区间为2022,则就不能查询2022的数据
//都是MUST_NOT则不能查询出数据
//和哪些sql语句的设置条件查询类似
BooleanQuery.Builder query = new BooleanQuery.Builder();
query.add(query1,Occur.MUST);
query.add(query2,Occur.MUST);
//之后的代码和之前的简单搜索过程的也就差不多了,唯一需要注意的 query.build()
TopDocs topDocs = indexSearcher.search(query.build(),10);
//......
6.最后提一下,高亮,关键字使用了highlighter后,便都加上了相应的 html语句,如<font color='red'>大学</font> ,在 vue中使用 v-html 即可解析该<font>得到红色的大学 ,其他的都差不多找到解析的操作即可!
**********************************************************
若需要实现更加具体的文本搜索,则使用如下:
QueryParser queryParser2 = new QueryParser("filetype",analyser); Query query4 = queryParser2.parse(fileType); QueryParser queryParser3 = new QueryParser("depart_name",analyser); Query query5 = queryParser3.parse(depart_Name); //这样即可使用在fileType域和depart_name文本域中进行查找更为准确和精准的信息 query.add(query4,Occur.MUST); query.add(query5,Occur.MUST);