1.需求
2.创建索引
2.1实现步骤
2.2.创建lucene项目
2.3代码实现
3.查询索引
3.1实现步骤
3.2代码实现
4.分析器
4.1标准分析器的分词效果
4.2IKAnalyzer分析器的分词效果
5.索引库的维护
5.1Field域的属性
5.2索引库的添加、删除、修改
6.Lucene索引库查询
6.1TermQuery
6.2数据查询和使用QueryParser查询
项目目录:
1.需求
实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询,并且需要支持多个条件查询。
本案例中的原始内容就是磁盘上的文件,如下图:
2.创建索引
2.1实现步骤
第一步:创建一个java工程,并导入jar包。
第二步:创建一个indexwriter对象。
1)指定索引库的存放位置Directory对象
2)指定一个IndexWriterConfig对象。
第二步:创建document对象。
第三步:创建field对象,将field添加到document对象中。
第四步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。
第五步:关闭IndexWriter对象。
2.2创建lucene项目
2.3代码实现
public class LuceneFirst { //创建索引 @Test public void createIndex() throws Exception { //1.创建一个directory对象,指定索引库保存的位置 //把索引库保存在内存中(不建议这样做) //Directory directory = new RAMDirectory(); //把索引库保存在在磁盘中,指定路径名 Directory directory = FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath()); //2.基于directory对象创建一个indexwriter对象 IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig()); //3.读取磁盘上的文件,对应每个文件创建一个文档对象 File dir = new File("D:\BaiduNetdiskDownload\lucene\02.参考资料\searchsource"); File[] files = dir.listFiles(); for (File f : files) { //取文件名 String filename = f.getName(); //文件路径名 String filepath = f.getPath(); //文件内容 String filecontent = FileUtils.readFileToString(f, "utf-8"); //文件大小 long filesize = FileUtils.sizeOf(f); //创建Field //参数1:域的名称 ,参数2:域的内容 , 参数3:是否存储 Field fieldName = new TextField("name", filename, Field.Store.YES); //Field fieldPath = new TextField("path",filepath,Field.Store.YES); //StoreField仅做存储,其下内容不做分词和索引 Field fieldPath = new StoredField("path", filepath); Field fieldContent = new TextField("content", filecontent, Field.Store.YES); //Field fieldSize = new TextField("size", filesize + "", Field.Store.YES);//+""是为了将long类型数据转成字符串 Field fieldSizeValue = new LongPoint("size",filesize);//LongPoint用作运算,如数据范围查询 Field fieldSizeStore = new StoredField("size",filesize); //创建文档对象 Document document = new Document(); //向文档对象中添加域 document.add(fieldName); document.add(fieldPath); document.add(fieldContent); //document.add(fieldSize); document.add(fieldSizeValue); document.add(fieldSizeStore); //5.把文档对象写入索引库 indexWriter.addDocument(document); } //6.关闭索引库 indexWriter.close(); }
}
D:\IDEA\IdeaProjects\lucene\temp\index为索引库的路径。
注意:new IndexWriterConfig()里面为空时,默认使用标准分词器 StandardAnalyzer,也可以自定义分析器,如:new IndexWriterConfig(new IKAnalyzer());
创建索引完成后,索引库内出现如下文件:
3.查询索引
3.1实现步骤
第一步:创建一个Directory对象,也就是索引库存放的位置。
第二步:创建一个indexReader对象,需要指定Directory对象。
第三步:创建一个indexsearcher对象,需要指定IndexReader对象
第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
第五步:执行查询。
第六步:返回查询结果。遍历查询结果并输出。
第七步:关闭IndexReader对象
3.2代码实现
@Test public void searchIndex() throws Exception { //1.创建一个directory对象,指定索引库的位置 Directory directory = FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath()); //2.创建一个indexReader对象 IndexReader indexReader = DirectoryReader.open(directory); //3.创建一个indexSearcher对象,构造方法中的参数indexReader对象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); //4.创建一个query对象,TermQuery //参数1:指定查询的域 参数2:指定要查找的关键词 //Query query = new TermQuery(new Term("content", "spring")); Query query = new TermQuery(new Term("name", "apache"));
//5.执行查询,得到一个TopDocs对象 //参数1:查询对象 参数2:查询结果返回的最大记录数 TopDocs topDocs = indexSearcher.search(query, 10); //6.取查询结果的总记录数 System.out.println("查询总记录数:" + topDocs.totalHits); //7.取文档列表 ScoreDoc[] scoreDocs = topDocs.scoreDocs; //8.打印文档中的 内容 for (ScoreDoc doc : scoreDocs) { //取文档 int docId = doc.doc; //根据id取文档对象 Document document = indexSearcher.doc(docId); System.out.println(document.get("name")); System.out.println(document.get("path")); System.out.println(document.get("size")); //System.out.println(document.get("content")); System.out.println("------------------寂寞的分割线---------------"); } //9.关闭indexreader对象 indexReader.close(); }
查询结果:
4.分析器
4.1标准分析器的分词效果
@Test public void testTokenStream() throws Exception { //1.创建一个analyzer对象,standardAnalyzer对象 Analyzer analyzer = new StandardAnalyzer();//标准分析器只适用于英文文档的分析 //2.使用分析器对象的tokenStream方法获得一个tokenStream对象 TokenStream tokenStream = analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model."); //3.向tokenStream对象中设置有一个引用,相当于一个指针 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); //4.调用tokenStream对象的reset方法,如果不调用抛异常 tokenStream.reset(); //5.使用while循环遍历tokenStream对象 while (tokenStream.incrementToken()) { System.out.println(charTermAttribute.toString()); } //6.关闭tokenStream对象 tokenStream.close(); }
analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model.");
其中第一个参数添加域名,这里我们不添加,第二个参数:标色语句为待分词的内容。
分词效果为:
4.2IKAnalyzer分析器的分词效果
@Test public void testTokenStream() throws Exception { //1.创建一个analyzer对象,standardAnalyzer对象 //Analyzer analyzer = new StandardAnalyzer();//标准分析器只是用于英文文档的分析 Analyzer analyzer1 = new IKAnalyzer();//该分析器适用于中文分词 //2.使用分析器对象的tokenStream方法获得一个tokenStream对象 //TokenStream tokenStream = analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model."); TokenStream tokenStream = analyzer1.tokenStream("","今天奶奶给我买了我最爱吃的喜之郎果冻"); //3.向tokenStream对象中设置有一个引用,相当于一个指针 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); //4.调用tokenStream对象的reset方法,如果不调用抛异常 tokenStream.reset(); //5.使用while循环遍历tokenStream对象 while (tokenStream.incrementToken()) { System.out.println(charTermAttribute.toString()); } //6.关闭tokenStream对象 tokenStream.close(); }
注意: IKAnalyzer分析器适用于无BOM 的UTF-8 编码文档。也就是说禁止使用windows记事本编辑扩展词典文件。
分词效果:
如果使用标准分析器对中文进行分词,则会变成:
这样是不便于搜索查询的!
5.索引库的维护
5.1Field域的属性
是否分析:是否对域的内容进行分词处理。前提是我们是否要对域的内容进行查询。
是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
是否存储的标准:是否要将内容展示给用户
5.2索引库的添加、删除、修改
代码实现:
public class IndexManager { private IndexWriter indexWriter; @Before public void init() throws Exception{ //创建一个indexWriter对象,需要使用使用IkAnalyzer作为分析器 indexWriter = new IndexWriter(FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath()), new IndexWriterConfig(new IKAnalyzer())); } //添加索引 @Test public void addDocument() throws Exception{ //创建一个document对象 Document document = new Document(); //向document对象中添加域 document.add(new TextField("name","新添加的文件", Field.Store.YES)); document.add(new TextField("content","新添加的文件内容",Field.Store.NO)); //不需要创建索引的就使用StoreField存储 document.add(new StoredField("path","D:\IDEA\IdeaProjects\lucene\temp\index")); //把文档写入索引库 indexWriter.addDocument(document); //关闭索引库 indexWriter.close(); } =================================================
//删除索引 //删除全部索引 @Test public void deleteAllDocument() throws Exception{ //删除全部文档 indexWriter.deleteAll(); //关闭索引库 indexWriter.close(); } //根据查询条件删除索引 @Test public void deleteDocumentByQuery() throws Exception{ indexWriter.deleteDocuments(new Term("name","apache")); indexWriter.close(); } ================================================= //修改索引库,原理就是先删除后添加 @Test public void updateDocument() throws Exception{ //创建一个新的文档对象 Document document = new Document(); //向文档对象中添加域 document.add(new TextField("name","更新后的文档",Field.Store.YES)); document.add(new TextField("name1","更新后的文档1",Field.Store.YES)); document.add(new TextField("name2","更新后的文档2",Field.Store.YES)); //更新操作,先删除再添加 indexWriter.updateDocument(new Term("name","apache"),document); //关闭索引库 indexWriter.close(); } }
6.Lucene索引库查询
对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法,类似关系数据库Sql语法一样Lucene也有自己的查询语法,比如:“name:lucene”表示查询Field的name为“lucene”的文档信息。
可通过两种方法创建查询对象:
1)使用Lucene提供Query子类
2)使用QueryParse解析查询表达式
6.1TermQuery
TermQuery,通过项查询,TermQuery不使用分析器所以建议匹配不分词的Field域查询,比如订单号、分类ID号等。
指定要查询的域和要查询的关键词。
在上文3.2代码实现中有用到TermQuery查询关键词:
6.2数据范围查询 和 使用QueryParser查询
public class SearchIndex { private IndexReader indexReader; private IndexSearcher indexSearcher; @Before public void init() throws Exception{ indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath())); indexSearcher = new IndexSearcher(indexReader); } //数据范围查询 @Test public void testRangeQuery() throws Exception{ //创建一个Query对象,当Field的类为LongPoint时可用于数据范围查询,上文2.3代码实现中就创建了LongPoint类的Field Query query = LongPoint.newRangeQuery("size",0,100); printResult(query); } private void printResult(Query query) throws Exception{ //执行查询 TopDocs topDocs = indexSearcher.search(query,10); System.out.println("总记录数:"+topDocs.totalHits); ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc doc:scoreDocs){ //取文档id int docId = doc.doc; //根据id取文档对象 Document document = indexSearcher.doc(docId); System.out.println(document.get("name")); System.out.println(document.get("path")); System.out.println(document.get("size")); System.out.println("----------------分割线---------------"); } indexReader.close(); } //使用QueryParser查询 @Test public void testQueryParser() throws Exception{ //创建一个queryParser对象,两个参数 //参数1:设置默认搜索的域 参数2:分析器对象 QueryParser queryParser = new QueryParser("name",new IKAnalyzer()); //使用queryParser对象创建一个query对象 Query query = queryParser.parse("lucene是一个Java开发的全文搜索工具包"); //执行查询 printResult(query); } }
使用queryParser时需要注意:
通过QueryParser也可以创建Query,QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。Query对象执行的查询语法可通过System.out.println(query);查询。
需要使用到分析器。建议创建索引时使用的分析器和查询索引时使用的分析器要一致。