对使用Lucene的一点点认识

jopen 10年前

就我而言我使用Lucene就是为了搜索而已,所以我就以这个目的来描述这个笔记。我使用Lucene的步骤如下:

1:创建索引

2:学习有哪些过滤器

3:学习有哪些查询器(很多)

4:学习有哪些分词器(很多,很重要)

1)创建索引

/**

 * @param  args

 * 这个是在文档目录中增加

这个例子是把docs目录下的文件都添加所以到index文件里面

 */

public static void main(String[] args)  throws Exception{

String indexPath="C:\\Users\\Administrator\\Workspaces\\MyEclipse9\\Lucene01\\index";

String docsPath="C:\\Users\\Administrator\\Workspaces\\MyEclipse9\\Lucene01\\docs";

//这段代码我们注意一下,因为我们创建所以的目录有两种方式,一种是真实的文件目录,一种是内存目录,一般内存目录是为了我们方便测试用的,我们这儿用的是真实的目录

Directory dir=FSDirectory.open(new File(indexPath));

Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_45);

IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_45,analyzer);

IndexWriter writer=new IndexWriter(dir,iwc);

File docDir=new File(docsPath);

indexDocs(writer,docDir);

writer.close();

dir.close();

}

public static void indexDocs(IndexWriter writer,File file) throws Exception{

if(file.canRead()){

if(file.isDirectory()){

String[] files=file.list();

if(files!=null){

for (int i = 0; i < files.length; i++) {

System.out.println(files[i]+"2");

//根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

//File(File parent, String child) 

indexDocs(writer,new File(file,files[i]));

}

}

}else{

FileInputStream fis ;

try {

fis = new FileInputStream(file);

catch (FileNotFoundException e) {

return;

}

Document doc = new Document();

Field PathField = new StringField("path",file.getPath(),Field.Store.YES);

doc.add(PathField);

doc.add(new LongField("modified",file.lastModified(),Field.Store.NO));

try {

doc.add(new TextField("contents",new BufferedReader(new InputStreamReader(fis,"UTF-8"))));

writer.addDocument(doc);

catch (UnsupportedEncodingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

finally {

fis.close();

}

System.out.println("writer.addDocument(doc)-file.getPath():"+file.getPath());

System.out.println("writer.numDocs():"+writer.numDocs());

System.out.println("writer.maxDoc():"+writer.maxDoc());

System.out.println("writer.numRamDocs():"+writer.numRamDocs());

}

}

}

(2)过滤器

我感觉不是很重要,所以这儿就不提了。

(3)查询器

很多,但是也好理解,我的有很多代码的例子,所以这儿也不说了。

4)分词器

我们的分词器有很多,但是常用的就有一下几种(针对的是西文的分词器)

,我们可以到文档之中去查找。

1:StandardAnalyzer支持汉语,混合分词。

2:StopAnalyzer去掉一些连接词,is,or啊之类的无意义的词,还有空格之类的连接词。

3:SimpleAnalyzer就是分割一些空格,连接符之类的。

如何使用呢?

public static void main(String[] args) throws Exception{

Directory dir=new RAMDirectory();

//想用成什么样的分词器就写什么样的分词器,包括写成后面说的中文分词器

Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_45);

IndexWriterConfig iwriter=new IndexWriterConfig(Version.LUCENE_45,analyzer);

IndexWriter writer=new IndexWriter(dir,iwriter);

Document doc=new Document();

Field f=new TextField("content","xy&z mail is - xyz@hello.com,中文",Store.YES);

doc.add(f);

writer.addDocument(doc);

writer.close();

DirectoryReader dr= DirectoryReader.open(dir);

IndexSearcher is=new IndexSearcher(dr);

    QueryParser parser=new QueryParser(Version.LUCENE_45,"content",analyzer);

Query query=parser.parse("\"xyz@hello.com,中文\"");

ScoreDoc[] hits=is.search(query, 100).scoreDocs;

System.out.println(query.toString());

for (int i = 0; i < hits.length; i++) {

Document document=is.doc(hits[i].doc);

System.out.println(hits[i]+"content:"+document.get("content"));

}

dr.close();

dr.close();

}

下面这段代码是验证上面提的这些分词器是如何分词的,打印分词的效果。

public static void main(String[] argsthrows Exception {

//Analyzer analyzer=new StandardAnalyzer(Version.LUCENE_45);

Analyzer analyzer=new SimpleAnalyzer(Version.LUCENE_45);

//获取lucenetokenstream对象

TokenStream ts=analyzer.tokenStream("content"new StringReader("xy&z mail is - xyz@hello.com,中文"));

//获取词源位置属性

OffsetAttribute offset=ts.addAttribute(OffsetAttribute.class);

CharTermAttribute term=ts.addAttribute(CharTermAttribute.class);

//获取词源文本属性

TypeAttribute type=ts.addAttribute(TypeAttribute.class);

ts.reset();

while(ts.incrementToken()){

System.out.println(offset.startOffset()+"-"+offset.endOffset()+":"+term.toString());

}

ts.end();

ts.close();

}

打印结果:

0-2:xy|word

3-4:z|word

5-9:mail|word

10-12:is|word

15-18:xyz|word

19-24:hello|word

25-28:com|word

29-31:中文|word

重点介绍的是中文的分词器:

IK Analyzer

它的安装部署十分简单,将IKAnalyzer2012.jar部署亍项目的lib目彔中;IKAnalyzer.cfg.xml和 stopword.dic文件放置在class根目彔(对亍web项目,通常是WEB-INF/classes目彔,同hibernate、log4j等配置文件相同)下即可。

使用方法跟上面是一模一样的,具体效果是什么样,可以自己测试,也可以看它的文档。

综合使用

思考1

我这儿创建索引的时候针对的都是txt文档,要是还有其他的比如视频格式,PDFXML等等之类的格式呢?

好办,我们用tika这个包。使用代码如下:

/**初始化给目录下的所有文件都建索引

 * @param  args

 */

public static void main(String[] args) throws Exception {

String indexPath=INDEX;

String docsPath=INDEX_PATH.replace(".","\\");

Directory dir=FSDirectory.open(new File(indexPath));

Analyzer analyzer=new IKAnalyzer(true);

IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_43,analyzer);

iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);

IndexWriter writer=new IndexWriter(dir,iwc);

File file=new File(docsPath);

indexDocs(writer,file);

writer.close();

dir.close();

}

