Lucene的全文检索算法

##1. 什么是lucene?
lucene是apache组织下的一个全文检索引擎工具包, 就是一堆jar包, 放入tomcat下不可以独立运行.
##2. lucene的作用?
优化查询速度, 在海量数据查询的时候, 可以优化查询速度
我们可以使用lucene来构建像百度, 谷歌, 必应这样的全文检索引擎系统
##3. 应用领域:
互联网全文检索引擎: 百度, 谷歌, 必应
站内全文检索引擎: 天猫, 京东的搜索栏, 贴吧的帖子搜索

##4.中文分词器:
作用: 让分成词符合中文语法
cjk 中日韩分词器: 它里面使用的是二分法分词
分词效果也不理想

08年之前用的庖丁分词, 后来代码不再更新, 所以不好用了

IK分词, 又是一个中国团队开发的中文分词器力作
停用词典: 凡是出现在停用词典中的词都会被过滤掉
扩展词典: 凡是出现在扩展词典中的词, 就认为是一个词

刚解压出来的停用词典和扩展词典的编码格式是utf-8+BOM格式的, 放入项目中是不好用的, 所以需要手动
转换成utf-8编码的文件

##全文检索算法(倒排索引表算法):
首先将需要查询的文本提取出来, 进行切分词, 将切分出来的一个一个的词组成索引(目录),
查询的时候先查索引(目录), 根据索引可以找到对应的文档, 那么这种通过索引找文档的方法,
叫做全文检索算法
优点: 查询速度快, 不会随着数据量的增大而使查询速度变慢
缺点: 目录会占用额外磁盘空间, 本算法是用空间换时间
切分词: 就是将一句一句的话切分成一个一个的词.去掉里面的空格, 标点符号, 停用词(a, an, the, 的, 地, 得),
大写字母转小写字母
索引库: 就是自己创建的一个文件夹, 里面放了lucene的索引文件和文档文件
term词元: 就是一个单词

###索引的创建:

package cn.itcast.lucene;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
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;
import org.apache.lucene.util.Version;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import cn.itcast.dao.BookDao;
import cn.itcast.dao.BookDaoImpl;
import cn.itcast.pojo.Book;

public class IndexManagerTest {
	@Test
	public void testIndexAndDocCreate() throws Exception {
		//从数据库中获取原始数据
		BookDao bookDao = new BookDaoImpl();
		List<Book> bookList = bookDao.queryBookList();
		List<Document> docList = new ArrayList<Document>();
		
		for(Book book:bookList){
			Integer id = book.getId();
			String name = book.getName();
			Float price = book.getPrice();
			String pic = book.getPic();
			String desc = book.getDesc();
			
			//创建文档对象、
			Document doc = new Document();
			//创建域,第一个参数域名,第二个参数域值,第三个参数:是否存储,yes存储,no不存储
			//是否分词:否, 因为ID是一个整体,分词后无意义
			//是否索引:是, 因为需要根据id进行查询, 
			//是否存储:是, 因为id主键字段很重要, 所以需要索引
			StringField idField = new StringField("id",String.valueOf(id),Store.YES);
			//是否分词:是, 因为名称分词有意义,并且需要根据名称进行查询
			//是否索引:是, 因为需要根据名称查询
			//是否存储:是, 因为列表页需要显示出来
			TextField nameField = new TextField("name",name,Store.YES);
			//是否分词:是, 因为lucne底层算法规定, 数字必须分词, 因为需要根据价格对比查找, 这个我们改不了,lucene定死了
			//是否索引:是, 因为需要根据价格对比范围查找
			//是否存储:是, 因为页面需要展示价格
			FloatField priceField = new FloatField("price",price,Store.YES);
			
			//是否分词:否, 因为图片地址分词后无意义, 并且不需要根据地址查找
			//是否索引:否, 因为不需要根据地址进行查找
			//是否存储:是, 因为需要将图片展示出来
			StoredField picField = new StoredField("pic",pic);
			//是否分词:是, 因为需要根据描述进行查找, 并且描述分词后有意义
			//是否索引:是, 因为需要根据描述进行查找
			//是否存储:否, 查完不需要显示, 并且描述中含有量图片, 占用大量磁盘空间, 如果不存想拿出来, 也可以, 需要根据id
					//编写sql语句, 去数据库中查找
			TextField descField = new TextField("desc",desc,Store.NO);
			
			
			//将域放入到文档对象中
			doc.add(idField);
			doc.add(nameField);
			doc.add(priceField);
			doc.add(picField);
			doc.add(descField);
			
			//将文档放入到集合中
			docList.add(doc);
		}
		
		//1.创建分词器,StandardAnalyzer标准分词器,标准分词器就是单词分词,就是一个字就是一个词
		//Analyzer                  = new StandardAnalyzer();
		Analyzer analyzer = new IKAnalyzer();
		//2.创建写入的目录地址,RAMDirectory存在内容中,FSDirectory存储在硬盘中File System Directory
		Directory dir = FSDirectory.open(new File("E:\\dic"));
		//3.创建索引和文档的初始化对象
		IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3,analyzer);
		//4.创建索引和文档对象
		IndexWriter indexWriter = new IndexWriter(dir,config);
		//5.将一个一个的文档集合都放入索引和文档的对象中
		for(Document doc:docList){
			indexWriter.addDocument(doc);
		}
		//6.提交
		indexWriter.commit();
		//7.关闭
		indexWriter.close();
	
	}
}

