lucene全文检索入门

欢迎使用lucene

	非结构化数据顺序扫描很慢,对结构化数据的搜索却相对较快(由于结构化数据有一定的结构可以采取一定的搜索算法加快速度),那么把我们的非结构化数据想办法弄得有一定结构不就行了吗?关系数据库中存储的都是结构化数据,因此很检索都比较快。

从非结构化数据中提取出数据的然后重新组织成结构化数据的信息,我们称之索引。即为文本数据建立类似字典目录,从而提高检索速度。

全文检索的特点:

(1)快的很.
(2)相关度最高的排在最前面,官网中相关的网页排在最前面;(百度搜索)
(3)对摘要进行了截取;(截取核心思想如论文摘要)
(4)关键词的高亮。
(5)只关注文本,不考虑语义。(不考虑搜索的文本的含义) 

使用场景:

替换数据库模糊查询,提高查询速度
1)数据库缺点
全文检索也是一种搜索,我们之前也学过了数据库也支持搜索,如果这两种搜索用途一致,那就没必要学习全文检索了。下面就来把全文检索和数据库来做一个对比,简述下数据库搜索的弊端。
有这样一句SQL:Select * from article where content like ‘%java%’,执行以后会有如下结果: Select * from article where content like ‘java%’
根据我们写的SQL中使用的like可以知道,这种方式在搜索时不会索引。在数据库中复制上面的SQL,在SQL前添加explain命令,代表解释这句SQL,可以看到:

这是一个全表查询,在数据库中使用like查询的字段,会按照内容顺序,逐字扫描。需要的内容越是靠后,查询的时间久越长。 ----查询速度慢
不光包含java单词的数据会被查询出来,如果内容中有JavaScript这个单词的同样也会被查询出来。如果刚好100条结果99条都包含JavaScript,只有1条是我们真正要的结果。面对这么多无用的数据,用户肯定会非常无奈。 ----搜索效果不好
100条结果中,全字匹配的,和一字相差,多字相差的结果完全是混乱排列,不能把最符合的放在前面。在数据库中排序主要是按照时间,数字大小,对于内容的排序可能就开发者也摸不准是怎样的规则。 ----没有相关度排序

2) 具体说明
关系数据库中进行模糊查询时,数据库自带的全文索引将不起作用,此时需要通过全文检索来提高速度。
比如:网站系统中针对内容的模糊查询;
select * from article where content like ‘%源代码%’
ERP系统中产品等数据的模糊查询,BBS、BLOG中的文章搜索等;

全文索引是搜索引擎的基础
各种搜索引擎运行依赖于全文检索,百度谷歌的界面看着很简单,技术主要体现在后台全文检索技术的实现上,他们自己都有基于全文检索做了实现,这些技术肯定不是开源的,不然他们在整个是市场也没啥优势了。我们要学习的Lucene是开源的,目前使用最广的全文检索工具包;
只对“指定领域”的网站进行索引与搜索(即垂直搜索)
垂直领域:
如“818工作搜索”、“有道购物搜索”,拉钩网(只是针对专业的互联网搜索)
比如我们过来学习,需要找房子吧。为了解决这个问题我们首先会想到什么?到58同城等网站里搜索。那找工作呢?这类网站非常多了,有中华英才网、智联招聘,51job(前程无忧),那么要在这些网站中找工作,分别得在各个网站中进行搜索,同时我们还得在每个网站中维护简历,简直太麻烦了。而垂直检索就是为了解决这类问题的,如下面的818工作网:

这个网站界面很像百度,最上面是搜索关键字,只是右边多了个地区,在最左边列出了月薪、职位名称、公司名称、公司类型等信息都是专门针对职位招聘这个领域信息的高级查询,可以快速的帮我们筛选需要查找的内容。网站的正文中展示了职位的名称、部分简介、来源时间和来源网站等。由此可以看出这里汇聚了各大招聘网站的信息,一次搜索可以查到多个网站的信息,节约了大量时间。网上买东西除了物流速度以外就是价格了,同样商品在不同商家那的价格也是不同的,如果价比价,要在目前这个信息海洋中挑选最实惠的商品,真得费不少力气。而有道购物搜索,也就是现在的惠惠网这类垂直检索网站就解决了这类问题。这类网站也是近年才兴起,势头良好.

要在word、pdf等各种各样的数据格式中检索内容
特别是pdf,能够支持搜索的主要还是指内容是文字的。搜索到关键字以后大多会以黄底或者蓝底高亮显示。 比如在ctrl+F 搜索文字
其它场合:比如搜狐拼音输入法、Google输入法等
我们常用的输入法也是使用的全文检索,输入法对词库中的词都建立了目录,我们通过拼音甚至拼音首字母都可以快速找到需要的词。
全文检索应用架构

Lucene核心原理:

	收集数据,写入索引;用户输入关键字,对索引进行查询,返回查询结果.全文检索引擎就是一个搜索
