最近在我的项目中用到了solr,查阅材料晓得solr是基于Lucene实现了。本着刨根问底的精力,来钻研一下Lucene

啥是Lucene?

Lucene是apache下的一个开源的全文本搜索引擎包。他为开发人员提供了一个简略工具包,以不便在指标零碎中实现全文本搜寻的性能
为啥要用Lucene?

有人可能要说了,不就是搜寻吗,我间接用含糊查问在数据库查问它不香吗?

其实在数据量比拟小的时候用sql实现搜寻性能也无大碍,然而当数据量很大的时候数据库的压力就会十分大,而且含糊查问无奈应用索引,所以必须全表查问,具体的毛病如下

  1. SQL只能对数据库表进行搜寻,不能间接针对硬盘上的文件进行搜寻
  2. SQL没有相关度排名
  3. 应用含糊查问的时候是对全表的遍历,效率低下
  4. 当数据库不在本地的时候,查问十分慢

所以很多的我的项目中实现搜寻性能都是应用第三方的搜寻工具,比方solr等。Lucene并不是搜寻服务器,它不能独自的运行,它只是一个工具包,为用户提供一系列的api去调用。而很多的第三方的搜寻工具就是封装扩大了Lucene而实现的,所以学习Lucene还是很有必要滴

在理解Lucene的工作原理之前先谈一下搜寻的外围索引

索引是古代搜索引擎的外围,建设索引的过程就是把源数据处理成十分不便查问的索引文件的过程。为什么索引如此重要,试想一下,如果没有索引,当初你要在大量的文档中搜寻蕴含某个关键字的文档,这时因为没有索引你就必须把这些文档程序读入内存,而后顺次查看每个文档中是否蕴含相应的关键字,这个过程十分耗时,在数据量大,而且高并发的场景下,您就别在短时间内失去后果了。所以这就是建设索引的起因,能够把索引设想成一种数据结构,这种数据结构能够帮忙你疾速随机的拜访存储在索引中的关键词,通过关键词能疾速的定位到相干的文档。

全文本检索

在Lucene的介绍提到了全文本检索,那什么是全文本检索呢?

全文本检索就是提前将指标文档中的词提取进去,组成索引,通过查问索引定位到相干的文档。这种先建设索引再进行查问的过程就是全文本检索

Lucence如何实现全文本检索?

Lucene应用倒排索引实现全文本检索,它保护这一个词的表,对于表中的每一个词语都有一个链表形容了哪些文档蕴含了这个词,这样在用户搜寻的时候,找到关键词后就能够疾速失去相干文档

Lucene实现流程

<img src="D:java资源后端搜寻Lucene.jpg" style="zoom:75%;" />

Lucene实现全文检索的俩大流程:建设索引+搜寻流程

建设索引:采集数据-->构建文档对象(document)-->对文档分词-->建设索引

搜寻流程:创立查问--->从索引库中查问--->渲染搜寻后果

理解一下Lucene中的相干类

Package: org.apache.lucene.document

很显著,这个包就是用来将咱们咱们要检索的数据封装为document对象的,该包下的Filed类用于阐明document的域,也就是document中会有什么属性

Package: org.apache.lucene.analysis

在建设索引之前咱们必须先进行分词操作,这个包就是对文档分词,为建设索引做筹备

Package: org.apache.lucene.index

建设索引,以及对索引进行更新

Package: org.apache.lucene.search

在建设好的索引上进行搜寻所须要的类

入门demo

导入依赖呗

<!--        lucene外围包-->        <dependency>            <groupId>org.apache.lucene</groupId>            <artifactId>lucene-core</artifactId>            <version>${lucene.verseion}</version>        </dependency><!--        lucene分词器,实用于英文-->        <dependency>            <groupId>org.apache.lucene</groupId>            <artifactId>lucene-analyzers-common</artifactId>            <version>${lucene.verseion}</version>        </dependency><!--        lucene中文分词器,实用于中文-->        <dependency>            <groupId>org.apache.lucene</groupId>            <artifactId>lucene-analyzers-smartcn</artifactId>            <version>${lucene.verseion}</version>        </dependency>        <!--对分词索引查问解析-->        <dependency>            <groupId>org.apache.lucene</groupId>            <artifactId>lucene-queryparser</artifactId>            <version>${lucene.verseion}</version>        </dependency>        <!--检索关键字高亮显示-->        <dependency>            <groupId>org.apache.lucene</groupId>            <artifactId>lucene-highlighter</artifactId>            <version>${lucene.verseion}</version>        </dependency>