###查询具体流程:

package cn.itcast.lucene;
import java.io.File;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class IndexSearchTest {
	@Test
	public void testIndexSearch() throws Exception {
		//1. 创建分词器, 创建索引和搜索的时候使用的分词器要相同
		//Analyzer analyzer = new StandardAnalyzer();
		Analyzer analyzer = new IKAnalyzer();
		//2.创建查询语法,第一个参数:默认搜索域,查询语法中如果指定域名从指定域中进行搜索,如果没有指定搜索的域从默认搜索域中进行搜索
		QueryParser queryParser = new QueryParser("name",analyzer);
		//3.设置查询语法,返回query对象
		Query query = queryParser.parse("desc:java");
		//4.指定从哪个目录中读取,也就是索引库的位置
		Directory dir = FSDirectory.open(new File("E:\\dic"));
	    //5.创建读取对象
		IndexReader indexReader = IndexReader.open(dir);
		//6.创建搜索对象
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
		//7.查询并返回结果,第一个参数:查询对象,第二个参数,指定返回多少条数据
		TopDocs topDocs = indexSearcher.search(query, 10);
		//打印一共查询到多少条数据
		System.out.println("====count===" + topDocs.totalHits);
		//8.返回查询到的结果集
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		for(ScoreDoc scoreDoc :scoreDocs){
			//在lucene创建文档的时候会自动给每个文档对象分配id, 在索引中有索引和对应的文档进行关联
			int docID = scoreDoc.doc;
			//通过文档id读取指定的文档
			Document doc = indexReader.document(docID);
			//通过域名取出域值
			System.out.println("===id==" + doc.get("id"));
			System.out.println("===name==" + doc.get("name"));
			System.out.println("===price==" + doc.get("price"));
			System.out.println("=================================================================");
		}
	
	}
}

如果是IK分词器:需要引入config里面的东西、和ik分词器jar包
这里写图片描述

##总结:
1.我们从数据库查询出来数据,创建文档对象,每条数据均为一个文档对象。
2.创建域,每个字段均作为域名,然后为参数传入域值(数据库中具体的字段值)。
3.将文档放入到集合中,然后创建分词器,再创建要写入的目录地址。按照指定分词器规则进行分词,分词后,将文档的集合放入到索引和文档对象中(自动生成索引)。

查询:

  1. 创建分词器, 创建索引和搜索的时候使用的分词器要相同。
    2.创建查询语法,第一个参数:默认搜索域,查询语法中如果指定域名从指定域中进行搜索,如果没有指定搜索的域从默认搜索域中进行搜索。
    3.设置查询语法,返回query对象
    Query query = queryParser.parse(“desc:java”); 第一个参数是搜索域,第二个是要搜索的内容。
    也就是在desc域中,搜索java,这样就会避免了全文扫描而造成效率低的情况。
    4.指定从哪个目录中读取,也就是索引库的位置
    5.创建读取对象(指定要读取的目录)
    6.创建搜索对象,从读取出来的索引和文档中进行搜索。
    7.执行搜索。(指定搜索规则,和查询条数)
    简单说:设置默认查询语法(默认搜索域和指定分词器)–》设置查询语法(指定搜索域和搜索内容)–》从文档和索引对象中进行查询–》执行查询(指定查询语法,查询条数——直接去找搜索区中查询指定内容)

根据索引查询,可以避免全文扫描。大大提高查询效率。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值