擎,要进行搜索必须有一个数据源。在①处可以看到,我们的数据源可以来自下面四个地方:
文件系统:我们的操作系统搜索是通过顺序搜索速度相当的慢,在这也可以通过全文检索来提高	搜索速度;
数据库:数据库对于文本字段的内容不能建立索引,在以后应用中也会在数据库基础上集成全文检索来提高检索速度;
互联网:目前网络中的数据量已经达到百亿单位,在搜索时能够为所有的网页建立目录也是可以提高搜索速度。
	其他输入:可以直接对直接输入的内容建立索引和储存。
	有了数据,目的就是为了给用户提供搜索服务。在②处用户界面主要有两个功能:
获取用户输入的搜索关键字,发送请求到后台进行处理;
响应查询,展示搜索结果。

	我们可以看到对于索引库操作主要分为两步:对外部数据的索引建立,必要时索引库也可以储存数据;
响应用户的搜索请求,检索结果,进行相关排序后返回到前台。

全文索引核心

任何技术都有一些核心,全文搜索也有核心,而它的核心分为:索引创建,索引搜索。接下来我们就一一的来看。

将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
那么索引里面究竟存的什么,以及如何创建索引呢?在这通过下面的例子来解答这个问题。
首先构造三个不同的句子,有长有短:

在①处分别为3个句子加上编号,然后进行分词,把被一个单词分解出来与编号对应放在②处;在搜索的过程总,对于搜索的过程中大写和小写指的都是同一个单词,在这就没有区分的必要,按规则统一变为小写放在③处;要加快搜索速度,就必须保证这些单词的排列时有一定规则,这里按照字母顺序排列后放在④处;最后再简化索引,合并相同的单词,就得到如下结果:

通常在数据库中我们都是根据文档找到内容,而这里是通过词,能够快速找到包含他的文档,这就是文档倒排链表。

就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。

比如我们要搜索java world两个关键词,符合java的有1,2两个文档,符合world的有1,3两个文档,在搜索引擎中直接这样排列两个词他们之间是OR的关系,出现其中一个都可以被找到,所以这里3个都会出来。全文检索中是有相关性排序的,那么结果在是怎么排列的呢?hello java world中包含两个关键字排在第一,另两个都包含一个关键字,得到结果,hello lucene world排在第二,java在最长的句子中占的权重最低排在结果集的第三。从这里可以看出相关度排序还是有一定规则的。

Lucene入门

是什么?
Apache Lucene是一个用Java写的高性能、可伸缩的全文检索引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。Lucene的目标是为各种中小型应用程序加入全文检索功能。ElatisSearch(处理分布式)大型项目
Lucene的核心作者:Doug Cutting是一位资深全文索引/检索专家。
版本发布情况:2000年3月,最初版发布,2001年9月,加入apache;2004年7月,发布1.4正式版;2009年11月,发布2.9.1(jdk1.4)及3.0(jdk1.5)版本;2015年3月,发布4.10.4。2016年2月,发布5.5.0。
4.2.Helloworld
Lucene的索引库和数据库一样,都提供相应的API来便捷操作。

Lucene中的索引维护使用IndexWriter,由这个类提供添删改相关的操作;索引的搜索则是使用IndexSearcher进行索引的搜索。HelloWorld代码如下,导入三个jar包:lucene-analyzers-common-5.5.0.jar,lucene-core-5.5.0.jar,lucene-queryparser-5.5.0.jar

添加依赖jar包:


<dependencies>
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-core</artifactId>
        <version>5.5.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-analyzers-common</artifactId>
        <version>5.5.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-queryparser</artifactId>
        <version>5.5.0</version>
    </dependency>
</dependencies>

创建索引
步骤:
1、 把文本内容转换为Document对象
文本是作为Document对象的一个字段而存在
2、 准备IndexWriter(索引写入器)
3 、通过IndexWriter,把Document添加到缓冲区并提交
addDocument
commit
close

//创建索引的数据 现在写死,以后根据实际应用场景

	String doc1 = "hello world";
	String doc2 = "hello java world";
	String doc3 = "hello lucene world";
	Private String path ="F:/eclipse/workspace/lucene/index/
