lucene简单应用

一直都想花时间学习一下lucene的,主要有两个原因没有这么做。一是因为版本更新太快,而且每个版本的变化都比较大。二是花的时间精力可能有点多。现在lucene3.0已经发布了,基本上已经比较稳定了,而且公司以后很可能也会用到lucene,所以就提前学习一下吧!
关于lucene3.0的资料,网上的确很少,所以研究学习起来,困难还是有的。
lucene主要做两件事,建立索引和查询索引。下面通过网上找到的一个例子做个简单的介绍。

	public static int index(File indexDir, File dataDir) throws IOException {

		if (!dataDir.exists() || !dataDir.isDirectory()) {
			throw new IOException(dataDir
					+ " does not exist or is not a directory");
		}
		Directory directory = FSDirectory.open(indexDir);
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
		IndexWriter writer = new IndexWriter(directory, analyzer, true,
				IndexWriter.MaxFieldLength.LIMITED);
		indexDirectory(writer, dataDir);
		int numIndexed = writer.numDocs();
		writer.optimize();
		writer.close();
		return numIndexed;
	}

这里需要解释几个概念:

Directory
这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有三个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。还有一个是FileSwitchDirectory,这个API先不要用,因为API文档注明过有可能在将来的版本进行修改。如果数据量不大,可以直接使用RAMDirectory,毕竟在内存中,数据的访问会更快。RAMDirectory还有一个地方比较常用,那就是单元测试,可以在初始化测试方法时创建索引,测试完成后删除索引,这样就不会留下不必要的垃圾文件。

Analyzer
在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer,中文分词也就是在这里实现一个中文分词的解析器。Analyzer的处理对象必须是Document,

IndexWriter
IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象拿给Analyzer处理。

private static void indexDirectory(IndexWriter writer, File dir)
			throws IOException {

		File[] files = dir.listFiles();

		for (int i = 0; i < files.length; i++) {
			File f = files[i];
			if (f.isDirectory()) {
				indexDirectory(writer, f); // recurse
			} else if (f.getName().endsWith(".txt")) {
				indexFile(writer, f);
			}
		}
	}


private static void indexFile(IndexWriter writer, File f)
			throws IOException {

		if (f.isHidden() || !f.exists() || !f.canRead()) {
			return;
		}
		System.out.println("Indexing " + f.getCanonicalPath());
		Document doc = new Document();
		doc.add(new Field("contents", new FileReader(f)));
		doc.add(new Field("filename", f.getCanonicalPath(), Field.Store.YES,
				Field.Index.ANALYZED));

		writer.addDocument(doc);
	}


其中document是field的集合,field是term的集合。term是一个二元组,形式如<FieldName,Text>。

上面用到了Field.Index与Field.Store的字段,下面将对它们内部的每个字段都介绍一下:

Index.ANALYZED
通过分析器将输入文本分解成可独立查询的词汇单元,这对于正式的文本字段很有用,如body,title等。
Index.NOT_ANALYZED
对这个字段建立索引,但不对该内容进行分解,把整个字段的内容当做单个可查询的词汇单元。如URLS,filepath,dates,personal names,social security numbers,telephone number等。对于“精确匹配”查找也非常有用。
Index.ANALYZED_NO_NORMS
这是一个比较高级一些变量,表示不在索引中存储关于norms的信息。而norms是一个对索引查询进行加分的一个记录。
Index.NOT_ANALYZED_NO_NORMS
这个变量与上面的解释差不多,只是综合了一下。
Index.NO
不让字段的值在查询索引时可用。

Store.YES
存储字段,而且彻底存储的内容。这样IndexReader(IndexSearcher)查询索引时,就可以查询到字段的内容。这对于想显示查询结果内容的字段来说很有用,如URL,title,ID。一般不要存储非常大的字段。
Store.NO
与上面的字段相反,这个字段经常伴随着Index.ANALYZED一起使用,用于只需要查询结果而不需要查询内容的文本字段。

建立索引直接调用index方法即可,其中一个参数表示创建的索引存放的目录,另一个参数表示需要建立索引的存放txt文件的目录。

索引建立好以后,就可以查询了,查询的步骤相对来说,要简单一些:

 public static void search(File indexDir, String q) throws Exception {   
	    IndexSearcher is = new  IndexSearcher(FSDirectory.open(indexDir),true);//read-only   
	    String field = "contents";   
	       
	    QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, field, new StandardAnalyzer(Version.LUCENE_CURRENT));
	    Query query = parser.parse(q);   
	       
            //返回匹配的前两条记录
	    TopDocs topDocs = is.search(query, 2);
	    ScoreDoc[] hits = topDocs.scoreDocs;  
	  
	    for (int i = 0; i < hits.length; i++) {   
	        Document doc = is.doc(hits[i].doc);//new method is.doc()   
	        System.out.println(doc.getField("filename")+"   "+hits[i].toString()+"  ");   
	    }   
	  }


上面需要解释的是TopDocs类,它表示一个存放前N条查询记录的容器。ScoreDoc表示具体存放内容,有点领域模型的感觉。ScoreDoc其本身包含两种属性,一是document,另一种是score。上面方法的参数q表示需要查询的关键字。

这样,一个简单的lucene例子就完成了。关于lucene的内容非常的多,一一讲解的确需要很多时间,以后会尽快讲解一下更多的内容。不过从上面的基本应用可以看出,lucene API设计的确有些问题,明显违背面向对象的原则,比如通过属性访问字段,而不是通过公共方法来访问。不过由于其应用广泛,性能优越,也顾不了那么多了,先学着吧!

你可能感兴趣的:(单元测试,F#,Lucene,领域模型,Social)