##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.将文档放入到集合中,然后创建分词器,再创建要写入的目录地址。按照指定分词器规则进行分词,分词后,将文档的集合放入到索引和文档对象中(自动生成索引)。
查询:
- 创建分词器, 创建索引和搜索的时候使用的分词器要相同。
2.创建查询语法,第一个参数:默认搜索域,查询语法中如果指定域名从指定域中进行搜索,如果没有指定搜索的域从默认搜索域中进行搜索。
3.设置查询语法,返回query对象
Query query = queryParser.parse(“desc:java”); 第一个参数是搜索域,第二个是要搜索的内容。
也就是在desc域中,搜索java,这样就会避免了全文扫描而造成效率低的情况。
4.指定从哪个目录中读取,也就是索引库的位置
5.创建读取对象(指定要读取的目录)
6.创建搜索对象,从读取出来的索引和文档中进行搜索。
7.执行搜索。(指定搜索规则,和查询条数)
简单说:设置默认查询语法(默认搜索域和指定分词器)–》设置查询语法(指定搜索域和搜索内容)–》从文档和索引对象中进行查询–》执行查询(指定查询语法,查询条数——直接去找搜索区中查询指定内容)
根据索引查询,可以避免全文扫描。大大提高查询效率。