`
85600367
  • 浏览: 37655 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

Lucene对本地文件多目录创建索引

阅读更多
jar包版本说明:
lucene 版本2.9(当时是为了和solr1.4一起用,不过换成Lucene3.0的jar包代码是不用修改的)
ik分词器 版本3.1.6(说起来还是solr,高版本的死活配不上去,汗···)
jsoup 1.4.1用来解析HTML的工具包很好用。(比htmlparser好用多了 呵呵个人意见)

参考范围,本文仅限对TXT,HTML,HTM文件的内容创建索引。

创建索引的代码如下:
public class IndexJob {
	public static Date beginTime;

	//读取TXT文件内容
	private static String loadFileToString(File file) {
		try {
			InputStreamReader isr = new InputStreamReader(new FileInputStream(
					file), "UTF-8");
			BufferedReader br = new BufferedReader(isr);
			StringBuffer sb = new StringBuffer();
			String line = br.readLine();
			while (line != null) {
				sb.append(line);
				line = br.readLine();
			}
			br.close();
			return sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	//提取HTML文件的文本内容
	private static String getDocument(File html) {
		String text = "";
		try {
			//设置编码集
//			org.jsoup.nodes.Document doc = Jsoup.parse(html, "UTF-8");
			org.jsoup.nodes.Document doc = Jsoup.parse(html,"GBK");

			//提取标题信息
			Elements title = doc.select("title");
			for (org.jsoup.nodes.Element link : title) {
				text += link.text() + " ";
			}
			
			//提取table中的文本信息
			Elements links = doc.select("table");
			for (org.jsoup.nodes.Element link : links) {
				text += link.text() + " ";
			}
			
			//提取div中的文本信息
			Elements divs = doc.select("div[class=post]");
			for (org.jsoup.nodes.Element link : divs) {
				text += link.text() + " ";
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return text;
	}
	
	public static void createIndex(String inputDir) {
		Directory directory = null;
		IndexWriter writer = null;
		IndexSearcher searcher = null;
		// 索引创建开始时间
		Date beginTime = new Date();
		try {
			
			//从配置文件中读取索引存放路径
			String indexPath = IndexUtil.getValue(IndexUtil.INDEX_ROOT);
			directory = FSDirectory.open(new File(indexPath));
			File indexFile = new File(indexPath + "\\segments.gen");
			
			//根据索引文件segments.gen是否存在判断是否是第一次创建索引
			if (indexFile.exists()) {
				
				//增量添加索引信息
				writer = new IndexWriter(directory, new IKAnalyzer(), false,
						IndexWriter.MaxFieldLength.LIMITED);
				writer.setMergeFactor(1000);
				writer.setMaxBufferedDocs(100);
				writer.setMaxMergeDocs(Integer.MAX_VALUE);
			} else {
				
				//新建索引信息
				writer = new IndexWriter(directory, new IKAnalyzer(), true,
						IndexWriter.MaxFieldLength.LIMITED);
				writer.setMergeFactor(1000);
				writer.setMaxBufferedDocs(100);
				writer.setMaxMergeDocs(Integer.MAX_VALUE);
			}

			File fileDir = new File(inputDir);
			File[] files = fileDir.listFiles();
			if (files.length > 1) {

				//当前最新上传的文件夹名字(时间降序第二个,因为最新的可能还未上传完毕)
//				String newLastFolder = files[files.length - 2].getName();
				
				//将上次任务记录的文件夹名称取出
//				String lastFolder = IndexUtil
//						.getValue(IndexUtil.LAST_FOLDER_PATH);

				
				//索引文件中存储的索引字段的定义
				Field fieldName = new Field("id", "", Field.Store.YES,
						Field.Index.UN_TOKENIZED);
				Field fieldPath = new Field("path", "", Field.Store.YES,
						Field.Index.NO);
				Field fieldContent = new Field("content", "", Field.Store.COMPRESS,
						Field.Index.ANALYZED);
				Document doc = null;

				//遍历文件根目录下所有子目录并创建索引
				for (int i = 0; i < files.length - 1; i++) {
					if (files[i].isDirectory()) {
						
						//这里需要根据当前文件夹的命名规律和上次建索引后记录的文件夹名字比较
						//避免出现对文件重复创建索引
						
								File fileDirs = new File(files[i]
										.getAbsolutePath());
								File[] file = fileDirs.listFiles();
								for (int j = 0; j < file.length; j++) {
									
									String fileName = file[j].getName();

									String lastName = "";
									if (fileName.lastIndexOf(".") != -1) {
										lastName = fileName
												.substring(fileName
														.lastIndexOf("."));
									}
									if (lastName.equals(".txt")) {
										doc = new Document();
										fieldName.setValue(fileName.substring(0, fileName.indexOf(".")));
										doc.add(fieldName);
										fieldPath.setValue(file[j]
												.getAbsolutePath());
										doc.add(fieldPath);
										fieldContent
												.setValue(loadFileToString(file[j]));
										doc.add(fieldContent);
										writer.addDocument(doc);
									}
										else if (lastName.equals(".html") ||lastName.equals(".htm")) {
											doc = new Document();
										    String htmlCont = getDocument(file[j]);
											fieldName.setValue(file[j].getName());
											doc.add(fieldName);
											fieldPath.setValue(file[j]
													.getAbsolutePath());
											doc.add(fieldPath);
											fieldContent
													.setValue(htmlCont);
											doc.add(fieldContent);
											writer.addDocument(doc);
											
										}
								}
								Date endTime1 = new Date();
								long timeOfSearch1 = endTime1.getTime()
										- beginTime.getTime();
								System.out.println("--->" + files[i]
										+ "建立索引时间 " + timeOfSearch1 / 1000 / 60
										+ " Minute" + "(" + timeOfSearch1
										+ " ms )");
					}
				}

				//创建索引完成后记录下本次创建索引的最后一个目录名称
//				if (!lastFolder.equals(newLastFolder)) {
//					String path = PropertyUtil.class.getResource(
//							"/index.properties").toURI().getPath();
//					PropertyUtil.updateValue(path, IndexUtil.LAST_FOLDER_PATH,
//							LAST_FILE_PATH);
//					IndexUtil.indexMap.put(IndexUtil.LAST_FOLDER_PATH,
//							LAST_FILE_PATH);
//				}
			}

		} catch (Exception e) {
			//清空writer中的索引信息,否则writer在close时会将信息写入索引文件
			writer = null;
			e.printStackTrace();
		} finally {

			if (searcher != null) {
				try {
					searcher.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			if (writer != null) {
				try {
					//优化索引并合并索引文件
					writer.optimize();
					Date endTime1 = new Date();
					long timeOfSearch1 = endTime1.getTime()
							- beginTime.getTime();
					System.out.println("--->"
							+ "合并索引时间 " + timeOfSearch1 / 1000 / 60
							+ " Minute" + "(" + timeOfSearch1
							+ " ms )");
					writer.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if (directory != null) {
				try {
					directory.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}


	}

/**
	 * 将小索引文件合并到大的索引文件中去
	 * 
	 * @param from
	 *            将要合并到to文件的文件
	 * @param to
	 *            将from文件合并到该文件
	 */
	public static void mergeIndex() {
		IndexWriter writer = null;
		Directory toDirectory = null;
		Directory fromDirectory = null;
		try {
			File from = new File(INDEX_THREAD1);
			File to = new File(IndexUtil.getValue(IndexUtil.INDEX_ROOT));
			toDirectory = FSDirectory.open(to);
			fromDirectory = FSDirectory.open(from);
			writer = new IndexWriter(toDirectory, new IKAnalyzer(), false,
					IndexWriter.MaxFieldLength.LIMITED);
			writer.setMergeFactor(100);
			writer.setMaxBufferedDocs(100);
			writer.setMaxMergeDocs(Integer.MAX_VALUE);
			writer.addIndexes(IndexReader.open(fromDirectory));
			writer.optimize();
			writer.close();
		} catch (Exception e) {
			writer = null;
			e.printStackTrace();
		} finally {
			try {
				if (writer != null)
					writer.close();
			} catch (Exception e) {

			}
			if (toDirectory != null) {
				try {
					toDirectory.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			if (fromDirectory != null) {
				try {
					fromDirectory.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		//索引合并完成时间
		Date endTime = new Date();
		//索引合并所耗时间
		long timeOfSearch = endTime.getTime() - beginTime.getTime();
		System.out.println("The total time For index creat is " + timeOfSearch
				+ " ms");
	}


	//测试代码
	public static void main(String[] args) {
		 IndexJob processor = new IndexJob();
		 processor.createIndex("g:\\file4");
	}
	
}



对索引文件进行查询的代码如下:
public class Search {

	public void indexSearch(String searchType, String searchKey) {
		String INDEX_STORE_PATH = "g:\\index";
		Directory directory = null;
		IndexSearcher searcher = null;
		// 搜索开始时间
		Date beginTime = new Date();
		try {
			directory = FSDirectory.open(new File(INDEX_STORE_PATH));
			searcher = new IndexSearcher(directory,true);
			searcher.setDefaultFieldSortScoring(true, false);
			searcher.setSimilarity(new IKSimilarity());
			Query query = IKQueryParser.parse(searchType, searchKey);
			System.out.println("查询条件为:" + query);
			
			//索引排序条件
			SortField[] sortfield = new SortField[] { SortField.FIELD_SCORE, new SortField(null, SortField.DOC, true) };
			Sort sort = new Sort(sortfield);
			
			TopDocs topDocs = searcher.search(query, null, 10, sort);
			System.out.println("检索到总数:" + topDocs.totalHits);
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			
			//设置高亮显示的颜色等样式
			SimpleHTMLFormatter simpleHtmlFormatter = new SimpleHTMLFormatter("<FONT COLOR='RED'>", "</FONT>");
			Highlighter highlighter = new Highlighter(simpleHtmlFormatter,new QueryScorer(query));
			//设置高亮显示的字符串长度
			highlighter.setTextFragmenter(new SimpleFragmenter(100));
			
			Analyzer analyzer = new IKAnalyzer();
			for (int i = 0; i < scoreDocs.length; i++) {
				Document targetDoc = searcher.doc(scoreDocs[i].doc);
				TokenStream tokenStream = analyzer.tokenStream("",new StringReader(targetDoc.get("content")));  
				//读取索引的高亮信息
				String str = highlighter.getBestFragment(tokenStream, targetDoc.get("content"));  
//				System.out.println(targetDoc.get("id"));  
//				System.out.println(str);  
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (searcher != null) {
				try {
					searcher.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (directory != null) {
				try {
					directory.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			// 搜索完成时间
			Date endTime = new Date();
			// 搜索所耗时间
			long timeOfSearch = endTime.getTime() - beginTime.getTime();
			System.out
					.println("The time For indexsearch is " + timeOfSearch + " ms");
		}
	}

	//测试代码
	public static void main(String[] args) {
		Search search = new Search();
		search.indexSearch("content", "小贝");
	}
}


要注意的地方:
   由于在windows系统中一个文件夹只能存放2W多个文件,在1W多个文件以后写入速度会急剧下降所以建议每个文件夹存放1W个文件。
   本例只对2层目录结构中的第二层文件夹的文件创建索引,如需要读取其他位置的文件可自行修改代码,注意文件目录结构越多创建索引的速度就越慢,所以请谨慎决定。
   由于创建索引时会影响查询,而且创建索引的时间会很长,所以建议将增量的索引创建到其他目录中,当创建完成后和主索引目录中的进行索引合并。IndexJob.Java中的mergeIndex()就是进行索引合并的方法。

一些建议:
   在对本地文件创建索引文件的时候,尽量不要使用多线程。因为本地磁盘的IO效率是一定的,多线程并不能提高效率而且会加重服务器CPU使用率。
   创建索引的类IndexJob.Java可以配置成定时任务,根据相关数据量调整创建索引的周期。至于本地文件存放路径和创建索引的存放路径等等可以放在properties属性文件中设置,这样利于修改,不用去直接该代码。
   还有一点是每次要记录下上次创建索引的文件夹位置,因为Lucene对增量支持不好,未避免重复创建索引出此下策。


我已经所有jar包打成压缩包放在下面了。
  • lib.zip (4.4 MB)
  • 下载次数: 222
2
0
分享到:
评论
5 楼 chris开到荼縻 2013-07-15  
现在已经是4.3版本的了,不知道还能用不,只能敲出来看看了
4 楼 diyunpeng 2012-12-03  
请问增量哪里支持不好了?

增量的控制是我们自己控制的,我们记录了我们上一次控制的点,不就可以实现增量了么。
3 楼 comsci 2011-11-11  
非常感谢楼主的文章
2 楼 85600367 2011-08-02  
johnhsr888 写道
信息好像不完整啊 

     
            //从配置文件中读取索引存放路径  
            String indexPath =  IndexUtil.getValue(IndexUtil.INDEX_ROOT);  

这个东西是怎么配置的啊

这里只需要传入索引文件的路径 至于你在程序中写死 还是从properties文件读取都是可以的
1 楼 johnhsr888 2011-07-27  
信息好像不完整啊 

     
            //从配置文件中读取索引存放路径  
            String indexPath =  IndexUtil.getValue(IndexUtil.INDEX_ROOT);  

这个东西是怎么配置的啊

相关推荐

    lucene 索引 查看 工具

    在 Lucene 的使用过程中,创建索引是关键步骤,而有时我们需要查看这些索引来了解其结构、内容以及优化搜索性能。这就是"Lucene 索引 查看 工具"的用途,它可以帮助我们分析和理解 Lucene 索引的工作原理。 主要...

    Lucene创建与搜索索引

    本文将重点介绍如何使用Lucene创建索引以及如何基于这些索引进行高效的搜索。 #### 二、创建索引 ##### 2.1 准备工作 在开始之前,我们需要做一些准备工作: - **安装Java环境**:Lucene基于Java开发,因此首先...

    lucene全文检索简单索引和搜索实例

    2. 创建索引目录:索引数据会存储在一个Directory对象中,可以是文件系统、内存或数据库。通常我们选择FSDirectory,将索引存储在本地文件系统。 3. 创建文档对象:为每份要索引的数据创建一个Document对象,添加...

    基于Lucene和HDFS的PB级数据索引、搜索、存储系统.zip

    3. **Lucene索引分布**:每个HDFS节点上的数据都会被本地化的Lucene实例进行索引,确保索引与数据的物理位置相对应,减少网络传输开销。 4. **Shard和Replication**:Lucene的索引可能会被分成多个碎片(shards),...

    Lucene 2.0.0下载安装及简单测试

    《Lucene 2.0.0的下载、安装与初步测试》 一、Lucene简介与版本选择 ...随着版本的更新,Lucene已经引入了更多高级特性,包括对多种语言的支持,这使得它成为现代搜索应用开发中不可或缺的工具之一。

    ssh+lucene搜索实例

    在这种情况下,我们可以使用SSH连接到这些服务器,然后利用Lucene在本地构建索引,通过索引实现对远程数据的高效搜索。 以下是关于SSH和Lucene的详细知识点: 1. SSH基本概念: - 安全性:SSH使用加密技术确保...

    在HDFS上使用Lucene的SourceCode

    首先,我们需要在本地文件系统上创建索引。在Eclipse中创建一个新的项目,并为项目添加所有必要的JAR文件。假设我们有一个包含Web服务器日志文件的应用程序,其中的数据格式如下: ``` 2010-04-21 02:24:01 GET /...

    lucene索引

    首先,需要创建一个索引目录,这可以是本地文件系统、网络共享目录,甚至是内存中的索引。 ```java Directory directory = FSDirectory.open(Paths.get("index_directory")); ``` #### 2.2 创建索引写入器...

    weblucene安装文档

    在`src/main/resources`目录下,有多个配置文件,如`search-config.xml`和`index-config.xml`,它们定义了索引的字段、分析器以及搜索行为。根据你的需求,你可能需要调整这些配置。 8. **测试搜索功能** 部署...

    lucene工程,分词、索引

    1. 创建Directory:获取存储索引的Directory对象,可以是本地文件系统、内存或者远程存储。 2. 创建IndexReader:使用Directory实例创建IndexReader,它是读取索引的主要接口。 3. 创建IndexSearcher:基于...

    Lucene索引优化

    若索引必须存储在远程文件系统上,一个有效的策略是在本地文件系统上先建立索引,之后再将其复制到远程位置。这样能避免远程I/O带来的延迟,显著提升索引构建速度。 #### 升级硬件设备 投资更快的硬件,尤其是更快...

    基于Lucene的全文检索系统

    - **创建索引**:首先,我们需要遍历本地文件系统,读取每个文件的内容,并使用分词器将内容拆分成关键词。然后,将这些关键词及其在原文档中的位置信息保存到索引中。 - **搜索**:用户输入查询字符串后,Lucene会...

    Mdrill项目在lucene的改进上的10点心得1

    原本Lucene由于依赖本地硬盘的随机写操作,无法直接在HDFS上创建索引。Mdrill通过分析源码,发现Lucene的随机写主要用在文件头部预留空间和CRC32校验。通过避免预留空间,改为顺序写入另一个文件,成功解决了这一...

    LUCENE资料

    2. 创建索引:使用 `IndexWriter` 将分词后的文档信息写入索引文件。 3. 搜索:使用 `IndexSearcher` 和用户提供的查询字符串,在索引中执行搜索。 4. 处理结果:获取 `Hits` 对象,从中提取包含查询关键词的文档...

    lucene基本包

    Lucene提供了丰富的Java API,开发人员可以通过简单的代码调用来实现索引创建、更新、查询等功能。同时,Lucene还提供了对其他语言的支持,如Python的PyLucene,JavaScript的js-lucene等。 10. **应用场景** ...

    lucene_heritrix 搜索引擎

    首先,Heritrix爬取互联网上的网页,并将其存储为本地文件。然后,这些文件可以被Lucene读取并建立索引。用户通过搜索接口提交查询,Lucene会快速查找匹配的索引条目,返回相关的搜索结果。这种组合提供了从海量网页...

    Lucene分词器资源包

    使用Lucene进行分词时,你需要选择合适的分析器,如对于中文,可以使用`ChineseAnalyzer`,然后创建索引并执行查询。分词是全文搜索的关键步骤,因为搜索引擎是通过分析和索引文档中的单词来进行匹配的。通过合理的...

    基于lucene的桌面搜索引擎

    通过以上步骤,我们可以构建一个功能完备且高效的桌面搜索引擎,帮助用户快速地在海量的本地文件中找到所需信息。利用Lucene的强大功能,不仅可以实现基础的全文搜索,还能进行高级查询,如短语搜索、通配符搜索和...

    lucene地理位置搜索所用jar包

    记住,为了优化性能,应考虑使用内存映射文件(MMapDirectory)或在Android上适合的其他目录类型来存储索引,同时注意处理Android设备的内存和存储限制。 标签中的“lucene”、“地理位置”和“jar包”、“android...

Global site tag (gtag.js) - Google Analytics