day65_Lucene学习笔记

目录 1、Lucene介绍 1.1、什么是lucene 1.2、全文检索的应用场景 1.3、全文检索的定义 2、Lucene实现全文检索的流程 3、Lucene入门程序 3.1、需求 3.2、环境准备 3.3、工程搭建(两步) 3.4、创建索引的流程 3.5、搜索索引的流程 4、Field域 4.1、Field的属性 4.2、Field的常用类型 4.3、修改入门程序Field的代码 5、索引的维护 5.1、需求 5.2、添加索引 5.3、删除索引 5.4、修改索引 6、搜索 6.1、创建查询对象的两种方法 6.2、通过Query的子类创建查询对象 6.3、通过QueryParser创建查询对象 6.4、TopDocs 7、相关度排序 7.1、什么是相关度排序 7.2、相关度打分 7.3、设置boost值影响相关度排序 8、中文分词器 8.1、什么是中文分词器 8.2、Lucene自带的中文分词器 8.3、第三方中文分词器 8.4、使用中文分词器IKAnalyzer 8.5、扩展中文词库 8.6、使用luke测试中文分词效果 课程计划: 1、Lucene介绍 a) 什么是lucene b) 全文检索的应用场景 c) 全文检索定义 2、Luence实现全文检索的流程(重点) 3、Lucene入门程序 4、Field域(重点) 5、索引的维护 a) 添加索引 b) 删除索引 c) 修改索引 6、索引的搜索(重点) a) 通过Query子类创建查询对象 b) 通过QueryParser创建查询对象 7、相关度排序 8、中文分词器(重点) 回到顶部 1、Lucene介绍 1.1、什么是lucene Lucene是Apache的一个全文检索引擎工具包,通过lucene可以让程序员快速开发一个全文检索功能。 引擎:核心组件 工具包:jar包、类库 1.2、全文检索的应用场景 1.2.1、搜索引擎 1.2.2、站内搜索(关注) 1.2.3、文件系统的搜索 1.2.4、总结 Lucene和搜索引擎不是一回事! Lucene是一个工具包,它不能独立运行,不能单独对外提供服务。 搜索引擎可以独立运行对外提供搜索服务。 1.3、全文检索的定义 全文检索首先对要搜索的文档进行分词,然后形成索引,通过查询索引来查询文档。这种先创建索引,然后根据索引来进行搜索的过程,就叫全文检索。 比如:字典 字典的偏旁部首页,就类似于luence的索引。 字典的具体内容,就类似于luence的文档内容。 回到顶部 2、Lucene实现全文检索的流程 详解如下: 全文检索的流程:创建索引流程、搜索索引流程   创建索引流程:采集数据 --> 文档处理存储到索引库中   搜索索引流程:输入查询条件 --> 通过lucene的查询器查询索引 --> 从索引库中取出结果 --> 视图渲染 注意:Lucene本身不能进行视图渲染。 回到顶部 3、Lucene入门程序 3.1、需求 使用Lucene实现电商项目中图书类商品的索引和搜索功能。 3.2、环境准备 Jdk环境:1.7.0_80 Ide环境:Eclipse Oxygen 数据库环境:mysql 5.X Lucene:4.10.3(从4.8版本以后,必须使用jdk1.7及以上) 3.2.1、数据库脚本初始化 内容如下: drop table if exists book; create table book ( id int(11) not null, name varchar(192), privce double, pic varchar(96), description text, primary key (id) ); 3.2.2、Lucene下载安装 如下所示: Lucene是开发全文检索功能的工具包,使用时从官方网站下载,并解压。 官方网站:http://lucene.apache.org/ 目前最新版本:7.5.0 下载地址:http://archive.apache.org/dist/lucene/java/ 下载版本:4.10.3(学习使用版本) JDK要求:1.7以上(从版本4.8开始,不支持1.7以下) 要学习的3个文件夹 3.3、工程搭建(两步) 3.3.1、第一步:创建普通的java工程 3.3.2、第二步:添加jar包 如下所示: 入门程序只需要添加以下jar包:   mysql5.1驱动包:mysql-connector-java-5.1.7-bin.jar   核心包:lucene-core-4.10.3.jar   分析器通用包:lucene-analyzers-common-4.10.3.jar   查询解析器包:lucene-queryparser-4.10.3.jar   junit包(非必须):junit-4.9.jar 3.4、创建索引的流程 对文档索引的过程,就是将用户要搜索的文档内容进行索引,然后把索引存储在索引库(index)中。 3.4.1、为什么要采集数据 详解如下: 为什么要采集数据?   全文检索要搜索的数据信息格式多种多样,拿搜索引擎(百度、google)来说,通过搜索引擎网站能搜索互联网站上的网页(html)、互联网上的音乐(mp3)、视频(avi)、pdf电子书等。对于这种格式不同的数据,需要先将他们采集到本地,然后统一封装到lucene的文档对象中,也就是说需要将存储的内容进行统一后才能对它进行查询。   全文检索搜索的这些数据称为非结构化数据。 什么是非结构化数据?   结构化数据:指具有固定格式或有限长度的数据,如数据库、元数据等。   非结构化数据:指不定长或无固定格式的数据,如邮件、word文档等。 如何对结构化数据进行搜索?   由于结构化数据是固定格式,所以就可以针对固定格式的数据设计算法来搜索,比如数据库like查询,like查询采用顺序扫描法,使用关键字匹配内容,对于内容量大的like查询速度慢。 如何对非结构化数据进行搜索?   需要将所有要搜索的非结构化数据通过技术手段采集到一个固定的地方,将这些非结构化的数据想办法组成结构化的数据,再以一定的算法去搜索。 3.4.2、如何采集数据 详解如下: 采集数据技术有哪些?   1、对于互联网上网页采用http将网页抓取到本地生成html文件。(网页采集:使用爬虫工具(http工具)将网页爬取到本地)   2、如果数据在数据库中就连接数据库读取表中的数据。(数据库采集:使用jdbc程序进行数据采集)   3、如果数据是文件系统中的某个文件,就通过文件系统读取文件的内容。(文件系统采集:使用io流采集) (1)网页采集(了解) 详解如下: 因为目前搜索引擎主要搜索数据的来源是互联网,搜索引擎使用一种爬虫程序抓取网页(通过http抓取html网页信息),以下是一些爬虫项目:   Solr(http://lucene.apache.org/solr),solr 是apache的一个子项目,支持从关系数据库、xml文档中提取原始数据。   Nutch(http://lucene.apache.org/nutch),Nutch 是apache的一个子项目,包括大规模爬虫工具,能够抓取和分辨web网站数据。   jsoup(http://jsoup.org/),jsoup 是一款java的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。   heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一个由java开发的、开源的网络爬虫,用户可以使用它来从网上抓取想要的资源。其最出色之处在于它良好的可扩展性,方便用户实现自己的抓取逻辑。 (2)数据库采集(掌握) 针对电商站内搜索功能,全文检索的数据源在数据库中,需要通过jdbc访问数据库中book表的内容。 po类:Book.java package com.itheima.lucene.po; public class Book { // 图书ID private Integer id; // 图书名称 private String name; // 图书价格 private Float price; // 图书图片 private String pic; // 图书描述 private String description; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } public String getPic() { return pic; } public void setPic(String pic) { this.pic = pic; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } dao:BookDao.java public interface BookDao { // 图书查询 public List queryBookList(); } dao:BookDaoImpl.java public class BookDaoImpl implements BookDao { @Override public List queryBookList() { // 数据库连接 Connection connection = null; // 预编译statement PreparedStatement preparedStatement = null; // 结果集 ResultSet resultSet = null; // 图书列表 List list = new ArrayList(); try { // 加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); // 连接数据库 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/solr", "root", "root"); // SQL语句 String sql = "SELECT * FROM book"; // 创建preparedStatement preparedStatement = connection.prepareStatement(sql); // 获取结果集 resultSet = preparedStatement.executeQuery(); // 结果集解析 while (resultSet.next()) { Book book = new Book(); book.setId(resultSet.getInt("id")); book.setName(resultSet.getString("name")); book.setPrice(resultSet.getFloat("price")); book.setPic(resultSet.getString("pic")); book.setDescription(resultSet.getString("description")); list.add(book); } } catch (Exception e) { e.printStackTrace(); } return list; } 可以进行单元测试一下。这里省略啦! 3.4.3、索引文件的逻辑结构 详解如下: 文档域   文档域存储的信息就是采集到的信息,通过Document对象来存储,具体说是通过Document对象中Field域来存储数据。对于非结构化的数据统一格式为Document文档格式,一个文档有多个Field域,不同的文档其Field的个数可以不同,建议相同类型的文档包括相同的Field。   比如:数据库中一条记录会存储一个一个Document对象,数据库中一列会存储成Document中一个field域。   文档域中,Document对象之间是没有关系的。而且每个Document中的field域也不一定一样。   本例子一个Document对应一 条 Book表的记录。 索引域   用于搜索,搜索程序将从索引域中搜索一个一个词,根据词找到对应的文档。   将Document中的Field的内容进行分词,将分好的词创建索引,索引=Field域名:词。   索引域主要是为了搜索使用的。索引域内容是经过lucene分词之后存储的。 倒排索引表   传统方法是先找到文件,如何在文件中找内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大就搜索慢。   倒排索引结构是根据内容(词语)找文档,倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它是在索引中匹配搜索关键字,由于索引内容量有限并且采用固定优化算法搜索速度很快,找到了索引中的词汇,词汇与文档关联,从而最终找到了文档。 3.4.4、创建索引 创建索引流程图解: 详解如下:   IndexWriter:是索引过程的核心组件,通过IndexWriter可以创建新索引、更新索引、删除索引操作。IndexWriter需要通过Directory对索引进行存储操作。   Directory:描述了索引的存储位置,底层封装了I/O操作,负责对索引进行存储。它是一个抽象类,它的子类常用的包括FSDirectory(在文件系统存储索引)、RAMDirectory(在内存存储索引)。 (1)创建Document 采集数据的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档(Document)中包括一个一个的域(Field)。 (2)分词   在对Docuemnt中的内容创建索引之前需要使用分词器进行分词,主要过程就是分词、过滤两步。   分词:就是将采集到的文档内容切分成一个一个的词,具体应该说是将Document中Field的value值切分成一个一个的词。   过滤:包括去除标点符号、去除停用词(的、是、a、an、the等)、大写转小写、词的形还原(复数形式转成单数形参、过去式转成现在式)等。   什么是停用词?停用词是为节省存储空间和提高搜索效率,搜索引擎在索引页面或处理搜索请求时会自动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。比如语气助词、副词、介词、连接词等,通常自身并无明确的意义,只有将其放入一个完整的句子中才有一定作用,如常见的“的”、“在”、“是”、“啊”等。   示例:   要分词的内容:Lucene is a Java full-text search engine.   分词:     Lucene     is     a     Java     full     -     text     search     engine     .   过滤:   去掉标点符号     Lucene     is     a     Java     full     text     search     engine   去掉停用词     Lucene     Java     full     text     search     engine   大写转小写     lucene     java     full     text     search     engine Lucene作为了一个工具包提供不同国家的分词器,如下图所示: 注意由于语言不同分析器的切分规则也不同,本例子使用StandardAnalyzer,它可以对用英文进行分词。 如下是org.apache.lucene.analysis.standard.standardAnalyzer的部分源码: @Override protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) { final StandardTokenizer src = new StandardTokenizer(getVersion(), reader); src.setMaxTokenLength(maxTokenLength); TokenStream tok = new StandardFilter(getVersion(), src); tok = new LowerCaseFilter(getVersion(), tok); tok = new StopFilter(getVersion(), tok, stopwords); return new TokenStreamComponents(src, tok) { @Override protected void setReader(final Reader reader) throws IOException { src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength); super.setReader(reader); } }; } 详解如下: Tokenizer是分词器,负责将reader转换为语汇单元即进行分词,Lucene提供了很多的分词器,也可以使用第三方的分词,比如:IKAnalyzer是一个中文分词器。 tokenFilter是分词过滤器,负责对语汇单元进行过滤,tokenFilter可以是一个过滤器链儿,Lucene提供了很多的分词器过滤器,比如:大小写转换、去除停用词等。 如下图是语汇单元的生成过程: 从一个Reader字符流开始,创建一个基于Reader的Tokenizer分词器,经过三个TokenFilter生成语汇单元Token。 同一个域中相同的语汇单元(Token)对应同一个Term(词),Term(词)记录了语汇单元的内容及所在域的域名等,还包括了该token出现的频率及位置。 不同的域中拆分出来的相同的单词对应不同的term。 相同的域中拆分出来的相同的单词对应相同的term。 例如:图书信息里面,图书名称中的java和图书描述中的java对应不同的term。 代码实现如下: /** * 将采集到的数据list集合封装到Document对象中,创建索引库库 * @author Bruce * */ public class IndexManager { /** * 创建索引 * @throws Exception */ @Test public void createIndex() throws Exception { // 1、采集数据 BookDao dao = new BookDaoImpl(); List list = dao.queryBookList(); // 2、将采集到的数据list封装到Document对象中 // 先创建Document对象集合 List docList = new ArrayList<>(); Document document = null; // 开发时建议这么做,因为这样每次地址指向是同一片内存,省内存 for (Book book : list) { // 创建Document对象,同时要创建field对象 document = new Document(); Field id = new TextField("id", book.getId().toString(), Store.YES); Field name = new TextField("name", book.getName(), Store.YES); Field price = new TextField("price", book.getPrice().toString(), Store.YES); Field pic = new TextField("pic", book.getPic(), Store.YES); Field description = new TextField("description", book.getDescription(), Store.YES); // 把域(Field)添加到文档(Document)中 document.add(id); document.add(name); document.add(price); document.add(pic); document.add(description); docList.add(document); } // 3、创建分词器对象:标准分词器 Analyzer analyzer = new StandardAnalyzer(); // 4、创建索引写对象:IndexWriter // 指定索引库的地址 File indexFile = new File("E:\\index\\hm19"); // 创建索引目录流对象:Directory Directory directory = FSDirectory.open(indexFile); IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer); IndexWriter indexWriter = new IndexWriter(directory, cfg); // 5、通过索引写对象:IndexWriter,将Document写入到索引库中 for (Document doc : docList) { indexWriter.addDocument(doc); } // 6、关闭索引写对象:IndexWriter indexWriter.close(); } } 3.4.5、使用Luke查看索引 Luke作为Lucene工具包中的一个工具(http://www.getopt.org/luke/),可以通过界面来进行索引文件的查询、修改。 打开Luke方法:   命令运行:cmd运行:java -jar lukeall-4.10.3.jar   手动执行:双击lukeall-4.10.3.jar Luke的界面: 连接索引库成功的界面: 3.5、搜索索引的流程 搜索流程图: 详解如下: 1、Query 用户定义查询语句,用户确定查询什么内容(输入什么关键字)   指定查询语法,相当于sql语句。 2、IndexSearcher 索引搜索对象,定义了很多搜索方法,程序员调用此方法搜索。 3、IndexReader 索引读取对象,它对应的索引维护对象IndexWriter,IndexSearcher通过IndexReader读取索引目录中的索引文件。 4、Directory 索引流对象,IndexReader需要Directory读取索引库,使用FSDirectory文件系统流对象。 5、IndexSearcher 搜索完成,返回一个TopDocs(匹配度高的前边的一些记录)。 3.5.1、输入查询语句 详解如下: 同数据库的sql一样,lucene全文检索也有固定的语法: 最基本的有比如:AND, OR, NOT 等 举个例子,用户想找一个description中包括java关键字和spring关键字的文档。 它对应的查询语句:description:java AND spring 如下是使用Luke搜索的例子: 3.5.2、搜索分词 详解如下: 和索引过程的分词一样,这里要对用户输入的关键字进行分词,一般情况索引和搜索使用的分词器一致。 比如:输入搜索关键字“java培训”,分词后为java和培训两个词,与java和培训有关的内容都搜索出来了,如下: 3.5.3、搜索索引 详解如下: 根据关键字从索引中找到对应的索引信息,即词term。term与document相关联,找到了term就找到了关联的document,从document取出Field中的信息即是要搜索的信息。 代码实现: /** * 搜索索引 * @author Bruce * */ public class IndexSearch { @Test public void searchIndex() throws Exception { // 1、 创建查询对象(Query对象) // 使用QueryParser搜索时,需要指定分词器,搜索索引时使用的分词器要和创建索引时使用的分词器一致 // 创建分析器对象 Analyzer analyzer = new StandardAnalyzer(); QueryParser queryParser = new QueryParser("description", analyzer); // 第一个参数:是默认搜索的域的名称 // 通过QueryParser来创建Query对象 Query query = queryParser.parse("description:java AND spring"); // 参数:输入的是lucene的查询语句(注意:关键字一定要大写) // 2、创建IndexSearcher(索引搜索对象) File indexFile = new File("E:\\index\\hm19\\"); Directory directory = FSDirectory.open(indexFile); IndexReader indexReader = DirectoryReader.open(directory); IndexSearcher indexSearcher = new IndexSearcher(indexReader ); // 3、通过IndexSearcher(索引搜索对象)来搜索索引库 TopDocs topDocs = indexSearcher.search(query, 10); // 第二个参数:指定需要显示的顶部记录的N条 int totalHits = topDocs.totalHits; // 根据查询条件匹配出的记录总数 System.out.println("匹配出的记录总数:" + totalHits); ScoreDoc[] scoreDocs = topDocs.scoreDocs;// 根据查询条件匹配出的记录 for (ScoreDoc scoreDoc : scoreDocs) { int docId = scoreDoc.doc; // 获取文档的ID Document document = indexSearcher.doc(docId); // 通过ID获取文档 System.out.println("商品ID:" + document.get("id")); System.out.println("商品名称:" + document.get("name")); System.out.println("商品价格:" + document.get("price")); System.out.println("商品图片地址:" + document.get("pic")); System.out.println("商品描述:" + document.get("description")); } // 关闭I
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信