先简略构建一个entity类用来模仿数据,这里应用了lombok插件

/** * @author sheledon */@Setter@Getter@ToStringpublic class Book {    private Integer id;    private String name;    private Float price;    private String pic;    private String description;}

构建索引

@Testpublic void createIndex() throws IOException {    List<Book> list=new LinkedList<>();    //模仿数据    for (int i=0;i<200;i++){        Book b=new Book();        b.setId(i);        b.setDescription("i  "+i);        b.setName("java"+i*10);        b.setPic("中国"+i*2);        b.setPrice(Float.valueOf(i));        list.add(b);    }    //构建Document对象,将数据存入document对象中    List<Document> documentList=new ArrayList<Document>();    Document document;    for (Book book:list){        document=new Document();        //构建field域,了解这一步有利于solr域的了解        Field id=new StringField("id",book.getId().toString(), Field.Store.YES);        Field name=new TextField("name",book.getName(), Field.Store.YES);        Field price=new FloatDocValuesField("price",book.getPrice());        Field pic = new StoredField("pic", book.getPic());        Field description = new TextField("description",                book.getDescription(), Field.Store.YES);        // 将field域设置到Document对象中        document.add(id);        document.add(name);        document.add(price);        document.add(pic);        document.add(description);        documentList.add(document);    }    //构建分词器,对document进行分词    Analyzer analyzer = new SmartChineseAnalyzer();    //配置IndexWriter    IndexWriterConfig cfg = new IndexWriterConfig(analyzer);    //指定索引库的地址    File indexFile=new File("../");    Directory directory= FSDirectory.open(indexFile.toPath());    //IndexWriter对象,负责对索引的建设,更新    IndexWriter writer=new IndexWriter(directory,cfg);    //分词建设索引,并且增加到索引库中    for (Document doc:documentList){        writer.addDocument(doc);    }    writer.close();}

搜寻流程

@Testpublic void indexSearch() throws ParseException, IOException {    // 创立query对象    // 应用QueryParser搜寻时,须要指定分词器,搜寻时的分词器要和索引时的分词器统一    // 第一个参数:默认搜寻的域的名称    QueryParser parser=new QueryParser("name",new StandardAnalyzer());    // 通过queryParser来创立query对象    // 参数:输出的lucene的查问语句    Query query=parser.parse("name:java");    //指定索引库    File file=new File("../");    Directory directory=FSDirectory.open(file.toPath());    //创立IndexReader    IndexReader reader= DirectoryReader.open(directory);    IndexSearcher searcher=new IndexSearcher(reader);    //通过search在索引库中查问    TopDocs topDocs=searcher.search(query,10);    // 依据查问条件匹配出的记录总数    long count=topDocs.totalHits;    System.out.println("匹配总条数:"+count);    // 依据查问条件匹配出的记录    ScoreDoc[] scoreDocs = topDocs.scoreDocs;    for (ScoreDoc scoreDoc : scoreDocs) {        // 获取文档的ID        int docId = scoreDoc.doc;        // 通过ID获取文档        Document doc = searcher.doc(docId);        System.out.println("商品ID:" + doc.get("id"));        System.out.println("商品名称:" + doc.get("name"));        System.out.println("商品价格:" + doc.get("price"));        System.out.println("商品图片地址:" + doc.get("pic"));        System.out.println("==========================");        // System.out.println("商品形容:" + doc.get("description"));    }    // 敞开资源    reader.close();}
最初

我是不想当码农的小白,平时会写写一些技术博客,举荐优良的程序员博主给大家还有本人遇到的优良的java学习资源,心愿和大家一起提高,独特成长。关注对我真的很重要,老低微了!

公众号: 小白不想当码农