共计 25195 个字符,预计需要花费 63 分钟才能阅读完成。
1.1 全文检索的概念
1) 从大量的信息中快速、准确地查找出要的信息
2) 搜索的内容是文本信息(不是多媒体)
3) 搜索的方式:不是根据语句的意思进行处理。如果要搜索的文本为”2012 年的春晚有赵本山吗”,那么含有这些词(2012 年、春晚、赵本山) 就能搜索出来。每一个词都是关键词。
4) 全面、快速、准确是衡量全文检索系统的关键指标。
5) 概括:
a) 只处理文本
b) 不处理语义
c) 搜索时英文不区分大小写
d) 结果列表有相关度排序
全文检索应用场景:
* 信息量必须特别大
* 做一个全文检索的指标
快速
准确
站内搜索
通常用于在大量数据出现的系统中,找出你想要的资料。常见的有
a) bbs 的关键字搜索
baidu 贴吧 林志玲、胡汉三
b) 商品网站的搜索等
中关村在线 商品的名称、电脑硬件名称 (CPU)
c) 文件管理系统
对文件的搜索功能。Window 的文件搜索
1.3.2 垂直搜索
a) 是针对 某个行业的搜索引擎
b) 是搜索引擎的细分和延伸
c) 是针对网页库中的专门信息的整合
d) 其特点是专、深、精,并具有行业色彩
e) 可以应用于购物搜索、房产搜索、人才搜索
1.1 全文检索与数据库搜索的区别
1.4.1 数据库的搜索
类似:select * from 表名 where 字段名 like‘% 关键字 %’
例如:select * from article where content like’%here%’
结果: where here shere
缺点:
1) 搜索效果比较差
2) 在搜索的结果中,有大量的数据被搜索出来,有很多数据是没有用的。
3) 查询速度在大量数据的情况下是很难做到快速的。
1.4.2 全文检索
1) 搜索结果按相关度排序:意味着只有前几个页面对于用户来说是比较有用的,其他的结果与用户想要的答案很可能相差甚远。数据库搜索是做不到相关度排序的。
2) 因为全文检索是采用引索的方式,所以在速度上肯定比数据库方式 like 要快。
3) 所以数据库不能代替全文检索。
Lucene 的详细介绍:请点这里
Lucene 的下载地址:请点这里
基于 Lucene 多索引进行索引和搜索 http://www.linuxidc.com/Linux/2012-05/59757.htm
Lucene 实战(第 2 版) 中文版 配套源代码 http://www.linuxidc.com/Linux/2013-10/91055.htm
Lucene 实战(第 2 版) PDF 高清中文版 http://www.linuxidc.com/Linux/2013-10/91052.htm
使用 Lucene-Spatial 实现集成地理位置的全文检索 http://www.linuxidc.com/Linux/2012-02/53117.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a9 http://www.linuxidc.com/Linux/2012-02/53113.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a8 http://www.linuxidc.com/Linux/2012-02/53111.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a7 http://www.linuxidc.com/Linux/2012-02/53110.htm
Project 2-1: 配置 Lucene, 建立 WEB 查询系统[Ubuntu 10.10] http://www.linuxidc.com/Linux/2010-11/30103.htm
全文检索只是一个概念,而具体实现有很多框架,lucene 是其中的一种。Lucene 的主页 http://lucene.apache.org/。本文用的是 3.0.1 版本。
互联网搜索结构图
说明:
1) 当用户打开 www.baidu.com 网页搜索某些数据的时候,不是直接找的网页,而是找的百度的索引库。索引库里包含的内容有索引号和摘要。当我们打开 www.baidu.com 时,看到的就是摘要的内容。
2) 百度的索引库的索引和互联网的某一个网站对应。
3) 当用户数据要查询的关键字,返回的页面首先是从索引库中得到的。
4) 点击每一个搜索出来的内容进行相关网页查找,这个时候才找的是互联网中的网页。
2.2 lucene 的大致结构框图
说明:
1) 在数据库中,数据库中的数据文件存储在磁盘上。索引库也是同样,索引库中的索引数据也在磁盘上存在,我们用 Directory 这个类来描述。
2) 我们可以通过 API 来实现对索引库的增、删、改、查的操作。
3) 在数据库中,各种数据形式都可以概括为一种:表。在索引库中,各种数据形式也可以抽象出一种数据格式为 Document。
4) Document 的结构为:Document(List<Field>)
5) Field 里存放一个键值对。键值对都为字符串的形式。
6) 对索引库中索引的操作实际上也就是对 Document 的操作。
更多详情见请继续阅读下一页的精彩内容:http://www.linuxidc.com/Linux/2014-06/102856p2.htm
1.1 准备 lucene 的开发环境
搭建 lucene 的开发环境,要准备 lucene 的 jar 包,要加入的 jar 包至少有:
1) lucene-core-3.1.0.jar (核心包)
2) lucene-analyzers-3.1.0.jar (分词器)
3) lucene-highlighter-3.1.0.jar (高亮器)
4) lucene-memory-3.1.0.jar (高亮器)
public class Article {
private Long id;
private String title;
private String content;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
@Test
public void testCreateIndex() throws Exception{
/**
* 1、创建一个 article 对象,并且把信息存放进去
* 2、调用 indexWriter 的 API 把数据存放在索引库中
* 3、关闭 indexWriter
*/
// 创建一个 article 对象,并且把信息存放进去
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
// 调用 indexWriter 的 API 把数据存放在索引库中
/**
* 创建一个 IndexWriter
* 参数三个
* 1、索引库 指向索引库的位置
* 2、分词器
*/
// 创建索引库
Directory directory = FSDirectory.open(new File(“./indexDir”));
// 创建分词器
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
IndexWriter indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);
// 把一个 article 对象转化成 document
Document document = new Document();
Field idField = new Field(“id”,article.getId().toString(),Store.YES,Index.NOT_ANALYZED);
Field titleField = new Field(“title”,article.getTitle(),Store.YES,Index.ANALYZED);
Field contentField = new Field(“content”,article.getContent(),Store.YES,Index.ANALYZED);
document.add(idField);
document.add(titleField);
document.add(contentField);
indexWriter.addDocument(document);
// 关闭 indexWriter
indexWriter.close();
}
@Test
public void testSearchIndex() throws Exception{
/**
* 1、创建一个 IndexSearch 对象
* 2、调用 search 方法进行检索
* 3、输出内容
*/
// 创建一个 IndexSearch 对象
Directory directory = FSDirectory.open(new File(“./indexDir”));
IndexSearcher indexSearcher = new IndexSearcher(directory);
// 调用 search 方法进行检索
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
QueryParser queryParser = new QueryParser(Version.LUCENE_30,”content”,analyzer);
Query query = queryParser.parse(“baidu”);// 关键词
TopDocs topDocs = indexSearcher.search(query, 2);
int count = topDocs.totalHits;// 根据关键词查询出来的总的记录数
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
float score = scoreDoc.score;// 关键词得分
int index = scoreDoc.doc;// 索引的下标
Document document = indexSearcher.doc(index);
// 把 document 转化成 article
Article article = new Article();
article.setId(Long.parseLong(document.get(“id”)));//document.getField(“id”).stringValue()
article.setTitle(document.get(“title”));
article.setContent(document.get(“content”));
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
如何把一个信息写到索引库中
读取信息的过程
1.1 全文检索的概念
1) 从大量的信息中快速、准确地查找出要的信息
2) 搜索的内容是文本信息(不是多媒体)
3) 搜索的方式:不是根据语句的意思进行处理。如果要搜索的文本为”2012 年的春晚有赵本山吗”,那么含有这些词(2012 年、春晚、赵本山) 就能搜索出来。每一个词都是关键词。
4) 全面、快速、准确是衡量全文检索系统的关键指标。
5) 概括:
a) 只处理文本
b) 不处理语义
c) 搜索时英文不区分大小写
d) 结果列表有相关度排序
全文检索应用场景:
* 信息量必须特别大
* 做一个全文检索的指标
快速
准确
站内搜索
通常用于在大量数据出现的系统中,找出你想要的资料。常见的有
a) bbs 的关键字搜索
baidu 贴吧 林志玲、胡汉三
b) 商品网站的搜索等
中关村在线 商品的名称、电脑硬件名称 (CPU)
c) 文件管理系统
对文件的搜索功能。Window 的文件搜索
1.3.2 垂直搜索
a) 是针对 某个行业的搜索引擎
b) 是搜索引擎的细分和延伸
c) 是针对网页库中的专门信息的整合
d) 其特点是专、深、精,并具有行业色彩
e) 可以应用于购物搜索、房产搜索、人才搜索
1.1 全文检索与数据库搜索的区别
1.4.1 数据库的搜索
类似:select * from 表名 where 字段名 like‘% 关键字 %’
例如:select * from article where content like’%here%’
结果: where here shere
缺点:
1) 搜索效果比较差
2) 在搜索的结果中,有大量的数据被搜索出来,有很多数据是没有用的。
3) 查询速度在大量数据的情况下是很难做到快速的。
1.4.2 全文检索
1) 搜索结果按相关度排序:意味着只有前几个页面对于用户来说是比较有用的,其他的结果与用户想要的答案很可能相差甚远。数据库搜索是做不到相关度排序的。
2) 因为全文检索是采用引索的方式,所以在速度上肯定比数据库方式 like 要快。
3) 所以数据库不能代替全文检索。
Lucene 的详细介绍:请点这里
Lucene 的下载地址:请点这里
基于 Lucene 多索引进行索引和搜索 http://www.linuxidc.com/Linux/2012-05/59757.htm
Lucene 实战(第 2 版) 中文版 配套源代码 http://www.linuxidc.com/Linux/2013-10/91055.htm
Lucene 实战(第 2 版) PDF 高清中文版 http://www.linuxidc.com/Linux/2013-10/91052.htm
使用 Lucene-Spatial 实现集成地理位置的全文检索 http://www.linuxidc.com/Linux/2012-02/53117.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a9 http://www.linuxidc.com/Linux/2012-02/53113.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a8 http://www.linuxidc.com/Linux/2012-02/53111.htm
Lucene + Hadoop 分布式搜索运行框架 Nut 1.0a7 http://www.linuxidc.com/Linux/2012-02/53110.htm
Project 2-1: 配置 Lucene, 建立 WEB 查询系统[Ubuntu 10.10] http://www.linuxidc.com/Linux/2010-11/30103.htm
全文检索只是一个概念,而具体实现有很多框架,lucene 是其中的一种。Lucene 的主页 http://lucene.apache.org/。本文用的是 3.0.1 版本。
互联网搜索结构图
说明:
1) 当用户打开 www.baidu.com 网页搜索某些数据的时候,不是直接找的网页,而是找的百度的索引库。索引库里包含的内容有索引号和摘要。当我们打开 www.baidu.com 时,看到的就是摘要的内容。
2) 百度的索引库的索引和互联网的某一个网站对应。
3) 当用户数据要查询的关键字,返回的页面首先是从索引库中得到的。
4) 点击每一个搜索出来的内容进行相关网页查找,这个时候才找的是互联网中的网页。
2.2 lucene 的大致结构框图
说明:
1) 在数据库中,数据库中的数据文件存储在磁盘上。索引库也是同样,索引库中的索引数据也在磁盘上存在,我们用 Directory 这个类来描述。
2) 我们可以通过 API 来实现对索引库的增、删、改、查的操作。
3) 在数据库中,各种数据形式都可以概括为一种:表。在索引库中,各种数据形式也可以抽象出一种数据格式为 Document。
4) Document 的结构为:Document(List<Field>)
5) Field 里存放一个键值对。键值对都为字符串的形式。
6) 对索引库中索引的操作实际上也就是对 Document 的操作。
更多详情见请继续阅读下一页的精彩内容:http://www.linuxidc.com/Linux/2014-06/102856p2.htm
保持数据库与索引库的同步
说明:在一个系统中,如果索引功能存在,那么数据库和索引库应该是同时存在的。这个时候需要保证索引库的数据和数据库中的数据保持一致性。可以在对数据库进行增、删、改操作的同时对索引库也进行相应的操作。这样就可以保证数据库与索引库的一致性。
工具类 DocumentUtils
public class DocumentUtils {
public static Document article2Document(Article article){
Document document = new Document();
Field idField = new Field(“id”,article.getId().toString(),Store.YES,Index.NOT_ANALYZED);
Field titleField = new Field(“title”,article.getTitle(),Store.YES,Index.ANALYZED);
Field contentField = new Field(“content”,article.getContent(),Store.YES,Index.ANALYZED);
document.add(idField);
document.add(titleField);
document.add(contentField);
return document;
}
public static Article document2Article(Document document){
Article article = new Article();
article.setId(Long.parseLong(document.get(“id”)));
article.setTitle(document.get(“title”));
article.setContent(document.get(“content”));
return article;
}
}
public class LuceneUtils {
public static Directory directory;
public static Analyzer analyzer;
static{
try{
directory = FSDirectory.open(new File(“./indexDir”));
analyzer = new StandardAnalyzer(Version.LUCENE_30);
}catch(Exception e){
e.printStackTrace();
}
}
}
public class ArticleIndex {
@Test
public void testCreateIndex() throws Exception{
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
indexWriter.addDocument(DocumentUtils.article2Document(article));
indexWriter.close();
}
@Test
public void testSearchIndex() throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{“title”,”content”}, LuceneUtils.analyzer);
Query query = queryParser.parse(“baidu”);
TopDocs topDocs = indexSearcher.search(query, 2);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
Document document = indexSearcher.doc(scoreDoc.doc);
Article article = DocumentUtils.document2Article(document);
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
/**
* 一般情况下索引库的删除用关键词
* @throws Exception
*/
@Test
public void testDeleteIndex() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
//indexWriter.deleteAll() 删除所有的索引值
/**
* term 就为关键词对象
*/
Term term = new Term(“title”, “lucene”);
indexWriter.deleteDocuments(term);
indexWriter.close();
}
/**
* 修改
* 先删除后增加
*/
@Test
public void testUpdateIndex() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
Term term = new Term(“title”, “lucene”);
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“ 修改后的内容 ”);
/**
* term 是用删除的
* document 是用于增加的
*/
indexWriter.updateDocument(term, DocumentUtils.article2Document(article));
indexWriter.close();
}
}
public void create () throws CorruptIndexException, LockObtainFailedException, IOException{
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
IndexWriter indexWriter=new IndexWriter(LuceneUtils.directory, LuceneUtils.analyzer, MaxFieldLength.LIMITED);
indexWriter.addDocument(DocumentUtils.article2Document(article));
indexWriter.close();
}
@Test
public void Query() throws CorruptIndexException, IOException,
ParseException {
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30,
new String[] { “title”, “content”}, LuceneUtils.analyzer);
TopDocs topDocs = indexSearcher.search(queryParser.parse(“lucene”), 2);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for (ScoreDoc scoreDoc : scoreDocs) {
System.err.println(“ 相关度为 ” + scoreDoc.score);
Document document = indexSearcher.doc(scoreDoc.doc);
Article article = DocumentUtils.document2Article(document);
articleList.add(article);
}
for (Article article : articleList) {
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
@Test
public void delete() throws CorruptIndexException, LockObtainFailedException, IOException{
IndexWriter indexWriter=new IndexWriter(LuceneUtils.directory, LuceneUtils.analyzer, MaxFieldLength.LIMITED);
// indexWriter.deleteAll()删除所有的索引值
/**
* term 就为关键词对象
*/
indexWriter.deleteDocuments(new Term(“title”,”lucene”));
indexWriter.close();
}
/**
*
* 修改 先删除后增加
* @throws IOException
* @throws CorruptIndexException
*
*/
@Test
public void update() throws CorruptIndexException, IOException {
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,
LuceneUtils.analyzer, MaxFieldLength.LIMITED);
Term term = new Term(“title”, “lucene”);
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“ 修改后的内容 ”);
/**
* term 是用删除的 document 是用于增加的
*/
indexWriter.updateDocument(term,
DocumentUtils.article2Document(article));
indexWriter.close();
}
/**
* 1、索引库的增、删、改是由 indexWriter 来操作的
* 2、同一个时刻内,同一个索引库,只能允许一个 indexWriter 操作
* 3、当 IndexWriter 创建完成以后,indexwriter 所指向的索引库就被占领了,只有当 indexWriter.close 时,才能释放锁的资源
* 4、当一个新的 indexWriter 想拥有索引库时,原来的 indexWriter 必须释放锁
* 5、只要索引库中存在 write.lock 文件,说明上锁了
* 6、indexWriter.close 有两层含义:
* * 关闭 IO 资源
* * 释放锁
* @author Administrator
*
*/
public class IndexWriterTest {
@Test
public void testIndexWriter() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
indexWriter.close();// 这里不关闭 就不能再次开启 indexWriter
IndexWriter indexWriter2 = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
}
}
1. 索引库的优化
当执行创建索引多次时,索引库的文件如图所示:(索引里内容是一样的)
结论:如果增加、删除反复操作很多次,就会造成文件大量增加,这样检索的速度也会下降,所以我们有必要去优化索引结构。使文件的结构发生改变从而提高效率。
public class OptimizeTest {
@Test
public void testOptimize() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
indexWriter.optimize();// 这个方法 优化
indexWriter.close();
}
}
/**
* 1、能不能设置很多个索引库
* 可以设置很多个索引库
* 2、索引库能不能合并起来
* 如果是内存索引库
* Directory ramDirectory = new RamDirectory(Directory d);
* 这样就可以把一个索引库放入到内存索引库中
* 利用 IndexWriter.addIndexesNoOptimize 方法可以把很多个索引库进行合并操作
* 3、应用程序能不能在内存中和索引库进行交互
* @author Administrator
*
*/
public class DirectoryTest {
/**
* 内存索引库
* * 速度比较快
* * 数据是暂时的
* * 内存索引库和文件索引库的特点正好互补
*/
@Test
public void testRam() throws Exception{
Directory directory = new RAMDirectory();
IndexWriter indexWriter = new IndexWriter(directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
indexWriter.addDocument(DocumentUtils.article2Document(article));
indexWriter.close();
this.showData(directory);
}
private void showData(Directory directory) throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30,new String[]{“title”,”content”},LuceneUtils.analyzer);
Query query = queryParser.parse(“lucene”);
TopDocs topDocs = indexSearcher.search(query, 10);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
Document document = indexSearcher.doc(scoreDoc.doc);
articleList.add(DocumentUtils.document2Article(document));
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
/**
* 文件索引库和内存索引库的结合
*/
@Test
public void testRamAndFile() throws Exception{
/**
* 1、当应用程序启动的时候,把文件索引库的内容复制到内存库中
* 2、让内存索引库和应用程序交互
* 3、把内存索引库的内容同步到文件索引库
*/
Directory fileDirectory = FSDirectory.open(new File(“./indexDir”));
Directory ramDirectory = new RAMDirectory(fileDirectory);
IndexWriter ramIndexWriter = new IndexWriter(ramDirectory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
IndexWriter fileIndexWriter = new IndexWriter(fileDirectory,LuceneUtils.analyzer,true,MaxFieldLength.LIMITED);
/**
* 在内存索引库中根据关键词查询
*/
this.showData(ramDirectory);
System.out.println(“ 上面的是从内存索引库中查询出来的 ”);
/**
* 把一条信息插入到内存索引库
*/
Article article = new Article();
article.setId(1L);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
ramIndexWriter.addDocument(DocumentUtils.article2Document(article));
ramIndexWriter.close();
/*
* 把内存索引库中的内容同步到文件索引库中
*/
fileIndexWriter.addIndexesNoOptimize(ramDirectory);
fileIndexWriter.close();
this.showData(fileDirectory);
System.out.println(“ 上面的是从文件索引库中查询出来的 ”);
}
}
/**
* 经过该方法可以把分词后的结果输出
* @param analyzer
* @param text
* @throws Exception
*/
private void testAnalyzer(Analyzer analyzer,String text)throws Exception{
TokenStream tokenStream = analyzer.tokenStream(“content”, new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while(tokenStream.incrementToken()){
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
}
/**
* 主要針對汉语
* 英语
*
* 汉语
* @author Administrator
*
*/
public class AnalyzerTest {
@Test
public void testEn() throws Exception{
/**
* Creates a searcher searching the index in the named directory
*/
/**
* 1、切分关键词
* 2、去掉停用词
* 3、把大写转化成小写
*/
String text = “Creates a searcher searching the index in the named directory”;
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
this.testAnalyzer(analyzer, text);
}
@Test
public void testZH() throws Exception{
/**
* 单字分词
*/
Analyzer analyzer = new ChineseAnalyzer();
String text = “ 传智播客的黎活明是 UFO”;
this.testAnalyzer(analyzer, text);
}
@Test
public void testZH2() throws Exception{
/**
* 单字分词
*/
Analyzer analyzer = new CJKAnalyzer(Version.LUCENE_30);
String text = “ 传智播客的黎活明是 UFO”;
this.testAnalyzer(analyzer, text);
}
@Test
public void testZH3() throws Exception{
Analyzer analyzer = new IKAnalyzer();
String text = “ 北京美女 ”;
this.testAnalyzer(analyzer, text);
}
/**
* 经过该方法可以把分词后的结果输出
* @param analyzer
* @param text
* @throws Exception
*/
private void testAnalyzer(Analyzer analyzer,String text)throws Exception{
TokenStream tokenStream = analyzer.tokenStream(“content”, new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while(tokenStream.incrementToken()){
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
}
}
说明:
1) 在全文检索系统中,一般查询出来的内容比较多,所以必须将查询出来的内容进行分页处理。
2) 原理同 hibernate 的分页查询。在 hibernate 的分页查询中,有两个参数:
int firstResult 当前页的第一行在数据库里的行数
int maxResult 每页显示的页数
3) 返回值不仅包括查询结果,还应该有总记录数,用 SearchResult 来封装
@Test
public void testCreateIndexBatch() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
for(int i=0;i<25;i++){
Article article = new Article();
article.setId(1L+i);
article.setTitle(“lucene 可以做搜索引擎 ”);
article.setContent(“baidu,google 都是很好的搜索引擎 ”);
indexWriter.addDocument(DocumentUtils.article2Document(article));
}
indexWriter.close();
}
/**
*
* @param firstResult
* 当前页的第一行在集合中的位置
* @param maxResult
* 每页显示的最大的行数
* @throws Exception
*/
public void testSearchIndexDispage(int firstResult,int maxResult) throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{“title”,”content”}, LuceneUtils.analyzer);
Query query = queryParser.parse(“lucene”);
TopDocs topDocs = indexSearcher.search(query,25);
int count = Math.min(topDocs.totalHits, firstResult+maxResult);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(int i=firstResult;i<count;i++){
Document document = indexSearcher.doc(scoreDocs[i].doc);
Article article = DocumentUtils.document2Article(document);
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
@Test
public void testDispage() throws Exception{
this.testSearchIndexDispage(10, 10);
}
搜索方式
/**
* 1、关键词查询
* 2、查询所有文档
* 3、范围查询
* 4、通配符查询
* 5、短语查询
* 6、Boolean 查询
* @author Administrator
*
*/
public class QueryTest {
/**
* 关键词查询
* 因为没有分词器,所以区分大小写
*/
@Test
public void testTermQuery() throws Exception{
Term term = new Term(“title”,”lucene”);
Query query = new TermQuery(term);
this.showData(query);
}
/**
* 查询所有的文档
*/
@Test
public void testAllDocQuery() throws Exception{
Query query = new MatchAllDocsQuery();
this.showData(query);
}
/**
* 通配符查询
* * 代表任意多个任意字符
* ? 任意一个任意字符
*/
@Test
public void testWildCartQuery() throws Exception{
Term term = new Term(“title”,”*.java”);
Query query = new WildcardQuery(term);
this.showData(query);
}
/**
* 短语查询
* 所有的关键词对象必须针对同一个属性
* @param query
* @throws Exception
*/
@Test
public void testPharseQuery() throws Exception{
Term term = new Term(“title”,”lucene”);
Term term2 = new Term(“title”,” 搜索 ”);
PhraseQuery query = new PhraseQuery();
query.add(term,0);
query.add(term2,4);
this.showData(query);
}
/**
* boolean 查询
* 各种关键词的组合
* @param query
* @throws Exception
*/
@Test
public void testBooleanQuery() throws Exception{
Term term = new Term(“title”,” 北京 ”);
TermQuery termQuery = new TermQuery(term);
Term term2 = new Term(“title”,” 美女 ”);
TermQuery termQuery2 = new TermQuery(term2);
Term term3 = new Term(“title”,” 北京美女 ”);
TermQuery termQuery3 = new TermQuery(term3);
BooleanQuery query = new BooleanQuery();
query.add(termQuery,Occur.SHOULD);
query.add(termQuery2,Occur.SHOULD);
query.add(termQuery3,Occur.SHOULD);
this.showData(query);
}
/**
* 范围查询
* @param query
* @throws Exception
*/
@Test
public void testRangeQuery() throws Exception{
Query query = NumericRangeQuery.newLongRange(“id”, 5L, 10L, true, true);
this.showData(query);
}
private void showData(Query query) throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
TopDocs topDocs = indexSearcher.search(query, 25);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
Document document = indexSearcher.doc(scoreDoc.doc);
Article article = DocumentUtils.document2Article(document);
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
}
排序 + 高亮
public class SortTest {
/**
* 1、相同的结构,相同的关键词,得分一样
* 2、相同的结构,不同的关键词,得分不一样,一般来说中文比较高
* 3、不同的结构,关键词出现的次数越多,得分越高
* 4、利用 document.setBoost(100) 可以人为的提高相关度得分
* @throws Exception
*/
@Test
public void testSearchIndex() throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{“title”,”content”}, LuceneUtils.analyzer);
Query query = queryParser.parse(“lucene”);
TopDocs topDocs = indexSearcher.search(query, 26);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
float score = scoreDoc.score;
System.out.println(score);// 相关的得分
Document document = indexSearcher.doc(scoreDoc.doc);
Article article = DocumentUtils.document2Article(document);
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
}
/**
* 高亮
* * 使关键字变色
* * 设置
* * 使用
* * 控制摘要的大小
*/
public class HighlighterTest {
@Test
public void testSearchIndex() throws Exception{
IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{“title”,”content”}, LuceneUtils.analyzer);
Query query = queryParser.parse(“Lucene”);
TopDocs topDocs = indexSearcher.search(query, 25);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
/***********************************************************************/
/**
* 给关键字加上前缀和后缀
*/
Formatter formatter = new SimpleHTMLFormatter(“<font color=’red’>”,”</font>”);
/**
* scorer 封装了关键字
*/
Scorer scorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(formatter,scorer);
/**
* 创建一个摘要
*/
Fragmenter fragmenter = new SimpleFragmenter(10);
highlighter.setTextFragmenter(fragmenter);
/***********************************************************************/
List<Article> articleList = new ArrayList<Article>();
for(ScoreDoc scoreDoc:scoreDocs){
float score = scoreDoc.score;
System.out.println(score);// 相关的得分
Document document = indexSearcher.doc(scoreDoc.doc);
Article article = DocumentUtils.document2Article(document);
/*
* 使用高亮器
*/
/**
* 1、分词器
* 查找关键词
* 2、字段
* 在哪个字段上进行高亮
* 3、字段的内容
* 把字段的内容提取出来
*/
String titleText = highlighter.getBestFragment(LuceneUtils.analyzer, “title”, document.get(“title”));
String contentText = highlighter.getBestFragment(LuceneUtils.analyzer, “content”, document.get(“content”));
if(titleText!=null){
article.setTitle(titleText);
}
if(contentText!=null){
article.setContent(contentText);
}
articleList.add(article);
}
for(Article article:articleList){
System.out.println(article.getId());
System.out.println(article.getTitle());
System.out.println(article.getContent());
}
}
}
IndexWriter
1) 利用这个类可以对索引库进行增、删、改操作。
2) 利用构造方法 IndexWriter indexWriter = newIndexWriter(directory,LuceneConfig.analyzer,MaxFieldLength.LIMITED)可以构造一个 IndexWriter 的对象。
3) addDocument 向索引库中添加一个 Document
4) updateDocument 更新一个 Document
5) deleteDocuments 删除一个 Document
5.1.2 Directory
指向索引库的位置,有两种 Directory
5.1.2.1FSDirectory
1) 通过 FSDirectory.open(new File(“./indexDir”))建立一个 indexDir 的文件夹,而这个文件夹就是索引库存放的位置。
2) 通过这种方法建立索引库时如果 indexDire 文件夹不存在,程序将自动创建一个,如果存在就用原来的这个。
3) 通过这个类可以知道所建立的索引库在磁盘上,能永久性的保存数据。这是优点
4) 缺点为因为程序要访问磁盘上的数据,这个操作可能引发大量的 IO 操作,会降低性能。
5.1.2.2RAMDirectory
1) 通过构造函数的形式 Directory ramdirectory = new RAMDirectory(fsdirectory)可以建立 RAMDirectory。
2) 这种方法建立的索引库会在内存中开辟一定的空间,通过构造函数的形式把 fsdirectory 移动到内存中。
3) 这种方法索引库中的数据是暂时的,只要内存的数据消失,这个索引库就跟着消失了。
4) 因为程序是在内存中跟索引库交互,所以利用这种方法创建的索引的好处就在效率比较高,访问速度比较快。
5.1.3 Document
1) 通过无参的构造函数可以创建一个 Document 对象。Document doc = new Document();
2) 一个 Directory 是由很多 Document 组成的。用户从客户端输入的要搜索的关键内容被服务器端包装成 JavaBean,然后再转化为 Document。这个转化过程的代码如下:
5.1.4 Field
1) Field 相当于 JavaBean 的属性。
2) Field 的用法为:
new Field(“title”,article.getTitle(),Store.YES,Index.ANALYZED)
a) 第一个参数为属性
b) 第二个参数为属性值
c) 第三个参数为是否往索引库里存储
d) 第四个参数为是否更新引索
1) NO 不进行引索
2) ANALYZED 进行分词引索
3) NOT_ANALYZED 进行引索,把整个输入作为一个词对待。
5.1.5 MaxFieldLength
a) 能存储的最大长度
b) 在 IndexWriter 的构造方法里使用
c) 值为:
1) LIMITED 限制的最大长度 值为 10000
2) UNLIMITED 没有限制的最大长度(一般不使用)