以前使用数据库精确搜索,但是其使用(%和like)导致其速度慢、功能不强、性能低;所以我们今天来学习Lucene相关的搜索引擎。

Lucene是当前非常流行的、免费的Java信息搜索(IR)库。Lucene是Apache下一个著名的开源搜索引擎内核,它基于Java技术,可以处理索引、拼写检查、点击高亮和其他分词等技术。Lucene并不关心数据源、数据格式、甚至数据的语言,Lucene更不会关心搜索的界面会是怎样,因为这些工作都是由基于Lucene的程序来完成,因此Lucene使用于几乎所有以文本内容为主的数据检索功能。

Solr

Lucene主要实现索引和查询的核心功能及API,但并不是一个完整的产品,而Solr是一个独立的​企业级搜索​​应用服务器,它对外提供类似于Web-service的API接口,用户可以通过Http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引。并且提供灵活的Schema配置、多核心支持、面搜索(Faceted Search)等。同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的​全文搜索引擎​。Solr 主要特性有:强大的全文检索功能,高亮显示检索结果,动态集群,数据库接口和电子文档(Word ,PDF 等)的处理。而且Solr 具有高度的可扩展,支持分布搜索和索引的复制。

Nutch

Nutch和Solr原来都是Lucene下的子项目。但后来Nutch独立成为独立项目。Nutch是2004年由俄勒冈州立大学开源实验室模仿Google搜索引擎创立的开源搜索引擎,后归于Apache旗下。Nutch 是一个开源​Java​​实现的​搜索引擎​​。它提供了我们运行自己的搜索引擎所需的全部工具。包括​全文搜索​​和Web​爬虫​。

Hadoop

Hadoop 是一个开源的可运行于大规模集群上的分布式并行编程框架,由于分布式存储对于分布式编程来说是必不可少的,这个框架中还包含了一个分布式文件系统HDFS( Hadoop Distributed File System )。Hadoop原来是Nutch下的分布式任务子项目,现在也成为Apache下的顶级项目。Nutch可以利用Hadoop进行分布式多任务抓取和分析存储工作。Doug Cutting 把 Nutch 中的分布式文件系统以及实现 MapReduce 算法的代码独立出来形成了一个新的开源项 Hadoop。Nutch 也演化为基于 Lucene 全文检索以及 Hadoop 分布式计算平台的一个开源搜索引擎Hadoop 实现了 Google 的 MapReduce 编程模型,提供了简单易用的编程接口,也提供了它自己的分布式文件系统 HDFS,与 Google 不同的是,Hadoop 是开源的,任何人都可以使用这个框架来进行并行编程。如果说分布式并行编程的难度足以让普通程序员望而生畏的话,开源的 Hadoop 的出现极大的降低了它的门槛。

略微了解Lucene相关技术,现在让我们新建一个maven项目,运用一下!!!

pom.xml

<!--核心包-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>5.3.1</version>
</dependency>
<!--一般分词器,适用于英文分词-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>5.3.1</version>
</dependency>
<!--中文分词器-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>5.3.1</version>
</dependency>
<!--对分词索引查询解析-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>5.3.1</version>
</dependency>
<!--检索关键字高亮显示-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>5.3.1</version>
</dependency>

Indexer.java

package com.test;

import java.nio.file.Paths;

import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class Indexer {
//从数据库来看,这就是一个表的三个字段ids,citys,descs
private Integer ids[]={1,2,3};
private String citys[]={"《从你的全世界路过》","《子不语》","《平凡的世界》"};
private String descs[]={
"不想从你的全世界路过。",
"子不语。",
"平凡的世界。"
};
//创建一个文件夹,也就是索引
private Directory dir;
//写索引
private IndexWriter getWriter()throws Exception{
//中文分词器
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
IndexWriterConfig iwc=new IndexWriterConfig(analyzer);
IndexWriter writer=new IndexWriter(dir, iwc);
return writer;
}

private void index(String indexDir)throws Exception{
dir=FSDirectory.open(Paths.get(indexDir));
//写索引
IndexWriter writer=getWriter();
for(int i=0;i<ids.length;i++){
Document doc=new Document();
doc.add(new IntField("id", ids[i], Field.Store.YES));
doc.add(new StringField("city",citys[i],Field.Store.YES));
doc.add(new TextField("desc", descs[i], Field.Store.YES));
writer.addDocument(doc);
}
writer.close();
}


public static void main(String[] args) throws Exception {
new Indexer().index("D:\\Lucene");

System.out.println("Success Indexer");

}

}

ReaderByIndexerTest.java

package com.test;

import java.io.StringReader;
import java.nio.file.Paths;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class ReaderByIndexerTest {
public static void search(String indexDir, String par) throws Exception{
//得到读取索引文件的路径
Directory dir = FSDirectory.open(Paths.get(indexDir));
//通过dir得到的路径下的所有的文件
IndexReader reader = DirectoryReader.open(dir);
//建立索引查询器
IndexSearcher searcher = new IndexSearcher(reader);
//中文分词器
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
//建立查询解析器
/**
* 第一个参数是要查询的字段;
* 第二个参数是分析器Analyzer
* */
QueryParser parser = new QueryParser("desc", analyzer);
//根据传进来的par查找
Query query = parser.parse(par);
//计算索引开始时间
long start = System.currentTimeMillis();
//开始查询
/**
* 第一个参数是通过传过来的参数来查找得到的query;
* 第二个参数是要出查询的行数
* */
TopDocs topDocs = searcher.search(query, 10);
//索引结束时间
long end = System.currentTimeMillis();
System.out.println("匹配"+par+",总共花费了"+(end-start)+"毫秒,共查到"+topDocs.totalHits+"条记录。");
//高亮显示start
//算分
QueryScorer scorer=new QueryScorer(query);
//显示得分高的片段
Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
//设置标签内部关键字的颜色
//第一个参数:标签的前半部分;第二个参数:标签的后半部分。
SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");
//第一个参数是对查到的结果进行实例化;第二个是片段得分(显示得分高的片段,即摘要)
Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
//设置片段
highlighter.setTextFragmenter(fragmenter);
//高亮显示end
//遍历topDocs
/**
* ScoreDoc:是代表一个结果的相关度得分与文档编号等信息的对象。
* scoreDocs:代表文件的数组
*
* @throws Exceptio
* */
for(ScoreDoc scoreDoc : topDocs.scoreDocs){
//获取文档
Document document = searcher.doc(scoreDoc.doc);
//输出全路径
System.out.println(document.get("city"));
System.out.println(document.get("desc"));
String desc = document.get("desc");
if(desc!=null){
//把全部得分高的摘要给显示出来
//第一个参数是对哪个参数进行设置;第二个是以流的方式读入
TokenStream tokenStream=analyzer.tokenStream("desc", new StringReader(desc));
//获取最高的片段
System.out.println(highlighter.getBestFragment(tokenStream, desc));
}
}
reader.close(); }
//开始测试
public static void main(String[] args) {
//索引指定的路径
String indexDir = "D:\\Lucene";
//查询的字段
String par = "平凡之路";
try {
search(indexDir,par);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}



}