如何实现nutch增量索引,首先应该在Indexer建索引的时候,只对本次抓取的数据进行索引(以前的数据已经被索引过,所以没有必要再次进行索引),建立的索引假设在indexes文件夹下;然后在将本次建立的索引和以前建立的索引进行去重操作,去除对同一个url的重复的Document;最后将本次索引和以前建立的索引进行合并操作。下面将对上面三个步骤进行详细的说明:
(1)Indexer类的index()函数负责创建一个job,进行索引操作。index函数调用IndexerMapReduce类的静态函数initMRJob()初始化该job,initMRJob负责将需要建立索引的数据通过FileInputFormat.addInputPath(job, p1)操作,加载到job的输入路径中,所以可以通过修改iniMRJob()函数来控制建立索引的输入数据,即只将本次爬取的数据作为输入来避免重复建立索引。代码如下:
/** * 修改方法:该方法可以根据爬取的深度depth来确定需要索引的segment文件 * @param crawlDb 链接数据库路径 * @param linkDb 外部链接信息数据库路径 * @param segments 爬取结果数据路径 * @param job 工作配置信息类 * @param depth 本次爬取深度 */ public static void initMRJob(Path crawlDb, Path linkDb, Collection<Path> segments, JobConf job,int depth) { LOG.info("IndexerMapReduce: crawldb: " + crawlDb);//写日志信息 LOG.info("IndexerMapReduce: linkdb: " + linkDb);//写日记信息 // System.err.println("size="+segments.size()); /** * 下面的for循环,主要工作是取出爬取数据路径下所有的文件夹,然后将每个文件路径通过addInputPath添加到job中 * 只有添加的路径中的数据才会被索引,因为从文件segments中取出爬取结果文件夹的顺序是先入先出(即越早爬取的数据 * 会越早被取出),所以我们根据爬取深度将后面最新爬取的depth个结果数据文件夹添加到job中进行索引。 */ int count=segments.size()-depth,count2=1;// for (final Path segment : segments) { System.out.print("IndexerMapReduce: segment="+segment.toString()); if((count2++)>count){//将最新的depth个结果文件夹添加的job中 LOG.info("IndexerMapReduces: adding segment: " + segment); Path p1=new Path(segment, CrawlDatum.FETCH_DIR_NAME); FileInputFormat.addInputPath(job, p1); Path p2= new Path(segment, CrawlDatum.PARSE_DIR_NAME); FileInputFormat.addInputPath(job,p2); Path p3=new Path(segment, ParseData.DIR_NAME); FileInputFormat.addInputPath(job, p3); Path p4=new Path(segment, ParseText.DIR_NAME); FileInputFormat.addInputPath(job, p4); System.out.println("该文件被选中"); }else{//过滤掉老的结果文件夹 LOG.info("IndexerMapReduces: the segment is too old to add: " + segment); System.out.println("该文件被忽略"); } } /** * 下面是初始化job的其他工作,这里不再介绍 */ FileInputFormat.addInputPath(job, new Path(crawlDb, CrawlDb.CURRENT_NAME)); // System.err.println("CrawlDb="+new Path(crawlDb, CrawlDb.CURRENT_NAME)); FileInputFormat.addInputPath(job, new Path(linkDb, LinkDb.CURRENT_NAME)); // System.err.println("LinkDb="+new Path(linkDb, LinkDb.CURRENT_NAME)); job.setInputFormat(SequenceFileInputFormat.class); job.setMapperClass(IndexerMapReduce.class); job.setReducerClass(IndexerMapReduce.class); job.setOutputFormat(IndexerOutputFormat.class); job.setOutputKeyClass(Text.class); job.setMapOutputValueClass(NutchWritable.class); job.setOutputValueClass(NutchWritable.class); }
备注:indexer在创建索引钱会重新创建临时索引存放路径indexes文件夹,所以以前的索引会被删除掉,indexes下的索引是本次刚得到的索引。
(2)新索引建立完成,需要通过DeleteDuplicates类进行去重操作,调用方法为dedup.dedup(new Path[] { indexes}),他会将indexes文件夹下(indexes文件夹下存放多个索引,每个索引一个文件夹)所有的索引添加到job中,然后经过多步操作完成去重。所以首先应该将以前建立的最终索引剪切到该文件夹,这样DeleteDuplicates就会完成去重操作,这一步的工作关键是如何将以前的索引index移动到indexes下,通过分析hadoop的HDFS文件系统得到moveFromLocalFile()函数,可以移动文件系统中的文件夹,所以完成index文件夹移动的代码如下:
if(indexes != null) { if (fs.exists(index)) { /** * 将index剪切到indexes目录下 */ fs.moveFromLocalFile(index, indexes); } }
(3)IndexMerger负责将索引进行合并操作,操作的代码如下:
fstats = fs.listStatus(indexes, HadoopFSUtil.getPassDirectoriesFilter(fs)); merger.merge(HadoopFSUtil.getPaths(fstats), index, tmpDir);
解释:首先他获得indexes目录下的所有索引(以索引文件夹的形式存在),然后由merge()函数对所有的索引进行合并操作。因为以前的索引已经被移动到了indexes文件夹下,所以这样就不需要再改动IndexMerger的代码,就可以实现新老索引的合并操作,最终他会在结果路径下创建新的最终索引index,然后将合并后的结果放在index中。