一、Lucene基础知识
1.1 数据分类
数据总体分为两种:结构化数据和非结构化数据。
结构化数据:指具有固定格式或有限长度的数据,如数据库、元数据等。
非结构化数据:指格式不固定或者长度不固定的数据例如文档。
1.2 非结构化数据查询的方法
1、顺序扫描法:
所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。
2、全文检索法:
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
这种先建立索引,再对索引进行搜索的过程就叫全文检索。
创建索引的过程虽然非常耗时,但是索引一旦创建成功,可以多次使用,大大缩短了全文检索的时间。
1.3 全文检索的实现
可以使用Lucene实现全文检索。Lucene是apache下的一个开放源代码的全文检索引擎工具包。提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能。
对于数据量大、数据结构不固定的数据可采用全文检索方式搜索,比如百度、Google等搜索引擎、论坛站内搜索、电商网站站内搜索等。
1.4 Lucene实现全文检索的流程
1、 对要搜索的原始内容进行索引构建一个索引库,索引过程包括:
确定原始内容即要搜索的内容–采集文档–创建文档–分析文档–索引文档
2、从索引库中搜索内容,搜索过程包括:
用户通过搜索界面–创建查询–执行搜索,从索引库搜索渲染搜索结果
1.5 Lucene创建索引过程
1、获取原始文档
原始文档是指要索引和搜索的内容。原始内容包括互联网上的网页、数据库中的数据、磁盘上的文件等。
从互联网上、数据库、文件系统中等获取需要搜索的原始信息,这个过程就是信息采集,信息采集的目的是为了对原始内容进行索引。
在Internet上采集信息的软件通常称为爬虫或蜘蛛,也称为网络机器人,爬虫访问互联网上的每一个网页,将获取到的网页内容存储起来。
Lucene不提供信息采集的类库,需要自己编写一个爬虫程序实现信息采集,也可以通过一些开源软件实现信息采集,如下:
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 开发的、开源的网络爬虫,用户可以使用它来从网上抓取想要的资源。其最出色之处在于它良好的可扩展性,方便用户实现自己的抓取逻辑。
2、创建文档对象
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。
这里我们可以将磁盘上的一个文件当成一个document,Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容)
注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)
每个文档都有一个唯一的编号,就是文档id。
3、分析文档
将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词。
每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的term。term中包含两部分一部分是文档的域名,另一部分是单词的内容。
4、创建索引
对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
注意:创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。
5、查询索引
查询索引也是搜索的过程。搜索就是用户输入关键字,从索引(index)中进行搜索的过程。根据关键字搜索索引,根据索引找到对应的文档,从而找到要搜索的内容。
二、Lucene开发
2.1 导入相应的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
2.2 创建索引库
步骤:
1、指定索引库的存放位置,获得Directory对象
2、创建索引写入对象IndexWriter
需要指定分词器(分析器)Analyzer对象
通过分词器创建IndexWriterConfig对象
创建IndexWriter,传入Directory对象以及IndexWriterConfig对象
3、创建Document对象
4、创建Field对象,并添加到Document对象中。
5、将Document对象添加到IndexWriter对象中,此过程会创建索引并把索引和Document对象写入索引库。
6、关闭IndexWriter对象,注意如果不关闭,则索引不会写入到索引库。
代码示例如下:
@Test
public void createIndex() throws IOException{
// 1 指定索引库位置 如果目录不存在会自动创建目录
Directory directory = FSDirectory.open(new File("G:/lucene/index"));
// 2 创建分析器 使用基本分析器
Analyzer analyzer = new StandardAnalyzer();
// 3 创建索引库设置对象 IndexWriterConfig
// 里面有两个参数,第一个是指定使用的lucene版本,latest表示最新的版本,第二个参数为分析器
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
// 4 创建索引库创建对象 IndexWriter
// 两个参数 第一个是索引库的位置, 第一个参数是设置参数
IndexWriter indexWriter = new IndexWriter(directory, config);
// 5 读取文件
File file = new File("G:\\黑马\\就业班\\Lucene&solr\\searchsource");
if(file != null ){
File[] listFiles = file.listFiles();
for (File file2 : listFiles) {
//获取文件名
String fileName = file2.getName();
//获取文件内容
String fileContent = FileUtils.readFileToString(file2);
//获取文件路径
String filePath = file2.getPath();
//获取文件大小
Long fileSize = FileUtils.sizeOf(file2);
//创建对应的域存储相应的信息
//创建文件名域 分析创建索引且存储
Field fileNameFiled = new TextField("fileName", fileName, Store.YES);
//创建文件内容域 分析创建索引不存储
Field fileContentField = new TextField("fileContent", fileContent, Store.NO);
//创建文件路径域 不分析不创建索引存储
Field filePathFIeld = new StoredField("filePath", filePath);
//创建文件大小域 分析索引以及存储
Field fileSizeField = new LongField("fileSize", fileSize, Store.YES);
Document doc = new Document();
//将域对象添加到documnet对象中
doc.add(fileNameFiled);
doc.add(fileContentField);
doc.add(filePathFIeld);
doc.add(fileSizeField);
//将document对象添加到indexWriter对象中
indexWriter.addDocument(doc);
}
}
//注意必须要关闭资源,不关闭资源的情况下会没有数据
indexWriter.close();
}
域对象Field属性:
是否分析:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。
是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
是否存储的标准:是否要将内容展示给用户
StringField(FieldName, FieldValue,Store.YES)),字符串类型数据, ,不分词创建索引,可存可不存。这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等)。
是否存储在文档中用Store.YES或Store.NO决定。
LongField(FieldName, FieldValue,Store.YES),Long型,分词,创建索引, 这个Field用来构建一个Long数字型Field,进行分析和索引,比如(价格)。
StoredField(FieldName, FieldValue) ,重载方法,支持多种类型 ,这个Field用来构建不同类型Field吗,不分析,不索引,但要Field存储在文档中。
TextField(FieldName, FieldValue, Store.NO)或TextField(FieldName, reader)字符串或流类型,分词,创建索引, 如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.
2.3 查询索引
步骤:
1、指定索引库,获得Directory对象
2、通过Directory对象获取IndexReader对象,,索引读取对象
3、通过IndexReader对象获取IndexSearcher对象 索引查询对象
4、创建查询条件Query对象
5、执行查询条件,获得结果集
6、通过结果集获取符合条件的Document对象的id
7、通过IndexReader对象根据id获取相应的document对象
8、通过document对象获取指定域中的数据
9、关闭IndexReader
示例代码如下:
@Test
public void readIndex() throws IOException{
// 1 创建索引库
Directory directory = FSDirectory.open(new File(directoryPath));
// 2创建IndexReader对象
IndexReader indexReader = DirectoryReader.open(directory);
// 3创建IndexSearcher对象
IndexSearcher indexSearch = new IndexSearcher(indexReader);
// 4创建查询条件
Query query = new TermQuery(new Term("fileName","spring.txt"));
// 5执行查询
TopDocs topDocs = indexSearch.search(query, 101);
System.out.println("总共有多少条记录:"+topDocs.totalHits);
// 6 scoreDoc中存储的是Document对象的id
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println("-----------------");
// 7根据id获取相应的document对象
Document doc = indexReader.document(scoreDoc.doc);
// 8 通过document对象取出域中的数据
System.out.println("文件的名称为:"+doc.get("fileName"));
System.out.println("文件的路径为:"+doc.get("filePath"));
System.out.println("文件的大小为:"+doc.get("fileSize"));
}
// 9关闭IndexReader对象
indexReader.close();
}
三、Lucene中文分词支持
3.1 分词器执行流程
从一个Reader字符流开始,创建一个基于Reader的Tokenizer分词器,经过三个TokenFilter生成语汇单元Token。
要看分析器的分析效果,只需要看Tokenstream中的内容就可以了。每个分析器都有一个方法tokenStream,返回一个tokenStream对象。
实例代码如下:
//查看标准分析器的分词效果
public void testTokenStream() throws Exception {
//创建一个标准分析器对象
Analyzer analyzer = new StandardAnalyzer();
//获得tokenStream对象
//第一个参数:域名,可以随便给一个
//第二个参数:要分析的文本内容
TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model.");
//添加一个引用,可以获得每个关键词
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();
}
3.2 中文分词器
1、Lucene自带的中文分词器
StandardAnalyzer:
单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,
效果:“我”、“爱”、“中”、“国”。
CJKAnalyzer
二分法分词:按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”“国人”。
上边两个分词器无法满足需求。
SmartChineseAnalyzer
对中文支持较好,但扩展性差,扩展词库,禁用词库和同义词库等不好处理
2、第三方中文分词器
paoding: 庖丁解牛最新版在 https://code.google.com/p/paoding/ 中最多支持Lucene 3.0,且最新提交的代码在 2008-06-03,在svn中最新也是2010年提交,已经过时,不予考虑。
mmseg4j:最新版已从 https://code.google.com/p/mmseg4j/ 移至 https://github.com/chenlb/mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代码是2014年6月,从09年~14年一共有:18个版本,也就是一年几乎有3个大小版本,有较大的活跃度,用了mmseg算法。
• IK-analyzer: 最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开 始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词 歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。 但是也就是2012年12月后没有在更新。
• ansj_seg:最新版本在 https://github.com/NLPchina/ansj_seg tags仅有1.1版本,从2012年到2014年更新了大小6次,但是作者本人在2014年10月10日说明:“可能我以后没有精力来维护ansj_seg了”,现在由”nlp_china”管理。2014年11月有更新。并未说明是否支持Lucene,是一个由CRF(条件随机场)算法所做的分词算法。
• imdict-chinese-analyzer:最新版在 https://code.google.com/p/imdict-chinese-analyzer/ , 最新更新也在2009年5月,下载源码,不支持Lucene 4.10 。是利用HMM(隐马尔科夫链)算法。
• Jcseg:最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有较高的活跃度。利用mmseg算法。
3、IKAnalyzer分词器使用
导入jar包 IKAnalyzerxxx.jar
将配置文件和停用词典以及扩展词典放到classpath目录下
注意词典必须使用utf-8无BOM编码
3.3 分词器的使用时机
1、索引时使用
输入关键字进行搜索,当需要让该关键字与文档域内容所包含的词进行匹配时需要对文档域内容进行分析,需要经过Analyzer分析器处理生成语汇单元(Token)。分析器分析的对象是文档中的Field域。当Field的属性tokenized(是否分词)为true时会对Field值进行分析。
对于一些Field可以不用分析:
1、不作为查询条件的内容,比如文件路径
2、不是匹配内容中的词而匹配Field的整体内容,比如订单号、身份证号等。
2、搜索时使用
对搜索关键字进行分析和索引分析一样,使用Analyzer对搜索关键字进行分析、分词处理,使用分析后每个词语进行搜索。比如:搜索关键字:spring web ,经过分析器进行分词,得出:spring web拿词去索引词典表查找 ,找到索引链接到Document,解析Document内容。
对于匹配整体Field域的查询可以在搜索时不分析,比如根据订单号、身份证号查询等。
注意:搜索使用的分析器要和索引使用的分析器一致。
四、索引库的维护
用到的获取IndexWriter对象的方法
public IndexWriter getIndexWriter() throws IOException{
String directoryPath = "G:/lucene/index";
Directory dirrctory = FSDirectory.open(new File(directoryPath));
Analyzer analyzer = new IKAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,analyzer);
IndexWriter indexWriter = new IndexWriter(dirrctory,config);
return indexWriter;
}
4.1 索引添加
步骤:
1、创建IndexWriter对象
2、创建Document对象
3、向Document对象中添加域对象
4、将Document对象添加到IndexWriter对象中
5、关闭IndexWriter
示例代码:
public void insert() throws IOException{
//打开一个索引库
Directory directory = FSDirectory.open(new File(directoryPath));
//创建分析器
Analyzer analyzer = new IKAnalyzer();
//设置索引库写入参数
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,analyzer);
//创建索引库写入对象
IndexWriter indexWriter = new IndexWriter(directory, config);
//创建Document
Document doc = new Document();
//其中设置域
doc.add(new TextField("fileName","新添加的文件名",Store.YES));
doc.add(new TextField("fileContent","新添加的文件内容",Store.NO));
//添加到IndexWriter中
indexWriter.addDocument(doc);
//关闭IndexWriter
indexWriter.close();
}
4.2 索引更新
索引更新的本质是,删除符合条件的索引,并添加新索引
步骤:
1、获得IndexWriter对象
2、创建新的Document,并添加相应的域对象
3、创建更新Term对象
4、执行更新操作
5、关闭IndexWriter对象
@Test
public void updateIndex() throws IOException{
//获取IndexWriter对象
IndexWriter indexWriter = getIndexWriter();
//创建Document对象
Document doc = new Document();
//添加相应的域对象
doc.add(new TextField("fileName","更新的文件名称", Store.YES));
doc.add(new TextField("fileContent","更新的内容",Store.YES));
//执行更新操作
indexWriter.updateDocument(new Term("fileContent","java"),doc);
//关闭IndexWriter
indexWriter.close();
}
4.3 索引删除
1、删除所有(慎用)
步骤:
(1)获取IndexWriter对象
(2)删除所有索引
(3)关闭IndexWriter
示例代码
@Test
public void deleteAll() throws IOException{
IndexWriter indexWriter = getIndexWriter();
//删除全部,删除之后无法找回,此方法慎用
indexWriter.deleteAll();
indexWriter.close();
}
2、删除指定索引(根据条件)
步骤:
(1)获取IndexWriter对象
(2)创建条件Query
(3)执行删除操作
(4)关闭IndexWriter
示例代码:
@Test
public void deleteByCondition() throws Exception{
IndexWriter indexWriter = getIndexWriter();
//创建条件
Query query = new TermQuery(new Term("fileContent","jasssva"));
//根据条件进行删除
indexWriter.deleteDocuments(query);
indexWriter.close();
}
五、 索引查询
对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法,类似关系数据库Sql语法一样Lucene也有自己的查询语法,比如:“name:lucene”表示查询Field的name为“lucene”的文档信息。
可通过两种方法创建查询对象:
1)使用Lucene提供Query子类
Query是一个抽象类,lucene提供了很多查询对象,比如TermQuery项精确查询,NumericRangeQuery数字范围查询等。
如下代码:
Query query = new TermQuery(new Term("name", "lucene"));
2)使用QueryParse解析查询表达式
QueryParse会将用户输入的查询表达式解析成Query对象实例。
如下代码:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");
下面操作用到的公共方法
获取IndexReder对象
public IndexReader getIndexReader()throws IOException{
String path = "G:/lucene/index";
Directory directory = FSDirectory.open(new File(path));
IndexReader indexReader = DirectoryReader.open(directory);
return indexReader;
}
进行查询操作,并遍历结果集
public void printResult(Query query,IndexSearcher search,IndexReader indexReader) throws IOException{
//执行查询操作,并获取符合条件的TopDocs。第一个参数是查询对象,第二个参数是返回的最大对象个数
TopDocs topDocs = search.search(query, 611);
//获取符合条件所有对象个数的方法
System.out.println("总共有多少条记录:"+topDocs.totalHits);
//ScoreDoc中封装了Document的id属性
ScoreDoc[] socerDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : socerDocs) {
//获取Doucument
Document doc = indexReader.document(scoreDoc.doc);
System.out.println("-----------------");
//输出指定的域中数据
System.out.println("文件的名称为:"+doc.get("fileName"));
System.out.println("文件的路径为:"+doc.get("filePath"));
System.out.println("文件的大小为:"+doc.get("fileSize"));
System.out.println("文件的大小为:"+doc.get("fileContent"));
}
}
5.1 查询所有MatchAllDocsQuery
使用MatchAllDocsQuery查询索引目录中的所有文档
@Test
public void testMatchAllDocsQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//创建查询条件
Query query = new MatchAllDocsQuery();
//执行查询,并输出查询结果
printResult(query, indexSearcher);
indexReader.close();
}
5.2 条件查询TermQuery
TermQuery,通过项查询,TermQuery不使用分析器所以建议匹配不分词的Field域查询,比如订单号、分类ID号等。
指定要查询的域和要查询的关键词。
//使用Termquery查询
@Test
public void testTermQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//创建查询对象
Query query = new TermQuery(new Term("fileContent", "lucene"));
//执行查询
printResult(query, indexSearcher);
indexReader.close();
}
5.3 条件查询NumericRangeQuery
可以根据数值进行查询
//数值范围查询
@Test
public void testNumericRangeQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//创建查询
//参数:
//1.域名
//2.最小值
//3.最大值
//4.是否包含最小值
//5.是否包含最大值
Query query = NumericRangeQuery.newLongRange("size", 1l, 1000l, true, true);
//执行查询
printResult(query, indexSearcher);
}
5.4 条件查询BooleanQuery
用于组合查询条件
//组合条件查询
@Test
public void testBooleanQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//创建一个布尔查询对象
BooleanQuery query = new BooleanQuery();
//创建第一个查询条件
Query query1 = new TermQuery(new Term("fileName", "apache"));
Query query2 = new TermQuery(new Term("fileContent", "apache"));
//组合查询条件
query.add(query1, Occur.MUST);
query.add(query2, Occur.MUST);
//执行查询
printResult(query, indexSearcher);
}
Occur.MUST:必须满足此条件,相当于and
Occur.SHOULD:应该满足,但是不满足也可以,相当于or
Occur.MUST_NOT:必须不满足。相当于not
5.5 条件解析查询QueryParse
通过QueryParser也可以创建Query,QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。Query对象执行的查询语法可通过System.out.println(query);查询。
需要使用到分析器。建议创建索引时使用的分析器和查询索引时使用的分析器要一致。
需要加入queryParser依赖的jar包,lucene-queryparser-xxx.jar
@Test
public void testQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//创建queryparser对象
//第一个参数默认搜索的域
//第二个参数就是分析器对象
QueryParser queryParser = new QueryParser("content", new IKAnalyzer());
//解析查询语句
Query query = queryParser.parse("Lucene是java开发的");
//执行查询
printResult(query, indexSearcher);
}
查询语法:
1、基础的查询语法,关键词查询:
域名+“:”+搜索的关键字
例如:fileContent:java
2、范围查询
域名+“:”+[最小值 TO 最大值]
例如:fileSize:[1 TO 1000]
范围查询在lucene中支持数值类型,不支持字符串类型。在solr中支持字符串类型。
3、组合条件查询
1)+条件1 +条件2:两个条件之间是并且的关系and
例如:+filename:apache +content:apache
2)+条件1 条件2:必须满足第一个条件,应该满足第二个条件
例如:+filename:apache content:apache
3)条件1 条件2:两个条件满足其一即可。
例如:filename:apache content:apache
4)-条件1 条件2:必须不满足条件1,要满足条件2
例如:-filename:apache content:apache
Occur.MUST 查询条件必须满足,相当于and +(加号)
Occur.SHOULD 查询条件可选,相当于or
空(不用符号)
Occur.MUST_NOT 查询条件不能满足,相当于not非 -(减号)
第二种写法:
条件1 AND 条件2
条件1 OR 条件2
条件1 NOT 条件2
5.6 多域查询MultiFieldQueryParser
可以指定多个默认搜索域
@Test
public void testMultiFiledQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//可以指定默认搜索的域是多个
String[] fields = {"fileName", "fileContent"};
//创建一个MulitFiledQueryParser对象
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer());
Query query = queryParser.parse("java AND apache");
System.out.println(query);
//执行查询
printResult(query, indexSearcher);
}