hello";
@Test
	publicvoid testCreate() {
		try {
			//2、准备IndexWriter(索引写入器)
			//索引库的位置 FS fileSystem
			Directory d = FSDirectory.open(Paths.get(path ));
			//分词器
			Analyzer analyzer = new SimpleAnalyzer();
			//索引写入器的配置对象
			IndexWriterConfig conf = new IndexWriterConfig(analyzer);
			IndexWriter indexWriter = new IndexWriter(d, conf);
			System.out.println(indexWriter);
			
			//1、 把文本内容转换为Document对象
			//把文本转换为document对象
			Document document1 = new Document();
			//标题字段
			document1.add(new TextField("title", "doc1", Store.YES));
			document1.add(new TextField("content", doc1, Store.YES));
			//添加document到缓冲区
			indexWriter.addDocument(document1);
			Document document2 = new Document();
			//标题字段
			document2.add(new TextField("title", "doc2", Store.YES));
			document2.add(new TextField("content", doc2, Store.YES));
			//添加document到缓冲区
			indexWriter.addDocument(document2);
			Document document3 = new Document();
			//标题字段
			document3.add(new TextField("title", "doc3", Store.YES));
			document3.add(new TextField("content", doc3, Store.YES));
			
			//3 、通过IndexWriter,把Document添加到缓冲区并提交
			//添加document到缓冲区
			indexWriter.addDocument(document3);
			indexWriter.commit();
			indexWriter.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	
	}

图形界面客户端使用
Luko的使用

4.2.2.搜索索引
1 封装查询提交为查询对象
2 准备IndexSearcher
3 使用IndexSearcher传入查询对象做查询-----查询出来只是文档编号DocID
4 通过IndexSearcher传入DocID获取文档
5 把文档转换为前台需要的对象 Docment----> Article

@Test
	publicvoid testSearch() {
		String keyWord = "lucene";
		try {
			// * 1 封装查询提交为查询对象
		//通过查询解析器解析一个字符串为查询对象
			String f = "content"; //查询的默认字段名,
			Analyzer a = new SimpleAnalyzer();//查询关键字要分词,所有需要分词器
			QueryParser parser = new QueryParser(f, a);
			Query query = parser.parse("content:"+keyWord);
          //Query query = new TermQuery(new Term("content",keyword));
			// * 2 准备IndexSearcher
			Directory d = FSDirectory.open(Paths.get(path ));
			IndexReader r = DirectoryReader.open(d);
			IndexSearcher searcher = new IndexSearcher(r);
			// * 3 使用IndexSearcher传入查询对象做查询-----查询出来只是文档编号DocID
			TopDocs topDocs = searcher.search(query, 1000);//查询ton条记录 前多少条记录
			System.out.println("总命中数:"+topDocs.totalHits);
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;//命中的所有的文档的封装(docId)
			// * 4 通过IndexSearcher传入DocID获取文档
			for (ScoreDoc scoreDoc : scoreDocs) {
				int docId = scoreDoc.doc;
				Document document = searcher.doc(docId);
				// * 5 把文档转换为前台需要的对象 Docment----> Article
				System.out.println("=======================================");
				System.out.println("title:"+document.get("title")
								+",content:"+document.get("content"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

4.2.3. 高亮操作

加入依赖

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-highlighter</artifactId>
    <version>5.5.0</version>
</dependency>
/**
 * 高亮:就是对搜索匹配到的关键字,自定义一个样式.
 *   需要一个高亮的pom依赖.
 *   对搜索出的结果再进行一个标识
 * @throws Exception
 */
@Test
public void testIndexSearcherHigh() throws Exception {

    Path pathStr = Paths.get(path);
    Directory directory = FSDirectory.open(pathStr);
    IndexReader indexReader = DirectoryReader.open(directory);//读索引
    //1:创建一个IndexSearcher:从哪里读取索引
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);

    String f = "content";//查询的字段:默认查询的字段
    //词法分析器
    Analyzer analyzer = new SimpleAnalyzer();
    QueryParser queryParser = new QueryParser(f, analyzer);
    String queryCondition = "content:hello";//查询条件
    Query query = queryParser.parse(queryCondition);
    //查询content字段包含java的前10条数据
    //2:使用indexSearcher进行搜索:
    TopDocs topDocs = indexSearcher.search(query, 10);// top n

    //此处加入的是搜索结果的高亮部分
    SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<b><font color='red'>","</font></b>"); //如果不指定参数的话,默认是加粗,即<b><b/>
    QueryScorer scorer = new QueryScorer(query);//计算得分,会初始化一个查询结果最高的得分
    Fragmenter fragmenter = new SimpleSpanFragmenter(scorer); //根据这个得分计算出一个片段
    Highlighter highlighter = new Highlighter(simpleHTMLFormatter, scorer);
    highlighter.setTextFragmenter(fragmenter); //设置一下要显示的片段

    for(ScoreDoc scoreDoc : topDocs.scoreDocs) { //取出每条查询结果
        Document doc = indexSearcher.doc(scoreDoc.doc); //scoreDoc.doc相当于docID,根据这个docID来获取文档
        //获取content字段:
        String content = doc.get("content");
        System.out.println("没有高亮的:"+content);
        //显示高亮部分
        if(content != null) {
	   //TokenStream 是一个分词后的 Token 结果组成的流,通过流能够不断的得到下一个 Token。
            TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(content));
            //取高亮后的content字段的值
            String summary = highlighter.getBestFragment(tokenStream, content);
            System.out.println("高亮的:"+summary);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值