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的基本使用的更多相关文章

  1. lucene 基础知识点

    部分知识点的梳理,参考<lucene实战>及网络资料 1.基本概念 lucence 可以认为分为两大组件: 1)索引组件 a.内容获取:即将原始的内容材料,可以是数据库.网站(爬虫).文本 ...

  2. 用lucene替代mysql读库的尝试

    采用lucene对mysql中的表建索引,并替代全文检索操作. 备注:代码临时梳理很粗糙,后续修改. import java.io.File; import java.io.IOException; ...

  3. Lucene的评分(score)机制研究

    首先,需要学习Lucene的评分计算公式—— 分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素.其中每一项的意思如下表所示: 表3.5 评分公式中的因子 评分因子 描 述 ...

  4. Lucene的分析资料【转】

    Lucene 源码剖析 1 目录 2 Lucene是什么 2.1.1 强大特性 2.1.2 API组成- 2.1.3 Hello World! 2.1.4 Lucene roadmap 3 索引文件结 ...

  5. Lucene提供的条件判断查询

    第一.按词条搜索 - TermQuery query = new TermQuery(new Term("name","word1"));hits = sear ...

  6. Lucene 单域多条件查询

    在Lucene 中 BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST表示and,BooleanClause.Occur.MUST_NOT表 ...

  7. lucene自定义过滤器

    先介绍下查询与过滤的区别和联系,其实查询(各种Query)和过滤(各种Filter)之间非常相似,可以这样说只要用Query能完成的事,用过滤也都可以完成,它们之间可以相互转换,最大的区别就是使用过滤 ...

  8. lucene+IKAnalyzer实现中文纯文本检索系统

    首先IntelliJ IDEA中搭建Maven项目(web):spring+SpringMVC+Lucene+IKAnalyzer spring+SpringMVC搭建项目可以参考我的博客 整合Luc ...

  9. 全文检索解决方案(lucene工具类以及sphinx相关资料)

    介绍两种全文检索的技术. 1.  lucene+ 中文分词(IK) 关于lucene的原理,在这里可以得到很好的学习. http://www.blogjava.net/zhyiwww/archive/ ...

  10. MySQL和Lucene索引对比分析

    MySQL和Lucene都可以对数据构建索引并通过索引查询数据,一个是关系型数据库,一个是构建搜索引擎(Solr.ElasticSearch)的核心类库.两者的索引(index)有什么区别呢?以前写过 ...

随机推荐

  1. ONOS架构-概览

    这个是阅读https://wiki.onosproject.org/display/ONOS/Architecture+Guide是顺便翻译的,目前断断续续在阅读,今天先贴一部分 概览 基于osgi, ...

  2. windows2012安装

    windows server 2012 r2 安装无法找到install.wim 错误代码0x80070026,以及制作U启动盘决解ISO文件超过5G大小限制的解决方案关于在服务器上安装windows ...

  3. python学习(七)

  4. [LeetCode]题53:Maximum Subarray

    Given an integer array nums, find the contiguous subarray (containing at least one number) which has ...

  5. 解决Gephi导入csv文件时提示“边表格需要一个包含节点标号‘源’和‘目标’列” 问题的两个方案

    1.将csv文件在Excel中打开,并重新保存为csv. 2.将csv文件导入到sqlite,使用sqlite作为数据来源. 标准格式如下

  6. 关于window 图片系统功能

    直接选择文件悬浮  分辨率 大小 修改时间 文件选择较少的 可以选择反选  找到自己想要的 大小排序 找到最大的压缩  名称排序 同一类的图片  按时间排序找自己刚修改的图片 文档类预览

  7. 非阻塞IO发送http请求

    import socket from urllib.parse import urlparse from selectors import DefaultSelector, EVENT_READ, E ...

  8. .NET:bin 与 obj,Debug 与 Release ,区别与选择

    bin 与 obj bin 目录:用来存放编译的结果. ( bin是二进制binrary的英文缩写,因为最初C编译的程序文件都是二进制文件 ) 编译的结果,有 Debug 和 Release 两个版本 ...

  9. FileWriter向文件中写入内容

    1.创建fileWriter对象 2.写入文件信息 3.关闭流 必须关闭流,否则将不能写入文件中 /** * * @author Administrator * 文件写入对象的使用 * */ publ ...

  10. 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(三)(错误整理篇)

    使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(一) 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(二) 以上两篇已经把流 ...