数据分类
结构化数据和非结构化数据
结构化数据搜索
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.jarlucene-analyzers-common-7.4.0.jarlucene-core-7.4.0.jarlucene-queryparser-7.4.0.jar1 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 @Test14 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 @Test15 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 @Test13 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 @Test13 public void deleteAllDocument() throws Exception {14 indexWriter.deleteAll();15 indexWriter.close();16 17 }18 19 //根据查询条件删除索引20 @Test21 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 @Before12 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 @Test19 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 @Before15 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 @Test21 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 //关闭indexreader46 indexSearcher.getIndexReader().close();47 }48 }
完