为什么要用全文检索技术?
数据库搜索:
效率低 索引失效“苹果7是个好手机”这种就搜不出来东西格式固定、长度固定、数据类型固定。例如:数据库中的数据
非结构化数据word文档、PDF文档、邮件、html、txt
格式不固定、长度不固定、数据类型不固定。
通常使用sql语句进行查询,简单、速度快。
比如要找内容包含某一个字符串(如spring)的文件,就是一个文档一个文档的看,从头到尾,如果包含,接着看下一个文件,直到扫描完。windows的搜索、杀毒软件的全盘扫描就是这种。相当慢。
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,达到搜索相对较快的目的。这部分从非结构化数据中提取出来然后重新组织的信息,称为索引。
索引:一个为了提高查询速度,创建某种数据结构的集合,如B+tree
这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
虽然创建索引的过程非常耗时,但是一旦建立可以多次使用。全文检索主要处理的是查询。
流程:非结构化–>分词、结构化–>创建索引–>查询数据
搜索引擎
百度、360搜索、谷歌、搜狗
站内搜索
论坛搜索、微博、文章搜索
电商搜索
淘宝、京东搜索
只要有搜索的地方就可以使用全文检索技术。
使用Lucene。是Apache下的开源项目,提供完整的查询引擎和索引引擎,部分文本分析引擎。
就是帮助数据库进行高效获取数据的数据结构,如B+tree
指的是要索引和搜索的内容。包括互联网上的网页、数据库的数据、磁盘的文件等。
搜索引擎:使用爬虫获得原始文档
站内搜索:数据库中的数据
案例:直接使用io流读取磁盘的文件。
对应每个原始文档创建一个Document对象。
每个document对象包含多个域field
域中保存的就是原始文档数据。
域的名称
域的值
每个文档都有一个唯一的编号,就是文档id
eg. 商品,goods.java name field_name=name field_value=“苹果7”
分词的过程。
根据空格进行字符串拆分得到单词列表把单词统一转换为小写(或者大写),使得大小写不敏感。去除标点符号去除停用词(无意义的词,如the,a,and)每个关键词都封装为一个Term对象中。term中包含两部分内容:关键词所在的域;关键词本身。
注意:不同的域中拆分出来的相同的关键词是不同的term。
基于关键词列表来创建一个索引。保存到索引库中。
索引库中:索引、document对象和两者对应关系。
倒排索引,通过词语找文档,这种结构叫倒排索引,采用链表
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。
用户输入查询条件的地方,如:百度搜索框
两部分:要查询的域,要搜索的关键词
根据要查询的关键词到对应的域上进行搜索。
找到关键词,根据关键词找到对应的文档。
用户友好界面展示,如高亮显示、分页处理等。
需要下载lucene
最低要求JDK1.8
创建java工程、
添加jar包
lucene-analyzers-common-7.4.0.jarlucene-core-7.4.0.jarcommons-io.jar步骤
创建一个Director对象,指定索引库的位置创建一个IndexReader对象创建一个IndexSearcher对象,构造方法中的参数indexReader对象创建一个Query对象,TermQuery执行查询,得到一个TopDocs对象取查询结果的总记录数取文档列表打印文档中的内容关闭indexReader对象 @Test public void searchIndex() throws Exception{ //1. 创建一个Director对象,指定索引库的位置 Directory directory = FSDirectory.open(new File("E:\\indexRepo").toPath()); //2. 创建一个IndexReader对象 IndexReader indexReader = DirectoryReader.open(directory); //3. 创建一个IndexSearcher对象,构造方法中的参数indexReader对象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); //4. 创建一个 Query对象,TermQuery Query query = new TermQuery(new Term("content","spring")); //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) { // 取文档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(document.get("content")); System.out.println("----------分割线-------------"); } //9. 关闭indexReader对象 indexReader.close(); }默认使用标准分析器StandardAnalyzer,中文采用IKAnalyzer
使用Analyzer对象的tokenStream方法,返回一个tokenStream对象,词对象包含最终分词结果。
public final TokenStream tokenStream(String fieldName, Reader reader)
实现步骤:
创建一个Analyzer对象,StandardAnalyzer对象使用分析器对象的tokenStream方法获得一个tokenStream对象向token对象中设置一个引用,相当于一个指针调用tokenStream对象的reset方法,重置指针,如果不调用抛出异常使用while循环遍历tokenStream对象关闭tokenStream对象注意:扩展词典严禁使用windows记事本编辑,保证扩展词典的编辑格式是utf-8
扩展词典:添加一些新词
停用词词典:无意义的词或者敏感词汇
@Test// 添加停用词和常用词没有生效 public void testTokenStream() throws Exception{ // 1. 创建一个Analyzer对象,StandardAnalyzer对象 // Analyzer analyzer = new StandardAnalyzer(); Analyzer analyzer = new IKAnalyzer(); // 2. 使用分析器对象的tokenStream方法获得一个tokenStream对象 TokenStream tokenStream = analyzer.tokenStream("","2017年12月14日 - 传智播客Lucene概述公安局Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作"); // 3. 向token对象中设置一个引用,相当于一个指针 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(); }是否分析:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。
是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
path不需要分词,不需要索引,StoredField
身份证号不需要分词,StringField
Field fieldName = new TextField("name", fileName, Field.Store.YES); //Field fieldPath = new TextField("path", filePath, Field.Store.YES); Field fieldPath = new StoredField("path", filePath);// 不需要分词和索引,只存储 Field fieldContent = new TextField("content", fileContent, Field.Store.YES); //Field fieldSize = new TextField("size", fileSize + "", Field.Store.YES); // 文件大小查询时需要比较,必须LongPoint,但是也要取出来,StoredField Field fieldSizeValue = new LongPoint("size", fileSize);// 可以做运算,查询,不能存储 Field fieldSizeStore = new StoredField("size", fileSize);// 存储删除全部
根据查询、关键词删除文档
修改的原理是先删除再添加
范围查询
使用query的子类 TermQueryqueryParser进行查询