Lucene的基本使用
1、了解搜索技术
2、搜索引擎的原理
索引:类似于书的目录
3、实现搜索技术的方式
方式1:数据库搜索
利用SQL语句进行模糊搜索:
select * from items where title like “%上海%”;
select * from items where title like “上海%”;----走索引
问题:
在数据量很大的情况下,模糊搜索不一定走索引,因此效率就会很低。
方式2:Lucene技术
解决在海量数据的情况下,利用倒排索引技术,实现快速的搜索、打分、排序等功能
4、倒排索引技术
创建倒排索引,分为以下几步:
1)创建文档列表:
l lucene首先对原始文档数据进行编号(DocID),形成列表,就是一个文档列表
2)创建倒排索引列表
l 然后对文档中数据进行分词,得到词条(Term)。对词条进行编号,以词条创建索引。然后记录下包含该词条的所有文档编号(及其它信息)。
拉斯跳槽 ---》拉斯、跳槽 –》0234
l 倒排索引创建索引的流程:
1) 首先把所有的原始数据进行编号,形成文档列表
2) 把文档数据进行分词,得到很多的词条,以词条为索引。保存包含这些词条的文档的编号信息。
l 搜索的过程:
1) 当用户输入任意的内容时,首先对用户输入的内容进行分词,得到用户要搜索的所有词条
2) 然后拿着这些词条去倒排索引列表中进行匹配。找到这些词条就能找到包含这些词条的所有文档的编号。
3) 然后根据这些编号去文档列表中找到文档
5、Lucene技术的增、删、改、查
1)导入依赖和插件
<dependencies>
<!-- Junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lucene核心库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<!-- Lucene的查询解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的默认分词器库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的高亮显示 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>
<!--IK分词器-->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
</dependencies> <build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2)创建索引
public class LuceneCreateTest {
@Test
public void testCreate() throws IOException {
//创建文档对象
Document document = new Document(); //创建并添加字段信息,参数:字段的名称、字段的值、是否储存,这里选用Store.YES代表存储到文档列表
//Store.NO代表不存储
document.add(new StringField("id","1", Field.Store.YES));
//这里的title字段需要用TextField,即创建索引又会被分词,StringField会创建索引,但是不会被分词
document.add(new TextField("title","谷歌之父跳槽facebook,屌爆了", Field.Store.YES)); //索引目录类,指定索引在硬盘中的位置
Directory directory = FSDirectory.open(new File("indexDir")); //创建分词器对象
// Analyzer analyzer = new StandardAnalyzer();
//引用IK分词器
Analyzer analyzer = new IKAnalyzer(); //索引写出工具的配置对象
IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST,analyzer);
//创建索引的写出工具类,参数:索引的目录和配置信息
IndexWriter indexWriter = new IndexWriter(directory,conf); //把文档交给IndexWriter
indexWriter.addDocument(document); //提交
indexWriter.commit();
//关闭
indexWriter.close();
}
3)批量创建索引
@Test
public void testCreate2() throws IOException {
//创建文档的集合
Collection<Document> docs = new ArrayList<>(); //创建文档对象
Document document1 = new Document();
document1.add(new StringField("id","1", Field.Store.YES));
document1.add(new TextField("title","谷歌地图之父跳槽facebook", Field.Store.YES));
docs.add(document1); //创建文档对象
Document document2 = new Document();
document2.add(new StringField("id","2", Field.Store.YES));
document2.add(new TextField("title","谷歌地图创始人拉斯离开谷歌加盟Facebook", Field.Store.YES));
docs.add(document2); // 创建文档对象
Document document3 = new Document();
document3.add(new StringField("id", "3", Field.Store.YES));
document3.add(new TextField("title", "谷歌地图创始人拉斯离开谷歌加盟Facebook", Field.Store.YES));
docs.add(document3); // 创建文档对象
Document document4 = new Document();
document4.add(new StringField("id", "4", Field.Store.YES));
//document4.add(new TextField("title", "谷歌地图之父跳槽Facebook与Wave项目取消有关", Field.Store.YES));
Field field = new TextField("title","谷歌地图之父跳槽Facebook与Wave项目取消有关", Field.Store.YES);
//设置激励因子,作弊
field.setBoost(10.0f);
document4.add(field);
docs.add(document4); // 创建文档对象
Document document5 = new Document();
document5.add(new StringField("id", "5", Field.Store.YES));
document5.add(new TextField("title", "谷歌地图之父拉斯加盟社交网站Facebook", Field.Store.YES));
docs.add(document5); //索引目录类,指定索引在硬盘的位置
Directory directory = FSDirectory.open(new File("indexDir")); //引入IK分词器
Analyzer analyzer = new IKAnalyzer();
//索引写出工具的配置对象
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,analyzer); //设置打开方式:openMode.APPEND会在索引的基础上追加新索引
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
//创建索引的写出工具类,参数:索引的目录和配置信息
IndexWriter indexWriter = new IndexWriter(directory,config); //把文档集合交给IndexWriter
indexWriter.addDocuments(docs);
//提交
indexWriter.commit();
//关闭
indexWriter.close();
}
4)删除索引
/*
* 删除索引
* 注意事项:
* 1、一般,为了进行精确删除,我们会根据唯一字段来删除,比如ID
* 2、如果是用Term删除,要求ID也必须是字符串类型
* */
@Test
public void testDelete() throws IOException {
//创建目录对象
Directory directory = FSDirectory.open(new File("indexDir"));
//创建配置对象
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,new IKAnalyzer());
//创建索引写出工具
IndexWriter writer = new IndexWriter(directory,config);
//根据词条进行删除
// writer.deleteDocuments(new Term("id","2"));
//根据query对象删除,如果ID是数值类型,那么我们可以用数值范围查询锁定一个具体的ID
// Query query = NumericRangeQuery.newLongRange("2d",2L,2L,true,true);
//// writer.deleteDocuments(query); //删除所有
writer.deleteAll();
//提交
writer.commit();
//关闭
writer.close();
}
5)查询索引
@Test
public void testSearch() throws IOException, ParseException {
//索引目录对象
Directory directory = FSDirectory.open(new File("indexDir")); //索引读取工具
IndexReader reader = DirectoryReader.open(directory);
//索引搜索工具
IndexSearcher indexSearcher = new IndexSearcher(reader); //索引查询解析器,两个参数:默认要查询字段的名称、分词器
// QueryParser parser = new QueryParser("title",new IKAnalyzer()); //多字段查询解析器
QueryParser parser = new MultiFieldQueryParser(new String[]{"id","title"},new IKAnalyzer());
//创建查询对象
Query query = parser.parse("硅谷地图之父拉斯"); //搜索数据,两个参数:查询条件对象,要查询的最大结果条数(总共就5个文档,如果不知道文档数据数据,也可以
//使用Integer.MAX_VALUE)
//返回的结果是 按照匹配度排名得分前N名的文档信息(包括查询到的总条数信息、所有符合条件的文档的编号信息)
//topDocs:两个属性:总记录数、文档数组
TopDocs topDocs = indexSearcher.search(query,10); //获取总条数
System.out.println("本次搜索共找到" + topDocs.totalHits + "条数据");
//获取得分文档对象(ScoreDoc)数组 ScoreDao中包含:文档的编号、文档的得分
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
//取出文档编号
int docID = scoreDoc.doc;
//根据编号去找文档
Document document = reader.document(docID); System.out.println("id" + document.get("id"));
System.out.println("title" + document.get("title")); //取出文档得分
System.out.println("得分:" + scoreDoc.score);
}
6)为了查询的方便,可以把上边的公共的部分代码抽取出来
//抽取公共的方法,提取一个查询数据的通用方法
public void search(Query query) throws IOException {
//索引目录对象
Directory directory = FSDirectory.open(new File("indexDir")); //索引读取对象
IndexReader reader = DirectoryReader.open(directory);
//索引搜索工具
IndexSearcher searcher = new IndexSearcher(reader); //搜索数据,两个参数:查询条件对象,要查询的最大结果条数
//返回的结果是 按照匹配度排名得分前N名的文档信息(包括查询到的总条数信息、所有符合条件的文档的编号信息)
//topDocs:两个属性:总记录数、文档数组 TopDocs topDocs = searcher.search(query,10);
//获取总条数
System.out.println("本次搜索共找到" + topDocs.totalHits + "条数据");
//获取得分文档对象
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
//取出文档编号
int docID = scoreDoc.doc;
//根据编号去找文档
Document doc = reader.document(docID);
System.out.println("id: " + doc.get("id"));
System.out.println("title: " + doc.get("title"));
//取出文档得分
System.out.println("得分:" + scoreDoc.score);
}
}
/**
* 测试普通词条查询
* 注意:Term(词条)是搜索的最小单位,不可在分词,值必须是字符串
* 一般用来搜索唯一字段,比如ID(对不需要分词的关键字进行查询)
* @throws IOException
*/
@Test
public void testTermQuery() throws IOException {
//创建词条查询对象
Query query = new TermQuery(new Term("title","谷歌地图"));
search(query);
} /*
* 通配符查询
* ? 可以代表任意一个字符
* * 可以任意多个任意字符
* */
@Test
public void testWildCardQuery() throws IOException {
//创建查询对象
Query query = new WildcardQuery(new Term("title","*歌*"));
search(query);
} /*
* 模糊查询
*
* */
@Test
public void testFuzzyQuery() throws IOException {
//创建模糊查询对象:允许用户输错,但是要求错误的最大编辑距离不能超过2
//编辑距离:一个单词到另一个单词最少修改的次数
//可以手动指定编辑距离,但是参数必须在0~2之间
Query query = new FuzzyQuery(new Term("title","facevool"),2);
search(query);
} /*
* 数值范围查询
* 注意:数值范围查询,可以用来对非String类型的ID进行精确的查找
* */
@Test
public void testNumericRangeQuery() throws IOException {
//数值范围查询对象,参数:字段名称,最小值、最大值、是否包含最小值、是否包含最大值
Query query = NumericRangeQuery.newLongRange("id",2L,2L,true,true);
search(query);
} /*
* 布尔查询
* 布尔查询本身没有查询条件,可以把查询通过逻辑运算进行组合!
* 交集:Occur.MUST + Occur.MUST
* 并集:Occur.SHOULD + Occur.SHOULD
* 非:Occur.MUST
* */
@Test
public void testBooleanQuery() throws IOException {
Query query1 = NumericRangeQuery.newLongRange("id",1L,3L,true,true);
Query query2 = NumericRangeQuery.newLongRange("id",2L,4L,true,true); // 创建布尔查询的对象
BooleanQuery query = new BooleanQuery();
// 组合其它查询
query.add(query1, BooleanClause.Occur.MUST_NOT);
query.add(query2, BooleanClause.Occur.SHOULD); search(query); }
7)修改索引
public class LuceneUpdate {
/*
* 修改索引
* 注意事项:
* 1、Lucene修改功能底层会先删除,再把新的文档添加
* 2、修改功能会根据Term进行匹配,所有匹配到的都会被删除,这样不好
* 3、因此,一般我们修改时,都会根据一个唯一不重复字段进行匹配修改,例如ID
* 4、但是词条搜索,要求ID必须是字符串,如果不是,这个方法就不能用
*
* 如果ID是数值类型,我们不能直接去修改,可以先手动删除deleteDocument(数值范围查询锁定ID)。再添加
* */ @Test
public void testUpdate() throws IOException {
//创建目录对象
Directory directory = FSDirectory.open(new File("indexDir")); //创建配置对象
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,new IKAnalyzer()); //创建索引写出工具
IndexWriter writer = new IndexWriter(directory,config);
//创建新的文档数据
Document doc = new Document();
doc.add(new StringField("id","1", Field.Store.YES));
doc.add(new TextField("title","谷歌地图之父跳槽facebook为了加入传智播客 屌爆了呀", Field.Store.YES)); /*
* 修改索引,参数
* 词条:根据这个词条匹配到的所有的文档都会被修改
* 文档信息:要修改的新的文档数据
* */ writer.updateDocument(new Term("id","1"),doc);
//提交
writer.commit();
//关闭
writer.close();
}
Lucene的基本使用的更多相关文章
- lucene 基础知识点
部分知识点的梳理,参考<lucene实战>及网络资料 1.基本概念 lucence 可以认为分为两大组件: 1)索引组件 a.内容获取:即将原始的内容材料,可以是数据库.网站(爬虫).文本 ...
- 用lucene替代mysql读库的尝试
采用lucene对mysql中的表建索引,并替代全文检索操作. 备注:代码临时梳理很粗糙,后续修改. import java.io.File; import java.io.IOException; ...
- Lucene的评分(score)机制研究
首先,需要学习Lucene的评分计算公式—— 分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素.其中每一项的意思如下表所示: 表3.5 评分公式中的因子 评分因子 描 述 ...
- Lucene的分析资料【转】
Lucene 源码剖析 1 目录 2 Lucene是什么 2.1.1 强大特性 2.1.2 API组成- 2.1.3 Hello World! 2.1.4 Lucene roadmap 3 索引文件结 ...
- Lucene提供的条件判断查询
第一.按词条搜索 - TermQuery query = new TermQuery(new Term("name","word1"));hits = sear ...
- Lucene 单域多条件查询
在Lucene 中 BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST表示and,BooleanClause.Occur.MUST_NOT表 ...
- lucene自定义过滤器
先介绍下查询与过滤的区别和联系,其实查询(各种Query)和过滤(各种Filter)之间非常相似,可以这样说只要用Query能完成的事,用过滤也都可以完成,它们之间可以相互转换,最大的区别就是使用过滤 ...
- lucene+IKAnalyzer实现中文纯文本检索系统
首先IntelliJ IDEA中搭建Maven项目(web):spring+SpringMVC+Lucene+IKAnalyzer spring+SpringMVC搭建项目可以参考我的博客 整合Luc ...
- 全文检索解决方案(lucene工具类以及sphinx相关资料)
介绍两种全文检索的技术. 1. lucene+ 中文分词(IK) 关于lucene的原理,在这里可以得到很好的学习. http://www.blogjava.net/zhyiwww/archive/ ...
- MySQL和Lucene索引对比分析
MySQL和Lucene都可以对数据构建索引并通过索引查询数据,一个是关系型数据库,一个是构建搜索引擎(Solr.ElasticSearch)的核心类库.两者的索引(index)有什么区别呢?以前写过 ...
随机推荐
- Thread的中断机制(interrupt)
先看收集了别人的文章,全面的了解下java的中断: 中断线程 线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡.还是等待新的 ...
- gulp下单页面应用打包
项目地址:https://pan.baidu.com/s/1cu4WW2 之前已经说过多入口打包,最近正好做一个单页面应用,之前多人口是用webpack打包的,但是感觉webpack比较重,单页面我又 ...
- Android中如何解决editText一进入activity就自动获取焦点的bug
有时候我们在进入activity 的时候,EditText会自动聚焦 有人说搞个宽高 都为0dp的EditText 但是我们搞代码的肯定要从实际层面解决,这样更能说服人 所以只需要在EditText的 ...
- yii2解决资源插件路径不对应问题
//本人ubuntu系统 composer self-update //升级到最新 //官网找到最新下面的这行执行. composer global require "fxp/compose ...
- django项目的部署
django项目部署到云服务器: 0.通过xshell连接远程主机服务器ip 1.使用xftp将项目发送到服务器端(也可以使用git) a.路径推荐为/var/project/(项目名) 2.给服务器 ...
- 判断网页打开浏览器类型,PC 手机端,微信浏览器,,,
//判断网页打开浏览器类型,PC 手机端,微信浏览器,,, <script type="text/javascript"> var browser = { versio ...
- 238. Product of Array Except Self除自身以外数组的乘积
网址:https://leetcode.com/problems/product-of-array-except-self/ 参考:https://leetcode.com/problems/prod ...
- Linux进程间通信机制
Linux支持管道.信号.unix system V三种IPC(Inter-Process-Communication)机制.以下分别对三种机制加以简单介绍. 一.信号机制: 信号又称作软中断,用来通 ...
- linux 搭建rap记录
1 安装 jdk1.8版本及以上 2 安装 apache-tomcat 3 安装 redis 最后编辑session配置和数据库配置 <session-config><session ...
- python四
三元运算 name = "张三" if 1 == 2 else "李四" print(name) name1 = "张三" if 1 == ...