`
twh1224
  • 浏览: 95570 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Lucene学习(18)

阅读更多
关于SegmentInfos类的具体实现大致已经在文章 Lucene-2.3.1 源代码阅读学习(17) 中有了一个简单的印象,可以在文章 Lucene-2.3.1 源代码阅读学习(17) 中的末尾部分看到一点非常有用的总结。

然而,到底SegmentInfos类能够实现哪些功能,让我们能够亲眼看到它产生了哪些东西呢?我们可以从SegmentInfos类的一些重要的成员方法中开始追踪一些真实存在的东西到底去向如何,比如segmentName,以及version和gen等等,他们都是有值的,那么,这些值应该被怎样地输出呢,又输出到哪里去了呢,下面仔细学习研究。

先做个引子:

在前面的文章 Lucene-2.3.1 源代码阅读学习(4) 中,我们做了一个小例子,对指定目录中的一些txt文本进行索引,然后做了一个简单的检索关键字的测试。

就从这里说起,在文章 Lucene-2.3.1 源代码阅读学习(4) 中,没有学习到内在的机制,而只是为学习分词做了一个引导。现在,也不对如果构建Document和Field进行解释,因为这一块也非常地复杂,够学习一阵子了。但是,我们要看的就是,在这个过程中都有哪些产物,即产生了哪些文件,知道了建立索引过程中产生了哪些文件,有助于我们在 SegmentInfos类中对一些与维护索引文件相关的信息的去向进行追踪。

在文章 Lucene-2.3.1 源代码阅读学习(4) 中,运行测试程序后,在指定的索引文件目录(测试程序中指定为E:\Lucene\myindex)生成了很多文件(因为指定的要建立索引的txt文件具有一定的量,如果只有一两个txt文本文件,而且它们的大小都不是很大,则产生的索引文件数量会很少,可能只有3个或者4个),segments_N文件和segments.gen文件的生成

1、先看segment_N文件:

在将文件写入到磁盘的目录中之前,一般来说首先要创建一个输出流。在SegmentInfos类中,有一个成员方法write(),在该方法中:
IndexOutput output = directory.createOutput(segmentFileName); 


根据指定的索引段文件的名称segmentFileName,创建一个指向索引目录directory的输出流output。

关于这个segmentFileName,是要先从指定的索引目录中读取出来的,在write()方法中第一行代码中就获取了这个索引段的文件名:
String segmentFileName = getNextSegmentFileName(); 


这里的 getNextSegmentFileName()方法是SegmentInfos类的一个成员方法,了解它有助于我们继续追踪:

public String getNextSegmentFileName() { 
    long nextGeneration; 
 
    if (generation == -1) { 
      nextGeneration = 1; 
    } else { 
      nextGeneration = generation+1; 
    } 
    return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",nextGeneration); 
} 


该方法返回的就是我们将要处理的一个索引段文件的名称。最后一句return返回,调用了IndexFileNames类的fileNameFromGeneration()方法,它也很重要,因为要使用文件名称作为参数获取索引目录下的维护索引的文件都要从这里获得。

关注一下IndexFileNames类的实现:

package org.apache.lucene.index; 
 
// 该类主要是对索引文件的命名进行管理 
final class IndexFileNames { 
 
/** 索引段文件名 */ 
static final String SEGMENTS = "segments"; 
 
/** generation reference文件名*/ 
static final String SEGMENTS_GEN = "segments.gen"; 
 
/** Name of the index deletable file (only used in pre-lockless indices) */ 
static final String DELETABLE = "deletable"; 
   
/** norms file的扩展名 */ 
static final String NORMS_EXTENSION = "nrm"; 
 
/** 复合文件扩展名*/ 
static final String COMPOUND_FILE_EXTENSION = "cfs"; 
 
/** 删除文件扩展名 */ 
static final String DELETES_EXTENSION = "del"; 
 
/** plain norms扩展名 */ 
static final String PLAIN_NORMS_EXTENSION = "f"; 
 
/** Extension of separate norms */ 
static final String SEPARATE_NORMS_EXTENSION = "s"; 
 
/** 
   * Lucene的全部索引文件扩展名列表 
   */ 
static final String INDEX_EXTENSIONS[] = new String[] { 
      "cfs", "fnm", "fdx", "fdt", "tii", "tis", "frq", "prx", "del", 
      "tvx", "tvd", "tvf", "gen", "nrm" 
}; 
 
/** 被添加到复合索引文件上的文件扩展名 */ 
static final String[] INDEX_EXTENSIONS_IN_COMPOUND_FILE = new String[] { 
      "fnm", "fdx", "fdt", "tii", "tis", "frq", "prx", 
      "tvx", "tvd", "tvf", "nrm" 
}; 
 
/** old-style索引文件扩展名 */ 
static final String COMPOUND_EXTENSIONS[] = new String[] { 
    "fnm", "frq", "prx", "fdx", "fdt", "tii", "tis" 
}; 
 
/** 词条向量支持的文件扩展名 */ 
static final String VECTOR_EXTENSIONS[] = new String[] { 
    "tvx", "tvd", "tvf" 
}; 
 
/** 
   * 根据基础文件名(不包括后缀,比如segments.gen文件,segments部分为基础文件名)、扩展名和generarion计算指定文件的完整文件名 
   */ 
static final String fileNameFromGeneration(String base, String extension, long gen) { 
    if (gen == SegmentInfo.NO) { 
      return null; 
    } else if (gen == SegmentInfo.WITHOUT_GEN) { 
      return base + extension; 
    } else { 
      return base + "_" + Long.toString(gen, Character.MAX_RADIX) + extension; 
    } 
} 
} 


fileNameFromGeneration实现的功能:根据传进来的base(比如segments)、扩展名、gen来生成一个新的文件名,并返回。

在SegmentInfos类中getNextSegmentFileName() 方法调用了fileNameFromGeneration,如下所示:

return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,"",nextGeneration);

第一个参数值为"segments",第二个参数值为"",第三个是一个gen(它是一个Long型的数字),如果假设这里的 nextGeneration=5,调用fileNameFromGeneration()方法后,返回的是一个索引段文件名:segments_5。

这样,就可以根据生成的segments_N文件名,创建一个输出流,将需要的信息写入到该文件中。

2、再看segments.gen文件:

仔细观察,其实SegmentInfos类的write方法就是对segments_N文件和segments.gen文件进行写入操作的。

在写入segments_N文件以后,紧接着就是处理segments.gen文件:

output = directory.createOutput(IndexFileNames.SEGMENTS_GEN);

因为在一个索引目录下,属于同一个索引段的索引文件就是通过一个segments.gen文件来维护的,segments.gen文件的文件名自然不需要那么麻烦地去获取。直接使用IndexFileNames.SEGMENTS_GEN = segments.gen作为参数构造一个输出流,进行输出,写入到索引目录中即可。

关于segments_N文件和segments.gen文件保存的信息

同样在SegmentInfos类的write方法中能够看到,这两个文件中都加入了哪些信息。

■ 关于segments_N文件

如下所示:

output.writeInt(CURRENT_FORMAT); // write FORMAT 
      output.writeLong(++version); // every write changes the index 
      output.writeInt(counter); // write counter 
      output.writeInt(size()); // write infos 
      for (int i = 0; i < size(); i++) { 
        info(i).write(output); 
      }         


(1) CURRENT_FORMAT

其中,CURRENT_FORMAT是SegmentInfos类的一个成员:

/* This must always point to the most recent file format. */ 
private static final int CURRENT_FORMAT = FORMAT_SINGLE_NORM_FILE; 


上面CURRENT_FORMAT的值就是FORMAT_SINGLE_NORM_FILE的值-3:

/** This format adds a "hasSingleNormFile" flag into each segment info. 
   * See <a href="http://issues.apache.org/jira/browse/LUCENE-756">LUCENE-756</a> for details. 
   */ 
public static final int FORMAT_SINGLE_NORM_FILE = -3; 


(2) version

version是SegmentInfos类的一个成员,版本号通过系统来获取:

/** 
   * counts how often the index has been changed by adding or deleting docs. 
   * starting with the current time in milliseconds forces to create unique version numbers. 
   */ 
private long version = System.currentTimeMillis(); 


(3) counter

用于为当前待写入索引目录的索引段文件命名的,即segments_N中的N将使用counter替换。

counter也是SegmentInfos类的一个成员,初始化是为0:

public int counter = 0; 


在read()方法中,使用从索引目录中已经存在的segment_N中读取的出format的值,然后根据format的值来指派counter的值,如下所示:

 
      int format = input.readInt(); 
      if(format < 0){     // file contains explicit format info 
       // check that it is a format we can understand 
        if (format < CURRENT_FORMAT) 
          throw new CorruptIndexException("Unknown format version: " + format); 
        version = input.readLong(); // read version 
        counter = input.readInt(); // read counter 
      } 
      else{    // file is in old format without explicit format info 
        counter = format; 
      } 


(4) size()

size()就是SegmentInfos的大小,SegmentInfos中含有多个SegmentInfo,注意:SegmentInfos类继承自Vector。

(5) info(i)

info()方法的定义如下所示:
public final SegmentInfo info(int i) { 
    return (SegmentInfo) elementAt(i); 
} 


可见,SegmentInfos是SegmentInfo的一个容器,它只把当前这个索引目录中的SegmentInfo装进去,以便对他们管理维护。

这里,info(i).write(output);又调用了SegmentInfo类的write()方法,来向索引输出流output中加入信息。SegmentInfo类的write()方法如下所示:

/** 
   * Save this segment's info. 
   */ 
void write(IndexOutput output) 
    throws IOException { 
    output.writeString(name); 
    output.writeInt(docCount); 
    output.writeLong(delGen); 
    output.writeByte((byte) (hasSingleNormFile ? 1:0)); 
    if (normGen == null) { 
      output.writeInt(NO); 
    } else { 
      output.writeInt(normGen.length); 
      for(int j = 0; j < normGen.length; j++) { 
        output.writeLong(normGen[j]); 
      } 
    } 
    output.writeByte(isCompoundFile); 
} 


从上可以看到,还写入了SegmentInfo的具体信息:name、docCount、delGen、(byte)(hasSingleNormFile ? 1:0)、NO/normGen.length、normGen[j]、isCompoundFile。

■ 关于segments.gen文件

通过SegmentInfos类的write()方法可以看到:

         output.writeInt(FORMAT_LOCKLESS);
        output.writeLong(generation);
        output.writeLong(generation);

segments.gen文件中只是写入了两个字段的信息:FORMAT_LOCKLESS和generation。

因为segments.gen文件管理的就是segments_N文件中的N的值,与该文件相关就只有一个generation,和一个用于判断是否是无锁提交的信息:

/** This format adds details used for lockless commits. It differs 
   * slightly from the previous format in that file names 
   * are never re-used (write once). Instead, each file is 
   * written to the next generation. For example, 
   * segments_1, segments_2, etc. This allows us to not use 
   * a commit lock. See <a 
   * href="http://lucene.apache.org/java/docs/fileformats.html">file 
   * formats</a> for details. 
   */ 
public static final int FORMAT_LOCKLESS = -2; 


最后,总结一下:

现在知道了segments_N文件和segment.gen文件都记录了什么内容。

其中,segments_N文件与SegmentInfo类的关系十分密切,接下来要学习SegmentInfo类了。
分享到:
评论

相关推荐

    【分享:lucene学习资料】---<下载不扣分,回帖加1分,欢迎下载,童叟无欺>

    1&gt; lucene学习笔记 2&gt; 全文检索的实现机制 【1】lucene学习笔记的目录如下 1. 概述 3 2. lucene 的包结构 3 3. 索引文件格式 3 4. lucene中主要的类 4 4.1. Document文档类 4 4.1.1. 常用方法 4 4.1.2. 示例 4 4.2...

    java的28个学习目标

    了解并掌握常用的外部API和框架,如Log4J(日志)、Quartz(调度)、JGroups(网络组通信)、JCache(分布式缓存)、Lucene(全文检索)等。 ### 19. 跨平台与本地接口 学习跨平台开发技巧,掌握JNI(Java Native ...

    (狂神)ElasticSearch快速入门笔记,ElasticSearch基本操作以及爬虫(Java-ES仿京东实战)

    (狂神)ElasticSearch快速入门笔记,ElasticSearch基本操作以及爬虫(Java-ES仿京东实战),包含了小狂神讲的东西,特别适合新手学习,笔记保存下来可以多看看。好记性不如烂笔头哦~,ElasticSearch,简称es,es是一个...

    Java学习的30个目标

    熟悉日志框架(如Log4J)、任务调度(如Quartz)、分布式缓存(如JCache)、全文搜索(如Lucene)等常用框架和API。 #### 21. 本地接口与连接器架构 学习Java Native Interface(JNI)和Java Connector ...

    学习java的30个目标.txt

    #### 目标18:掌握J2EE应用服务器 - **服务器选择**:WebLogic、JBoss等。 - **管理技能**:安装配置、集群管理、性能调优等。 #### 目标19:了解面向切面编程(AOP) - **技术框架**:AspectJ、AspectWerkz等。 ...

    学习Java语言的30个参考,让你坐拥别人之上的30个擦考

    - **Lucene**:学习全文检索技术的基础知识。 ### 21. 本地接口与连接器 - **JNI、JCA**:掌握Java Native Interface、Java Connector Architecture等技术,实现Java与其他语言或平台的交互。 通过以上知识点的...

    java的30个学习目标

    #### 18. **Web服务** - JAX-RPC、SAAJ、JAXB、JAXM、JAXR等技术用于构建和消费Web服务。 #### 19. **依赖注入(DI/IoC)** - Spring、PicoContainer、Avalon等框架支持自动装配对象依赖,提高代码的解耦和可测试性...

    搜索引擎开发培训课程提纲PPT学习教案.pptx

    11. **中文分词**:在Lucene和Lietu等工具中,中文分词是文本预处理的关键步骤,正向最大匹配是常见的分词算法。 12. **查找词典算法**:Trie树是构建词典和进行快速查找的高效数据结构,包括数字搜索树和Tire树,...

    全文检索参考

    首先,MySQL是广泛应用的关系型数据库管理系统,而“mysql实现全文检索-魏经-2009-8-18+.doc”这份文档可能详细介绍了如何在MySQL中实现全文检索功能。MySQL支持FTS(Full-Text Search)全文索引,允许用户执行复杂...

    收集java学习资料和面试题包括git上好的项目

    ##### 18. Ehcache - **简介**:一个开源的、高性能的缓存系统。 - **链接**:[http://www.ehcache.org/](http://www.ehcache.org/) - **核心特性**: - 内存缓存 - 分布式缓存 ##### 19. ActiveMQ - **简介**:...

    18-文本分词1

    2. **IKAnalyzer**:源自Apache Lucene,是一个开源的Java实现的中文分词器。它支持词典动态更新,具备较好的分词效果,并且可以自定义扩展,如添加停用词库。 3. **结巴分词(Jieba)**:因其名称的幽默性而...

    0-1-knapsack-problem-master (18).zip

    【标题】"0-1-knapsack-problem-master (18).zip" 提到的是一个与优化问题相关的项目,具体来说是0-1背包问题的实现。0-1背包问题是一个经典的组合优化问题,常见于运筹学和计算机科学中,用于模拟资源分配和决策...

    《ElasticSearch入门到实战》电子书,从入门到进阶实战项目的教程文档,框架SpringBoot框架整合ES.zip

    Elasticsearch 是一个基于 Lucene 的开源全文搜索引擎,以其分布式、可扩展性、实时搜索以及强大的数据分析能力而受到广泛欢迎。它不仅支持文本搜索,还可以处理结构化和非结构化数据,适用于日志分析、监控、信息...

    hierarchical-programming-language-mapping:[ICSE'18]通过代码的分布式矢量表示分层学习跨语言映射

    DATA / DATA_RAW.tar.gz:包含以下开源项目的原始数据:antlr,cordova,datastax,factual,fpml,log4j,lucene,spring,uap,zeromq DATA / TRAINING_SENTENCES:包含C#和Java之间对齐语句的已处理数据,它将...

    JEECMS2012系统使用手册2012-07美化版(pdf格式)

    18. **[@CMS_CUT]**:截断字符串。 #### 五、常用的一些标签 此外,手册还提供了常用的标签及其使用方法: 1. **页面处理时间标签**:显示页面渲染所需的时间。 2. **页面模板包含标签**:在页面中包含另一个模板...

    本科毕业设计,基于Java-web开发的内容管理系统(java cms),使用SpringBoot、vue、MyBatis等技术

    Lucene:全文检索组件。 前端技术 TypeScript: JavaScript的一个超集。 Vue3:JavaScript框架。 ElementPlus:Vue 3 UI 框架。 Vite: 下一代前端开发与构建工具。 Tailwind CSS: 功能类优先的 CSS 框架。 VueRouter:...

    JAVA技术实现的搜索引擎(含源码)Java实用源码整理learns

    这涉及到匹配算法,如BM25、TF-IDF等,这些可以在JAVA中通过自定义算法实现,也可以使用开源库如Lucene。 在"jsymxx254.zip"这个压缩文件中,可能包含了实现以上部分的JAVA源代码。对于初学者,可以通过阅读源码...

    Eclipse开发分布式商城系统+完整视频代码及文档

    │ 18-禁用AJP连接器.avi │ 19-tomcat中JVM参数优化.avi │ ├─补充2:Redis3.0新特性、主从复制、集群视频教程 │ │ 打开必读.txt │ │ │ ├─相关资料 │ │ redis-3.0.1.tar.gz │ │ redis-3.0.2.tar.gz ...

    Nutch入门.rar

    1.4 nutch VS lucene.....2 2. nutch的安装与配置.....3 2.1 JDK的安装与配置.3 2.2 nutch的安装与配置........5 2.3 tomcat的安装与配置......5 3. nutch初体验7 3.1 爬行企业内部网....7 3.1.1 配置nutch.....

    nutch 初学文档教材

    1.4 nutch VS lucene.....2 2. nutch的安装与配置.....3 2.1 JDK的安装与配置.3 2.2 nutch的安装与配置........5 2.3 tomcat的安装与配置......5 3. nutch初体验7 3.1 爬行企业内部网....7 3.1.1 配置nutch....7 ...

Global site tag (gtag.js) - Google Analytics