lucene
Lucene介绍及核心概念
什么是Lucene
Lucene是一套用于全文检索和搜索的开放源代码程序库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程序接口,能够做全文索引和搜索,在Java开发环境里Lucene是一个成熟的免费开放源代码工具;就其本身而论,Lucene是现在并且是这几年,最受欢迎的免费Java信息检索程序库。
lucene可以做什么
Lucene允许你向自己的应用程序中添加搜索功能。Lucene能够把你从文本中解析出来的数据进行索引和搜索。Lucene并不关心你的数据来源,格式,甚至不关心数据的语种。
Lucene的优点
- Lucene作为一个全文检索引擎,其具有如下突出的优点:
(1)索引文件格式独立于应用平台。Lucene定义了一套以8位字节为基础的索引文件格式,使得兼容系统或者不同平台的应用能够共享建立的索引文件。
(2)在传统全文检索引擎的倒排索引的基础上,实现了分块索引,能够针对新的文件建立小文件索引,提升索引速度。然后通过与原有索引的合并,达到优化的目的。
(3)优秀的面向对象的系统架构,使得对于Lucene扩展的学习难度降低,方便扩充新功能。
(4)设计了独立于语言和文件格式的文本分析接口,索引器通过接受Token流完成索引文件的创立,用户扩展新的语言和文件格式,只需要实现文本分析的接口。
(5)已经默认实现了一套强大的查询引擎,用户无需自己编写代码即使系统可获得强大的查询能力,Lucene的查询实现中默认实现了布尔操作、模糊查询(Fuzzy Search[11])、分组查询等等。
Lucene相关名词解释
1、IndexWriter
lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。
2、Analyzer
分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
3、Directory
索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。
4、Document
文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。
5、Field
类似于数据库中的一个字段,存储了key-value值。
6、IndexSearcher
是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;
7、Query
Query类似关系型数据库中的SQL语句。与关系型数据库类似,Lucene提供了以下的基本查询:精确查询xxx = ? TermQuery、范围查询 xxx BETWEEN? AND ? PointRangeQuery、模糊查询 xxx LIKE '%?%' PrefixQuery、RegexpQuery、组合查询 (...) AND (...) OR (...) BooleanQuery
8、QueryParser
是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。
9、Hits
在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在Lucene中,搜索的结果的集合是用Hits类的实例来表示的。
什么是正向索引、什么是倒排索引?
正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。
在搜索引擎中每个文件都对应一个文件ID,文件内容被表示为一系列关键词的集合(实际上在搜索引擎索引库中,关键词也已经转换为关键词ID)。例如“文档1”经过分词,提取了20个关键词,每个关键词都会记录它在文档中的出现次数和出现位置,得到正向索引的结构如下:
doc1 ---> word1:出现次数,出现位置列表;单词2:出现次数,出现位置列表...
doc2 ---> word1:出现次数,出现位置列表;单词2:出现次数,出现位置列表...
当用户在主页上搜索关键词“苹果手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“苹果手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。
所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词,得到倒排索引的结构如下:
word1 ---> doc3、doc2、doc7、doc6.....
word2 ---> doc1、doc2、doc5、doc6.....
lucene java客户端
1/** 2 * <p> 3 * 4 * @author leone 5 * @since: 2018-11-23 6 **/ 7public class Book { 8 9 private int bookId; 10 11 private String name; 12 13 private float price; 14 15 private String picture; 16 17 private String description; 18 19 public Book() { 20 } 21 22 public Book(int bookId, String name, float price, String picture, String description) { 23 this.bookId = bookId; 24 this.name = name; 25 this.price = price; 26 this.picture = picture; 27 this.description = description; 28 } 29 30 public int getBookId() { 31 return bookId; 32 } 33 34 public void setBookId(int bookId) { 35 this.bookId = bookId; 36 } 37 38 public String getName() { 39 return name; 40 } 41 42 public void setName(String name) { 43 this.name = name; 44 } 45 46 public float getPrice() { 47 return price; 48 } 49 50 public void setPrice(float price) { 51 this.price = price; 52 } 53 54 public String getPicture() { 55 return picture; 56 } 57 58 public void setPicture(String picture) { 59 this.picture = picture; 60 } 61 62 public String getDescription() { 63 return description; 64 } 65 66 public void setDescription(String description) { 67 this.description = description; 68 } 69} 70 71 72 73package com.andy.lucene; 74 75import org.apache.commons.io.FileUtils; 76import org.apache.lucene.analysis.Analyzer; 77import org.apache.lucene.analysis.standard.StandardAnalyzer; 78import org.apache.lucene.document.*; 79import org.apache.lucene.index.DirectoryReader; 80import org.apache.lucene.index.IndexReader; 81import org.apache.lucene.index.IndexWriter; 82import org.apache.lucene.index.IndexWriterConfig; 83import org.apache.lucene.queryparser.classic.QueryParser; 84import org.apache.lucene.search.IndexSearcher; 85import org.apache.lucene.search.Query; 86import org.apache.lucene.search.ScoreDoc; 87import org.apache.lucene.search.TopDocs; 88import org.apache.lucene.store.Directory; 89import org.apache.lucene.store.FSDirectory; 90import org.slf4j.Logger; 91import org.slf4j.LoggerFactory; 92 93import java.io.File; 94import java.io.IOException; 95import java.nio.charset.StandardCharsets; 96import java.nio.file.FileSystems; 97import java.nio.file.Path; 98import java.util.ArrayList; 99import java.util.HashMap; 100import java.util.List; 101import java.util.Map; 102 103/** 104 * <p> 105 * 106 * @author leone 107 * @since: 2018-11-23 108 **/ 109public class BookMain { 110 111 private static final Logger logger = LoggerFactory.getLogger(BookMain.class); 112 113 private static List<Book> books = new ArrayList<>(); 114 115 private static String docPath = "E:\\tmp\\lucene\\docDir"; 116 private static String indexPath = "E:\\tmp\\lucene\\indexDir"; 117 118 static { 119 books.add(new Book(1, "Java核心编程思想", 23, "http://www.taobao.com/image.jpg", "Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程")); 120 books.add(new Book(2, "mysql数据库设计", 47, "http://www.taobao.com/image.jpg", "MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。")); 121 books.add(new Book(3, "JavaWeb进阶", 75, "http://www.jd.com/image.jpg", "Java Web,是用Java技术来解决相关web互联网领域的技术总和。web包括:web服务器和web客户端两部分。Java在客户端的应用有java applet,不过使用得很少,Java在服务器端的应用非常的丰富,比如Servlet,JSP和第三方框架等等。Java技术对Web领域的发展注入了强大的动力。")); 122 books.add(new Book(4, "spring入门", 38, "http://www.tianmao.com/image.jpg", "Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架")); 123 books.add(new Book(5, "springCloud微服务实战", 42, "http://www.yamaxun.com/image.jpg", "Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。")); 124 books.add(new Book(5, "JavaScript高级", 71, "http://www.tianmao.com/image.jpg", "JavaScript 是属于网络的脚本语言 JavaScript 被数百万计的网页用来改进设计、验证表单、检测浏览器、创建cookies,以及更多的应用。")); 125 } 126 127 public static void main(String[] args) throws Exception { 128 129 Map<String, String> doc = new HashMap<>(); 130 doc.put("文件名称", "fileName"); 131 doc.put("文件大小", "fileSize"); 132 doc.put("文件路径", "filePath"); 133 doc.put("文件内容", "fileContent"); 134 135 Map<String, String> book = new HashMap<>(); 136 book.put("商品ID", "book_id"); 137 book.put("商品名称", "name"); 138 book.put("商品价格", "price"); 139 book.put("商品图片地址", "picture"); 140 book.put("商品描述", "description"); 141 142 long start = System.currentTimeMillis(); 143// createIndex(); 144// indexSearch("description:spring"); 145 logger.info("一共花费了:{}毫秒!", (System.currentTimeMillis() - start)); 146 } 147 148 private static void createIndex() throws IOException { 149 // 将采集到的数据封装到Document对象中 150 List<Document> docList = new ArrayList<>(); 151 Document document; 152 for (Book book : books) { 153 document = new Document(); 154 // store:如果是yes,则说明存储到文档域中 155 156 // 图书ID 157 Field bookId = new TextField("book_id", Integer.toString(book.getBookId()), Field.Store.YES); 158 // 图书名称 159 Field name = new TextField("name", book.getName(), Field.Store.YES); 160 // 图书价格 161 Field price = new TextField("price", Float.toString(book.getPrice()), Field.Store.YES); 162 // 图书图片地址 163 Field picture = new TextField("picture", book.getPicture(), Field.Store.YES); 164 // 图书描述 165 Field description = new TextField("description", book.getDescription(), Field.Store.YES); 166 // 将field域设置到Document对象中 167 document.add(bookId); 168 document.add(name); 169 document.add(price); 170 document.add(picture); 171 document.add(description); 172 docList.add(document); 173 } 174 // 创建分词器,标准分词器 175 Analyzer analyzer = new StandardAnalyzer(); 176 // 创建IndexWriter 177 IndexWriterConfig cfg = new IndexWriterConfig(analyzer); 178 // 指定索引库的地址 179 Directory directory = FSDirectory.open(FileSystems.getDefault().getPath(indexPath)); 180 // 创建索引writer 181 IndexWriter writer = new IndexWriter(directory, cfg); 182 // 清除以前的index 183 writer.deleteAll(); 184 // 通过IndexWriter对象将Document写入到索引库中 185 for (Document doc : docList) { 186 writer.addDocument(doc); 187 } 188 // 关闭writer 189 writer.close(); 190 } 191 192 193 /** 194 * @param query 195 * @throws IOException 196 */ 197 private static void searchDocument(Query query, Map<String, String> map) throws IOException { 198 // 1.创建DirectoryJDK 1.7以后 open只能接收Path 199 Directory directory = FSDirectory.open(FileSystems.getDefault().getPath(indexPath)); 200 IndexReader reader = DirectoryReader.open(directory); 201 IndexSearcher searcher = new IndexSearcher(reader); 202 // 通过searcher来搜索索引库,第二个参数指定需要显示的顶部记录的N条 203 TopDocs topDocs = searcher.search(query, 10); 204 205 // 根据查询条件匹配出的记录总数 206 int count = topDocs.totalHits; 207 208 System.out.println("匹配出的记录总数:[ " + count + " ]\n=========================="); 209 // 根据查询条件匹配出的记录 210 ScoreDoc[] scoreDocs = topDocs.scoreDocs; 211 for (ScoreDoc scoreDoc : scoreDocs) { 212 // 获取文档的ID 213 int docId = scoreDoc.doc; 214 // 通过ID获取文档 215 Document doc = searcher.doc(docId); 216 for (Map.Entry<String, String> entry : map.entrySet()) { 217 System.out.println(entry.getKey() + ":" + doc.get(entry.getValue())); 218 } 219 System.out.println("=========================="); 220 } 221 // 关闭资源 222 reader.close(); 223 224 } 225 226 public static void indexSearch(String name, Map<String, String> map) throws Exception { 227 // 创建query对象 228 Analyzer analyzer = new StandardAnalyzer(); 229 // 使用QueryParser搜索时,需要指定分词器,搜索时的分词器要和索引时的分词器一致,第一个参数:默认搜索的域的名称 230 QueryParser parser = new QueryParser("description", analyzer); 231 // 参数:输入的lucene的查询语句(关键字一定要大写) 232 Query query = parser.parse(name); 233 searchDocument(query, map); 234 } 235 236 237}