索引
全文检索:
全文索引时将存储在数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获取全文中有关章,节,段,句,词等信息,也可以进行各种统计和分析。
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此又一定结构的数据进行搜索,从而达到搜索相对较快的目的。根据文档建立索引再通过索引搜索称为全文索引
倒排索引:
全文索引通常使用倒排索引来实现。倒排索引同B+树索引一样,也是一种索引结构。
注意:创建索引是对语汇单元索引,通过词语找文档
全文索引的搜索流程:
原始文档===》创建索引===》建立索引库===》提供查询接口
创建索引的流程:
确定要搜索的内容===》获取原始文档===》创建文档===》分析文档===》索引文档
搜索的过程:
用户通过搜索界面===》创建查询===》执行搜索===》渲染结果
原始文件的获取
原始文件可以使互联网的网页、数据库的数据、磁盘上的文件
互联网上的数据可以使用爬虫技术将网页内容存储:
Nutch(http://lucene.apache.org/nutch), Nutch是apache的一个子项目,包括大规模爬虫工具,能够抓取和分辨web网站数据。
jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一个由 java 开发的、开源的网络爬虫,用户可以使用它来从网上抓取想要的资源。其最出色之处在于它良好的可扩展性,方便用户实现自己的抓取逻辑。
创建文档对象
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。
注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)
每个文档都有一个唯一的编号,就是文档id。
Lunece下载
Lucene是开发全文检索功能的工具包,并解压。
需要的jar包:
Lucene包:
lucene-core-4.10.3.jar
lucene-analyzers-common-4.10.3.jar
lucene-queryparser-4.10.3.jar
其它:
commons-io-2.4.jar
junit-4.9.jar
创建索引库
第一步: Eclipse创建java工程,导入jar包
第二步:创建一个indexwriter对象
1.指定索引库的存放位置Directory
2.指定一个分析器,对文档内容进行分析
第三步:创建document对象
第四步:创建field对象,将field添加到document对象中。
第五步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。
第六步:关闭IndexWriter对象。
Field域的属性
Field类 | 数据类型 | Analyzed是否分析 | Indexed是否索引 | Stored是否存储 | 说明 |
---|---|---|---|---|---|
StringField(FieldName, FieldValue,Store.YES)) | 字符串 | N | Y | Y或N | 这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中 |
LongField(FieldName, FieldValue,Store.YES) | Long型 | Y | Y | Y或N | 这个Field用来构建一个Long数字型Field,是否存储在文档中用Store.YES或Store.NO决定 |
StoredField(FieldName, FieldValue) | 重载方法,支持多种类型 | N | N | Y | 这个Field用来构建不同类型Field不分析,不索引,但要Field存储在文档中 |
TextField(FieldName, reader)或TextField(FieldName, FieldValue, Store.NO) | 流或字符串 | Y | Y | Y或N | 如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略. |
编写程序建立索引文件
// 创建索引方法与步骤
@Test
public void testLucene() throws Exception {
// 1.创建java工程,导入jar包
// 2.创建一个indexwriter对象File System Directory(FSDirectory对象)
//该路径是将创建的索引存放的位置
Directory director = FSDirectory.open(new File("D:\\javacode\\fileupload\\tmp"));
//标准分析器
Analyzer analy = new StandardAnalyzer();
//索引写配置
IndexWriterConfig condig = new IndexWriterConfig(Version.LATEST, analy);
IndexWriter indexw = new IndexWriter(director, condig);
// 3.创建document对象
Document document = new Document();
// 4.创建field域对象
File ff = new File(
"E:\\百度云\\00_黑马JavaEE32期\\02_2016年7月北京黑马32期就业班\\【阶段14】SSM综合练习\\ssm综合练习\\day77\\Lucene&solr\\01.参考资料\\mysource");
File[] listfile = ff.listFiles();
for (File file : listfile) {
// 获取文件姓名
String file_name = file.getName();
// lunece中对于域的存储Field有四个子类:
// TextField是对文件该文件进行拆除,建立索引,并保存,常对于姓名等文件
// LongField建立long型的域,用于建立文件大小的索引
// StoredField表示不进行拆分不进行索引但是进行保存,用于路径的域索引
//
Field fileNameField = new TextField("fileName", file_name, Store.YES);
// 获取文件大小
long file_size = FileUtils.sizeOf(file);
Field fileSizeField = new LongField("fileSize", file_size, Store.YES);
// 文件路径
String file_path = file.getPath();
Field filePathField = new StoredField("filePath", file_path);
// 文件内容
String file_content = FileUtils.readFileToString(file);
Field fileContentField = new TextField("fileContent", file_content, Store.NO);
document.add(fileNameField);
document.add(fileSizeField);
document.add(filePathField);
document.add(fileContentField);
// 使用Indexwriter索引对象将field添加到document对象中,创建索引并将源文件写入
indexw.addDocument(document);
}
// 5.关闭Indexwriter对象
indexw.close();
}
使用like查看索引
查询索引
步骤:
第一步:创建一个Directory对象,也就是索引库存放的位置。
第二步:创建一个indexReader对象,需要指定Directory对象。
第三步:创建一个indexsearcher对象,需要指定IndexReader对象
第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
第五步:执行查询。
第六步:返回查询结果。遍历查询结果并输出。
第七步:关闭IndexReader对象
IndexSearch搜索方法
方法 | 说明 |
---|---|
indexSearcher.search(query, n) | 根据Query搜索,返回评分最高的n条记录 |
indexSearcher.search(query, filter, n) | 根据Query搜索,添加过滤策略,返回评分最高的n条记录 |
indexSearcher.search(query, n, sort) | 根据Query搜索,添加排序策略,返回评分最高的n条记录 |
indexSearcher.search(booleanQuery, filter, n, sort) | 根据Query搜索,添加过滤策略,添加排序策略,返回评分最高的n条记录 |
查询程序
// 根据索引库,进行搜索程序
@Test
public void findIndex() throws Exception {
// 1.创建一个Directory对象,指向索引库位置
Directory directory = FSDirectory.open(new File("D:\\javacode\\fileupload\\tmp"));
// 2.创建一个indexReader对象,需要指定的Directory对象
IndexReader indexRead = DirectoryReader.open(directory);
// 3.创建一个indexSearcher对象,需要指定IndexReader对象
IndexSearcher indexSearch = new IndexSearcher(indexRead);
// 4.创建一个TermQuery对象,指定查找的关键词以及查找的域
// 搜索的方法有四个:
// 1.indexSearcher.search(query,n)返回最高的n条记录
// 2.indexSearcher.search(query,filter,n)添加过滤策略,返回最高n条记录
// 3.indexSearcher.search(query,n,sort)添加排序策略,返回评分最高n条记录
// 4.indexSearcher.search(booleanQuery,filter,n,sort)添加过滤策略,添加排序策略,返回n条记录
Query query = new TermQuery(new Term("fileName", "java.txt"));
// 5.执行查询
TopDocs topres = indexSearch.search(query, 6);
// 6.返回查询结果,遍历查询结果并输出
ScoreDoc[] scoreDocs = topres.scoreDocs;
for (ScoreDoc scdoc : scoreDocs) {
int doc = scdoc.doc;
Document document = indexSearch.doc(doc);
String fileName = document.get("fileName");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
String fileContent = document.get("fileContent");
System.out.println("fileName" + fileName + "filePath" + filePath + "fileSize" + fileSize + "fileContent"
+ fileContent);
System.out.println("=====================");
}
// 7.关闭IndexReader对象
indexRead.close();
}
Search方法需要指定匹配记录数量:indexSearcer.search(query,n)
TopDocs.totalHits:匹配索引库中所有记录的数量
TopDocs.scoreDocs:匹配相关度高的前边记录数组,scoreDocs的长度小于等于search方法指定的参数n
三种分词器的测试:
// 查看标准分析器的分词效果
@Test
public void testTokenStream() throws Exception {
// 1.创建一个标准分析器对象
//Analyzer analyzer = new StandardAnalyzer();
// 2.二分法分词器
// Analyzer analyzer = new CJKAnalyzer();
// 3.SmartChineseAnalyzer分词器,不可扩展
// 4.第三方分词器,可扩展
Analyzer analyzer = new IKAnalyzer();
// 获得tokenStream对象
// 第一个参数:域名,可以随便给一个
// 第二个参数:要分析的文本内容
TokenStream tokenStream = analyzer.tokenStream("test", "我爱中国");
// 添加一个引用,可以获得每个关键词
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
// 将指针调整到列表的头部
tokenStream.reset();
// 遍历关键词列表,通过incrementToken方法判断列表是否结束
while (tokenStream.incrementToken()) {
// 关键词的起始位置
System.out.println("start->" + offsetAttribute.startOffset());
// 取关键词
System.out.println(charTermAttribute);
// 结束位置
System.out.println("end->" + offsetAttribute.endOffset());
}
tokenStream.close();
}
IK分词器的配置:文件命名IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典,在同级目录下创建ext.dic,自定义扩展的词-->
<entry key="ext_dict">ext.dic;</entry>
<!--用户可以在这里配置自己的扩展停止词字典,自定义停用的词-->
<entry key="ext_stopwords">stopword.dic;</entry>
</properties>
第三方分析器:
paoding: 庖丁解牛 https://code.google.com/p/paoding/
mmseg4j:https://github.com/chenlb/mmseg4j-solr
IK-analyzer:https://code.google.com/p/ik-analyzer/
ansj_seg: https://github.com/NLPchina/ansj_seg
Jcseg:开源中国中搜索jcseg
索引库的维护
向索引库添加document对象:
第一步:先创建一个indexwriter对象
第二步:创建一个document对象
第三步:把document对象写入索引库
第四步:关闭indexwriter
新增索引
//创建方法获取IndexWriter对象
public IndexWriter getIndexWriter() throws Exception {
Directory directory=FSDirectory.open(new File("D:\\\\javacode\\\\fileupload\\\\tmp"));
Analyzer analyzer=new StandardAnalyzer();
IndexWriterConfig condig=new IndexWriterConfig(Version.LATEST,analyzer);
IndexWriter indexw=new IndexWriter(directory,condig);
return indexw;
}
//添加索引操作
@Test
public void testUpdate() throws Exception {
//获取IndexWriter
IndexWriter indexw=getIndexWriter();
//创建Document元素
Document doc=new Document();
doc.add(new TextField("FileN","测试文件名",Store.YES));
doc.add(new TextField("FileC","测试文件名",Store.YES));
indexw.addDocument(document);
indexw.close();
}
删除索引
//获取IndexReader对象
public IndexSearcher getIndexReader() throws Exception {
//1.创建Directory对象,索引库的位置
Directory directory=FSDirectory.open(new File("D:\\\\javacode\\\\fileupload\\\\tmp"));
//2.创建一个IndexReader对象,需要指定Directory对象
IndexReader indexR = DirectoryReader.open(directory);
//根据IndexReader创建IndexSearch对象
IndexSearcher indexSearch=new IndexSearcher(indexR);
//3.返回该对象
return indexSearch;
}
//删除索引(全部)
public void testAllDelete() throws Exception {
//1.获取indexReader对象
IndexWriter indexw=getIndexWriter();
//2.删除所有索引
indexw.deleteAll();
//3.关闭indexWriter对象
indexw.close();
}
//条件删除索引
public void testDelete() throws Exception {
//1.获取indexReader对象
IndexWriter indexw=getIndexWriter();
//2.添加条件
Query query=new TermQuery(new Term("fileName","apache"));
//2.删除所有索引
indexw.deleteDocuments(query);
//3.关闭indexWriter对象
indexw.close();
}
修改索引库文件
//创建方法获取IndexWriter对象
public IndexWriter getIndexWriter() throws Exception {
Directory directory=FSDirectory.open(new File("D:\\\\javacode\\\\fileupload\\\\tmp"));
Analyzer analyzer=new StandardAnalyzer();
IndexWriterConfig condig=new IndexWriterConfig(Version.LATEST,analyzer);
IndexWriter indexw=new IndexWriter(directory,condig);
return indexw;
}
//修改操作
@Test
public void testUpdate() throws Exception {
//获取IndexWriter
IndexWriter indexw=getIndexWriter();
//创建Document元素
Document doc=new Document();
doc.add(new TextField("FileN","测试文件名",Store.YES));
doc.add(new TextField("FileC","测试文件名",Store.YES));
//更改名字为java的索引
indexw.updateDocument(new Term("fileName","java"),doc, new IKAnalyzer());
}
查询索引(难点)
1)使用Lucene提供Query子类
Query是一个抽象类,lucene提供了很多查询对象,比如TermQuery项精确查询,NumericRangeQuery数字范围查询等。
2)使用QueryParse解析查询表达式
QueryParse会将用户输入的查询表达式解析成Query对象实例。
Query子类查询
1.查询索引目录中的所有文档
//查询操作,使用IndexReader对象
@Test
public void testSelect() throws Exception {
//1.获取indexReader对象
IndexSearcher indexSearch = getIndexReader();
//2.创建Qurey对象
Query query=new MatchAllDocsQuery();
//打印查询结果
printResult(indexSearch,query);
//关闭资源
indexSearch.getIndexReader().close();
}
2.Term查询
TermQuery不使用分析器所以建议匹配不分词的Field域查询
//使用Termquery查询
@Test
public void testTermQuery() throws Exception {
IndexSearcher indexSearcher = getIndexReader();
//创建查询对象
Query query = new TermQuery(new Term("content", "lucene"));
//执行查询
TopDocs topDocs = indexSearcher.search(query, 10);
//共查询到的document个数
System.out.println("查询结果总数量:" + topDocs.totalHits);
//遍历查询结果
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("filename"));
//System.out.println(document.get("content"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
}
//关闭indexreader
indexSearcher.getIndexReader().close();
}
3.数值范围的查询
//根据数值类型进行查询
@Test
public void testresByNumber() throws Exception {
//1.获取indexReader对象
IndexSearcher indexSearch = getIndexReader();
//2.创建Qurey对象
Query query=NumericRangeQuery.newLongRange("fieldSize", 50l, 200l, true, true);
//打印查询结果
printResult(indexSearch,query);
//关闭资源
indexSearch.getIndexReader().close();
}
4.组合查询
//组合查询
@Test
public void testBooleanQuery() throws Exception {
//获取搜索对象
IndexSearcher indexs=getIndexReader();
//创建组合查询对象
BooleanQuery booleanquery=new BooleanQuery();
//添加索引查询条件
Query query1=new TermQuery(new Term("filedName","apache"));
Query query2=new TermQuery(new Term("fieldName","lunece"));
//将查询呢条件添加到查询对象中
booleanquery.add(query1,Occur.MUST);
booleanquery.add(query2,Occur.MUST);
//根据搜索对象以及查询条件查询
printResult(indexs,booleanquery);
//关闭资源
indexs.getIndexReader().close();
}
QueryParse
//条件解析的对象查询
@Test
public void testQueryParser() throws Exception {
IndexSearcher indexReader = getIndexReader();
//参数 默认查询的域
QueryParser queryParser = new QueryParser("fieldName",new IKAnalyzer());
//*:* 域:值
// Query query = queryParser.parse("*:*");
//多个域查询使用MultiFieldQueryParser对象
Query query = queryParser.parse("fieldName:apache AND fieldName:lunece");
printResult(indexReader,query);
indexReader.getIndexReader().close();
}
Solr
使用Lucene实现站内搜索需要开发的工作量较大,主要表现在:索引维护、索引性能优化、搜索性能优化等.
Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
Solr可以独立运行,运行在Jetty、Tomcat等这些Servlet容器中,Solr 索引的实现方法很简单,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr根据xml文档添加、删除、更新索引 。Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr 返回Xml、json等格式的查询结果进行解析,组织页面布局。Solr不提供构建UI的功能,Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况
Solr与Lunece的区别
Lucene是一个开放源代码的全文检索引擎工具包,它不是一个完整的全文检索引擎,Lucene提供了完整的查询引擎和索引引擎,目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者以Lucene为基础构建全文检索引擎。
Solr的目标是打造一款企业级的搜索引擎系统,它是一个搜索引擎服务,可以独立运行,通过Solr可以非常快速的构建企业的搜索引擎,通过Solr也可以高效的完成站内搜索功能。
Solr下载
文件夹结构:
bin:solr的运行脚本
contrib:solr的一些贡献软件/插件,用于增强solr的功能。
dist:该目录包含build过程中产生的war和jar文件,以及相关的依赖文件。
docs:solr的API文档
example:solr工程的例子目录:
example/solr:
该目录是一个包含了默认配置信息的Solr的Core目录。
example/multicore
该目录包含了在Solr的multicore中设置的多个Core目录。
example/webapps:
该目录中包括一个solr.war,该war可作为solr的运行实例工程。
licenses:solr相关的一些许可信息
(还不是很清楚,后期补充)