数据分类
结构化数据和非结构化数据
结构化数据搜索
sql
非结构化数据查询方法
顺序扫描法
全文检索
lucene实现全文检索的流程
创建索引
对文档索引的过程,将用户要搜索的文档内容进行索引,索引存在索引库中,
获取原始文档
创建文档对象,文档中包括一个一个的域(Field),域中存储内容,可以将磁盘上的一个文件当成一个document, Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容)
每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和 域值都相同)
每个文档都有一个唯一的编号,就是文档id。
分析文档
将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文 档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个 一个的单词
创建索引
对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到 Document(文档)
创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
举例:通过关键字搜索文件
jar文件
commons-io-2.6.jar
IK-Analyzer-1.0-SNAPSHOT.jar
lucene-analyzers-common-7.4.0.jar
lucene-core-7.4.0.jar
lucene-queryparser-7.4.0.jar
1 public class LuenceFirsrt { 2 3 4 /** 5 * 1创建一个Director对象,指定索引库保存的位置 6 * 2.基于Director对象创建一个IndexWrite对象 7 * 3.读取磁盘上文件,对应每个文件创建一个文档对象 8 * 4.向文档对象中添加域 9 * 5.向文档对象中写入索引库 10 * 6.关闭indexwrite对象 11 */ 12 //创建索引库 13 @Test 14 public void createIndex() throws Exception{ 15 Directory directory = FSDirectory.open(new File("D:\lucene\demo\index").toPath()); 16 17 IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig()); 18 19 File dir = new File("D:\lucene\demo\searchsource"); 20 File[] files = dir.listFiles(); 21 22 for(File f :files) { 23 String fileName = f.getName(); 24 String filePath = f.getPath(); 25 26 String fileContent = FileUtils.readFileToString(f, "utf-8"); 27 long fileSize = FileUtils.sizeOf(f); 28 29 Field fieldName = new TextField("name",fileName,Field.Store.YES); 30 31 Field fieldPath = new TextField("path",filePath,Field.Store.YES); 32 33 Field fieldContent = new TextField("content",fileContent,Field.Store.YES); 34 35 Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES); 36 37 Document document = new Document(); 38 document.add(fieldName); 39 document.add(fieldPath); 40 document.add(fieldContent); 41 document.add(fieldSize); 42 43 44 indexWriter.addDocument(document); 45 46 47 } 48 indexWriter.close(); 49 } 50 }
查询索引
1 //查询索引库 2 /** 3 * /** 4 * 1.创建一个Director对象,指定索引库位置 5 * 2.创建一个IndexReader对象 6 * 3.创建一个IndexSearch 对象,构造方法中的参数indexReader对象 7 * 4.创建一个Query对象,TermQuery 8 * 5.执行查询,得到一个TopDocs对象 9 * 6.取查询结果的总记录数 10 * 7.取文档列表 11 * 8.打印文档内容 12 * 9.关闭indexReader对象 13 */ 14 @Test 15 public void searchIndex() throws Exception{ 16 17 18 Directory directory = FSDirectory.open(new File("D:\lucene\demo\index").toPath()); 19 20 IndexReader indexReader = DirectoryReader.open(directory); 21 22 IndexSearcher indexSearcher = new IndexSearcher(indexReader); 23 24 Query query = new TermQuery(new Term("content","spring")); 25 26 TopDocs topDocs = indexSearcher.search(query, 10); 27 28 System.out.println("查询记录总数为"+ topDocs.totalHits); 29 30 ScoreDoc[] scoreDocs = topDocs.scoreDocs; 31 32 for(ScoreDoc doc : scoreDocs) { 33 int docId = doc.doc; 34 Document document = indexSearcher.doc(docId); 35 System.out.println(document.get("name")); 36 System.out.println(document.get("path")); 37 System.out.println(document.get("size")); 38 System.out.println(document.get("content")); 39 System.out.println("----------------------"); 40 } 41 42 indexReader.close(); 43 } 44
查看分词效果
1 @Test 2 public void testTokenStream() throws Exception { 3 /** 4 * 1.创建一个Analyzer对象,StandardAnalyzer 5 * 2.使用分析器对象的tokenStream方法获得tokenStream对象 6 * 3.向TokenStream对象中设置一个引用,相当于数一个指针 7 * 4.调用TokenStream对象的rest方法 8 * 5.使用while循环遍历ToeknStream对象 9 * 6.关闭TokenStream对象 10 */ 11 12 Analyzer analyzer = new StandardAnalyzer(); 13 TokenStream tokenStream = analyzer.tokenStream("", "The Spring Framework provides a comprehensive programming and configuration model."); 14 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); 15 tokenStream.reset(); 16 17 while(tokenStream.incrementToken()) { 18 System.out.println(charTermAttribute.toString()); 19 } 20 21 tokenStream.close(); 22 } 23
查看中文分词器效果
第一步:把jar包添加到工程中
第二步:把配置文件和扩展词典和停用词词典添加到classpath下
注意:hotword.dic和ext_stopword.dic文件的格式为UTF-8
1 public class Ikanalyzer { 2 @Test 3 public void testCHTokenStream() throws Exception { 4 /** 5 * 1.创建一个Analyzer对象,StandardAnalyzer 6 * 2.使用分析器对象的tokenStream方法获得tokenStream对象 7 * 3.向TokenStream对象中设置一个引用,相当于数一个指针 8 * 4.调用TokenStream对象的rest方法 9 * 5.使用while循环遍历ToeknStream对象 10 * 6.关闭TokenStream对象 11 */ 12 13 Analyzer analyzer = new IKAnalyzer(); 14 TokenStream tokenStream = analyzer.tokenStream("", "由于日前美方宣称拟对3000亿美元中国输美商品加征10%关税,严重违背中美两国元首大阪会晤共识"); 15 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); 16 tokenStream.reset(); 17 18 while(tokenStream.incrementToken()) { 19 System.out.println(charTermAttribute.toString()); 20 } 21 22 tokenStream.close(); 23 } 24 25 }
索引库的维护
是否分析,是否索引,是否存储
Field类 |
数据类型 |
Analyzed 是否分析 |
Indexed 是否索引 |
Stored 是否存储 |
说明 |
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等) 是否存储在文档中用Store.YES或Store.NO决定 |
LongPoint(String name, long... point) |
Long型 |
Y |
Y |
N |
可以使用LongPoint、IntPoint等类型存储数值类型的数据。让数值类型可以进行索引。但是不能存储数据,如果想存储数据还需要使用StoredField。 |
StoredField(FieldName, FieldValue) |
重载方法,支持多种类型 |
N |
N |
Y |
这个Field用来构建不同类型Field 不分析,不索引,但要Field存储在文档中 |
TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader)
|
字符串 或 流 |
Y |
Y |
Y或N |
如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略. |
添加文档
1 public class IndexManager { 2 3 4 /** 5 * 添加文档 6 *创建一个indexWriter对象,需要使用ik作为分词器 7 *创建一个Document对象 8 *向document对象添加域 9 *把文档写入索引库 10 *关闭索引库 11 */ 12 @Test 13 public void addDocument() throws Exception{ 14 IndexWriter indexWriter = 15 new IndexWriter(FSDirectory.open(new File("D:\lucene\demo\index").toPath()), 16 new IndexWriterConfig(new IKAnalyzer())); 17 18 Document document = new Document(); 19 document.add(new TextField("name","新添加的文件",Field.Store.YES)); 20 document.add(new TextField("content","新添加的文件内容",Field.Store.NO)); 21 document.add(new StoredField("path","D:\lucene\demo\path")); 22 23 24 indexWriter.addDocument(document); 25 indexWriter.close(); 26 }
删除索引
1 public class Delete { 2 3 private IndexWriter indexWriter; 4 5 @Before 6 public void init() throws Exception { 7 indexWriter = 8 new IndexWriter(FSDirectory.open(new File("D:\lucene\demo\index").toPath()), 9 new IndexWriterConfig(new IKAnalyzer())); 10 } 11 12 @Test 13 public void deleteAllDocument() throws Exception { 14 indexWriter.deleteAll(); 15 indexWriter.close(); 16 17 } 18 19 //根据查询条件删除索引 20 @Test 21 public void deleteDocumentByQuery() throws Exception { 22 indexWriter.deleteDocuments(new Term("name","apache")); 23 indexWriter.close(); 24 } 25 26 }
修改索引
先删除后添加
1 /** 2 *1.创建Document对象 3 *2.向Document中添加域 4 *3.不同的Document中有不同的域,同一个document中有相同的域 5 *4.关闭IndexWriter 6 */ 7 public class Update { 8 9 private IndexWriter indexWriter; 10 11 @Before 12 public void init() throws Exception { 13 indexWriter = 14 new IndexWriter(FSDirectory.open(new File("D:\lucene\demo\index").toPath()), 15 new IndexWriterConfig(new IKAnalyzer())); 16 } 17 18 @Test 19 public void updateDocument() throws Exception { 20 Document document = new Document(); 21 22 document.add(new TextField("name","更新后的文档",Field.Store.YES));; 23 24 indexWriter.updateDocument(new Term("name","spring"),document); 25 26 indexWriter.close(); 27 28 29 } 30 31 }
索引库查询
1)使用Lucene提供Query子类
2)使用QueryParse解析查询表达式
Query
public class testRangeQuery{ private IndexReader indexReader; private IndexSearcher indexSearcher; @Before public void init() throws Exception { indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\lucene\demo\index").toPath())); indexSearcher = new IndexSearcher(indexReader); } @Test public void testRangleQuery() throws Exception { Query query = LongPoint.newRangeQuery("size", 0l, 10000l); TopDocs topDocs = indexSearcher.search(query, 10); System.out.println("总记录数:"+topDocs.totalHits); ScoreDoc[] scoreDocs = topDocs.scoreDocs; for(ScoreDoc doc:scoreDocs) { int docId = doc.doc; 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("----------------------"); } indexReader.close(); } }
QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询
1 /** 2 *1.创建QueryParser对象,两个参数 3 * 参数一,默认搜索域 。 第二 ,分析器对象 4 *2.使用QueryPaser对象创建一个Query对象 5 *3.执行查询 6 * 7 */ 8 public class Paser { 9 10 private IndexReader indexReader; 11 12 private IndexSearcher indexSearcher; 13 14 @Before 15 public void init() throws Exception { 16 indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\lucene\demo\index").toPath())); 17 indexSearcher = new IndexSearcher(indexReader); 18 } 19 20 @Test 21 public void testQueryParser() throws Exception { 22 23 //创建queryparser对象 24 //第一个参数默认搜索的域 25 //第二个参数就是分析器对象 26 QueryParser queryParser = new QueryParser("content", new IKAnalyzer()); 27 Query query = queryParser.parse("Lucene是java开发的"); 28 //执行查询 29 printResult(query, indexSearcher); 30 } 31 32 private void printResult(Query query, IndexSearcher indexSearcher) throws Exception { 33 //执行查询 34 TopDocs topDocs = indexSearcher.search(query, 10); 35 //共查询到的document个数 36 System.out.println("查询结果总数量:" + topDocs.totalHits); 37 //遍历查询结果 38 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { 39 Document document = indexSearcher.doc(scoreDoc.doc); 40 System.out.println(document.get("filename")); 41 System.out.println(document.get("content")); 42 System.out.println(document.get("path")); 43 System.out.println(document.get("size")); 44 } 45 //关闭indexreader 46 indexSearcher.getIndexReader().close(); 47 } 48 }
完