public static void indexDocs(IndexWriter writer,File file) throws IOException {

if(file.canRead()){

if(file.isDirectory()){//如果是文件夹

String[] filelist=file.list();

if(filelist!=null){

for (int i = 0; i < filelist.length; i++) {

indexDocs(writer,new File(file,filelist[i]));

}

}

}else{

//如果不是文件夹

FileInputStream fis;

try {

fis = new FileInputStream(file);

catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

return ;

}

Document doc=new Document();

Field PathField = new StringField(PATH_FIELD_NAME,file.getPath(),Field.Store.YES); 

doc.add(PathField);

doc.add(new LongField(MODIFIED_FIELD_NAME,file.lastModified(),Field.Store.NO));

//我们用tika来解析我们的流

try{

ContentHandler handler = new BodyContentHandler();

Parser parser = new AutoDetectParser();

Metadata meta = new Metadata();

parser.parse(fis, handler, meta, new ParseContext());

doc.add(new TextField(CONTENTS_FIELD_NAME,""+file.getName()+""+handler.toString(),Field.Store.YES));

writer.updateDocument(new Term(PATH_FIELD_NAME, file.getPath()),doc);

writer.commit();

}catch (Exception e){

e.printStackTrace();

}finally{

fis.close();

}

System.out.println("%%%%%%%%%%%%%%%%%%%%");

System.out.println("writer.updateDocument(doc)-file.getPath():"+file.getPath());

System.out.println("writer.numDocs():"+writer.numDocs());

System.out.println("writer.maxDoc():"+writer.maxDoc());

System.out.println("writer.numRamDocs():"+writer.numRamDocs());

}

}

}

 

}

 

Tika相当于是一个万能的解析工具了。

 

思考2

百度文库人家查出来你要搜索的词包含在文中,文中使用高亮显示的啊,我能吗?

能!

代码如下:

public void Searcher(String danci,Integer pageno,Integer pagesize)throws Exception{

String indexPath = INDEX;

Directory dir = FSDirectory.open(new File(indexPath));

Analyzer analyzer = new IKAnalyzer(true);

DirectoryReader ireader = DirectoryReader.open(dir);

IndexSearcher searcher = new IndexSearcher(ireader);

QueryParser parser = new QueryParser(Version.LUCENE_43,CONTENTS_FIELD_NAME,analyzer); 

Query query = parser.parse(danci);

System.out.println("QueryParser:"+query.toString());

if(pageno*pagesize>querysize){

querysize=pageno*pagesize;

}

ScoreDoc[] hits = searcher.search(query, querysize).scoreDocs;

//高亮设置

SimpleHTMLFormatter simpleHtmlFormatter = new SimpleHTMLFormatter("<font color=red>","</font>");

Highlighter highlighter = new Highlighter(simpleHtmlFormatter,new QueryScorer(query));

highlighter.setTextFragmenter(new SimpleFragmenter(150));

Integer start=(pageno-1)*pagesize;

Integer end=pageno*pagesize;

hitslength=hits.length;

if(hits.length>start){

if(pagesize*pageno>hits.length) end=hits.length;

for (Integer i=start;i<end;i++){

fileatr=new FileAtr();

Document doc = searcher.doc(hits[i].doc);

String strpath = doc.get(PATH_FIELD_NAME);

fileatr.setLujing(strpath);//添加文件的路径

fileatr.setFileName(getFileNameByPath(strpath));

//这儿也是为了高亮显示

TokenStream tokenStream = analyzer.tokenStream(CONTENTS_FIELD_NAMEnew StringReader(doc.get(CONTENTS_FIELD_NAME)));

String str = highlighter.getBestFragment(tokenStream, doc.get(CONTENTS_FIELD_NAME));

fileatr.setContents(str);

filelist.add(fileatr);

}

}

System.out.println(hits.length+"这是多少");

pagecount=hits.length/pagesize;

if( pageno >= pagecountpagecount = pageno;

//  System.out.prIntegerln("ireader.numDeletedDocs()"+ireader.numDeletedDocs());

//  System.out.prIntegerln("ireader.numDocs()"+ireader.numDocs());

//  System.out.prIntegerln("ireader.maxDoc()"+ireader.maxDoc());

ireader.close();

dir.close();

}