之前看过也部署过ES,但和MQ一样,认识的很浅导致现在也没什么印象,所以这里也从全文检索开始了解和学习.
全文检索的概念:
全文检索是指以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。
关于全文检索,我们要知道:1,只处理文本。2,不处理语义。3,搜索时英文不区分大小写。4,结果列表有相关度排序。5,并且可以对结果具有过滤高亮的功能
原文地址: 原文地址
lucene:
lucene是是apche下的一个全文检索工具, solr和ES都是基于lucene的原理开发的, 类似于jdbc和Mybatis的关系.
lucene的使用场景:
站内搜索: 如taobao,jd,站内贴吧搜索等.
互联网搜索: baidu,google, biying等搜索引擎
常见的搜索算法:
顺序扫描法:
描述: 拿着关键字逐条比较,逐字匹配,找到为止.
缺点: 随着内容的大量增长而效率逐渐降低.
优点: 准确率高.
举例:数据库中的like查询.
倒排索引算法:
描述:把数据库中的所有内容查询出来,然后进行切分词(把内容中的:的,得,地,a,an,the等不重要的词删掉,大写变小写,空格去掉.剩下词就是分词),将分出来的词组成索引(目录),把查询出来的内容存入文档中,索引和文档组成索引库( 索引 + 文档,就是电脑上的一个文件夹,其中存储了索引和文档). 在检索时,先找到索引,索引和文档之间有联系,能快速地确定文档的内容,返回数据.
优点: 效率高,不会随着数据大量增长而效率逐渐降低.
缺点: 需要定时针对索引库进行更新,以空间换时间,索引库占用电脑大量的空间.
举例:新华字典,当我们看到一个不认识的字的时候,会通过偏旁(索引)查询该字,而该字后面有页码(索引和文档的联系),通过页码找到该字地详细信息(文档).
lucene的原理:
倒排索引算法思路:
文档; 是lucene中的一个对象, 一个文档对应表里的一条数据.,每条数据中的每一列对应一个域对象.
从图中可以看到,lucene会将数据全部查询出来,进行分词,切分词后组成索引,然后将内容放入文档当中,将数据每一个字段值放到域对象中,域对象放入文档,文档 +索引 组成索引库.
lucene的使用:
创建索引库:
1.创建分词对象:
//1.创建分词对象:
Analyzer analyzer = new StandardAnalyzer();
2.准备数据 —jdbc
//2.准备数据 ---jdbc,从数据库查询数据
BookDao bookDao= new BookDao();
List<Book> bookList = bookDao.findAll();
3.创建文档列表对象 ---- 一个book对象对应一个文档
4.创建域对象 ---- 一个book对象中的属性对应一个域对象
//3.创建文档列表对象 ---- 一个book对象对应一个文档
//文档集合
List<Document> docList = new ArrayList<Document>();
for (Book book : bookList) {
//一个book 一个文档
//创建文档
Document doc = new Document();
//4.创建域对象 ---- 一个book对象中的属性对应一个域对象
//id
TextField idFiled = new TextField("id",String.valueOf(book.getId()), Field.Store.YES); //param1: 域名(列名称) param2:域值(数据值) param3: 是否存储
//name
TextField nameFiled = new TextField("name",book.getName(), Field.Store.YES); //param1: 域名(列名称) param2:域值(数据值) param3: 是否存储
//price
TextField priceFiled = new TextField("price",String.valueOf(book.getPrice()), Field.Store.YES); //param1: 域名(列名称) param2:域值(数据值) param3: 是否存储
//description
TextField descFiled = new TextField("description",book.getDescription(), Field.Store.YES); //param1: 域名(列名称) param2:域值(数据值) param3: 是否存储
//pic
TextField picFiled = new TextField("pic",book.getPic(), Field.Store.YES); //param1: 域名(列名称) param2:域值(数据值) param3: 是否存储
//把域对象添加到文档对象中
doc.add(idFiled);
doc.add(nameFiled);
doc.add(priceFiled);
doc.add(descFiled);
doc.add(picFiled);
//将文档对象添加到文档集合中
docList.add(doc);
}
5.指定索引库的位置
//5.指定索引库的位置
FSDirectory dir = FSDirectory.open(new File("D:\\es"));
//创建索引输出流配置对象
//param1 版本号 param2: 分词器对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3,analyzer);
6.创建索引输出流对象
//6.创建索引输出流对象
//param1: 索引库位置对象 param2: 索引输出流配置对象
IndexWriter indexWriter = new IndexWriter(dir,indexWriterConfig);
//把文档对象写入到索引库中
for (Document document : docList) {
indexWriter.addDocument(document);
}
//提交
indexWriter.commit();
//释放资源
indexWriter.close();
Dao层代码各位自行写,就是一个简单的查询sql,启动后报如下错误.
检查了一下包,发现根本没有lucene50这个包,怀疑是否是版本问题,于是换成了5.0.0版本
启动后发现50变成53了…
后来排查后发现是依赖冲突的问题,因为是学ES写了几个demo,导入了ES相关的包导致的,将ES的依赖删除即可启动成功了
成功后找到对应目录发现多了几个文件:
lucene图形化界面:
使用lukeall的jar包:
运行命令: java -jar lukeall-4.10.3.jar
打开刚刚lucene存储数据的目录即可看到存的数据了:
索引界面.
文档界面:
搜索页面:
搜索页面我们可以看到查出来的数据有score一列,分数越高排行越靠前,分数是经过一定算法算出数据和查询需求的匹配度,匹配度越高分数越高.
其他页面就不一一说了,大家可以自己研究.
lucene的查询操作:
需要注意一点: 创建索引库和查询(删除,修改)索引库使用的分词对象必须是同一个.
直接贴代码吧:
package com.ceeemall.es.demo;
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.ParseException;
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.FSDirectory;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
public class SearchDemo {
@Test
public void searchTest() throws IOException, ParseException {
//创建分词器
Analyzer analyzer = new StandardAnalyzer();
//指定索引位置
FSDirectory dir = FSDirectory.open(new File("D:\\es\\dic"));
//输入流对象
IndexReader indexReader = IndexReader.open(dir);
//索引的查询对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//创建查询分析对象
//param1: 默认的域名(如果查询时指定了域则在指定域中查询,否则使用默认域) param2: 分词器对象
QueryParser queryParser =new QueryParser("name",analyzer);
//获取查询关键字对象
Query query = queryParser.parse("java");
//检索
//param1: query 查询的关键字对象 param2: 最多查询的条数
//return:最上面的文档
TopDocs topdocs = indexSearcher.search(query, 2);
//返回分数文档
ScoreDoc[] scoreDocs = topdocs.scoreDocs;
//遍历
for (ScoreDoc scoreDoc : scoreDocs) {
//文档索引
int docId = scoreDoc.doc;
//获取文档对象
Document document = indexSearcher.doc(docId);
String id = document.get("id");
String name = document.get("name");
String description = document.get("description");
String price = document.get("price");
String pic = document.get("pic");
System.out.println(id + " " + name + " " + description + " " + price + " " + pic );
}
}
}
从索引中查询出数据:
删除更新就不写了,大家可以自己研究.