• lucene正向索引(续)——域(Field)的元数据信息在.fnm里,在倒排表里,利用跳跃表,有利于大大提高搜索速度。


    4.1.2. 域(Field)的元数据信息(.fnm)

    一个段(Segment)包含多个域,每个域都有一些元数据信息,保存在.fnm文件中,.fnm文件的格式如下:

    fnm

    • FNMVersion
      • 是fnm文件的版本号,对于Lucene 2.9为-2
    • FieldsCount
      • 域的数目
    • 一个数组的域(Fields)
      • FieldName:域名,如"title","modified","content"等。
      • FieldBits:一系列标志位,表明对此域的索引方式
        • 最低位:1表示此域被索引,0则不被索引。所谓被索引,也即放到倒排表中去。
          • 仅仅被索引的域才能够被搜到。
          • Field.Index.NO则表示不被索引。
          • Field.Index.ANALYZED则表示不但被索引,而且被分词,比如索引"hello world"后,无论是搜"hello",还是搜"world"都能够被搜到。
          • Field.Index.NOT_ANALYZED表示虽然被索引,但是不分词,比如索引"hello world"后,仅当搜"hello world"时,能够搜到,搜"hello"和搜"world"都搜不到。
          • 一个域除了能够被索引,还能够被存储,仅仅被存储的域是搜索不到的,但是能通过文档号查到,多用于不想被搜索到,但是在通过其它域能够搜索到的情况下,能够随着文档号返回给用户的域。
          • Field.Store.Yes则表示存储此域,Field.Store.NO则表示不存储此域。
        • 倒数第二位:1表示保存词向量,0为不保存词向量。
          • Field.TermVector.YES表示保存词向量。
          • Field.TermVector.NO表示不保存词向量。
        • 倒数第三位:1表示在词向量中保存位置信息。
          • Field.TermVector.WITH_POSITIONS
        • 倒数第四位:1表示在词向量中保存偏移量信息。
          • Field.TermVector.WITH_OFFSETS
        • 倒数第五位:1表示不保存标准化因子
          • Field.Index.ANALYZED_NO_NORMS
          • Field.Index.NOT_ANALYZED_NO_NORMS
        • 倒数第六位:是否保存payload

    要了解域的元数据信息,还要了解以下几点:

    • 位置(Position)和偏移量(Offset)的区别
      • 位置是基于词Term的,偏移量是基于字母或汉字的。

    clip_image002

    • 索引域(Indexed)和存储域(Stored)的区别
      • 一个域为什么会被存储(store)而不被索引(Index)呢?在一个文档中的所有信息中,有这样一部分信息,可能不想被索引从而可以搜索到,但是当这个文档由于其他的信息被搜索到时,可以同其他信息一同返回。
      • 举个例子,读研究生时,您好不容易写了一篇论文交给您的导师,您的导师却要他所第一作者而您做第二作者,然而您导师不想别人在论文系统中搜索您的名字时找到这篇论文,于是在论文系统中,把第二作者这个Field的Indexed设为false,这样别人搜索您的名字,永远不知道您写过这篇论文,只有在别人搜索您导师的名字从而找到您的文章时,在一个角落表述着第二作者是您。
    • payload的使用
      • 我们知道,索引是以倒排表形式存储的,对于每一个词,都保存了包含这个词的一个链表,当然为了加快查询速度,此链表多用跳跃表进行存储。
      • Payload信息就是存储在倒排表中的,同文档号一起存放,多用于存储与每篇文档相关的一些信息。当然这部分信息也可以存储域里(stored Field),两者从功能上基本是一样的,然而当要存储的信息很多的时候,存放在倒排表里,利用跳跃表,有利于大大提高搜索速度。
      • Payload的存储方式如下图:

    payload

    •  
      • Payload主要有以下几种用法:
        • 存储每个文档都有的信息:比如有的时候,我们想给每个文档赋一个我们自己的文档号,而不是用Lucene自己的文档号。于是我们可以声明一个特殊的域(Field)"_ID"和特殊的词(Term)"_ID",使得每篇文档都包含词"_ID",于是在词"_ID"的倒排表里面对于每篇文档又有一项,每一项都有一个payload,于是我们可以在payload里面保存我们自己的文档号。每当我们得到一个Lucene的文档号的时候,就能从跳跃表中查找到我们自己的文档号。
    //声明一个特殊的域和特殊的词 

    public static final String ID_PAYLOAD_FIELD = "_ID";

    public static final String ID_PAYLOAD_TERM = "_ID";

    public static final Term ID_TERM = new Term(ID_PAYLOAD_TERM, ID_PAYLOAD_FIELD);

    //声明一个特殊的TokenStream,它只生成一个词(Term),就是那个特殊的词,在特殊的域里面。

    static class SinglePayloadTokenStream extends TokenStream { 
        private Token token; 
        private boolean returnToken = false;

        SinglePayloadTokenStream(String idPayloadTerm) { 
            char[] term = idPayloadTerm.toCharArray(); 
            token = new Token(term, 0, term.length, 0, term.length); 
        }

        void setPayloadValue(byte[] value) { 
            token.setPayload(new Payload(value)); 
            returnToken = true; 
        }

        public Token next() throws IOException { 
            if (returnToken) { 
                returnToken = false; 
                return token; 
            } else { 
                return null; 
            } 
        } 
    }

    //对于每一篇文档,都让它包含这个特殊的词,在特殊的域里面

    SinglePayloadTokenStream singlePayloadTokenStream = new SinglePayloadTokenStream(ID_PAYLOAD_TERM); 
    singlePayloadTokenStream.setPayloadValue(long2bytes(id)); 
    doc.add(new Field(ID_PAYLOAD_FIELD, singlePayloadTokenStream));

    //每当得到一个Lucene的文档号时,通过以下的方式得到payload里面的文档号 

    long id = 0; 
    TermPositions tp = reader.termPositions(ID_PAYLOAD_TERM); 
    boolean ret = tp.skipTo(docID); 
    tp.nextPosition(); 
    int payloadlength = tp.getPayloadLength(); 
    byte[] payloadBuffer = new byte[payloadlength]; 
    tp.getPayload(payloadBuffer, 0); 
    id = bytes2long(payloadBuffer); 
    tp.close();

    •  
      •  
        • 影响词的评分
          • 在Similarity抽象类中有函数public float scorePayload(byte [] payload, int offset, int length)  可以根据payload的值影响评分。
    • 读取域元数据信息的代码如下:

    FieldInfos.read(IndexInput, String)

    • int firstInt = input.readVInt();
    • size = input.readVInt();
    • for (int i = 0; i < size; i++)
      • String name = input.readString();
      • byte bits = input.readByte();
      • boolean isIndexed = (bits & IS_INDEXED) != 0;
      • boolean storeTermVector = (bits & STORE_TERMVECTOR) != 0;
      • boolean storePositionsWithTermVector = (bits & STORE_POSITIONS_WITH_TERMVECTOR) != 0;
      • boolean storeOffsetWithTermVector = (bits & STORE_OFFSET_WITH_TERMVECTOR) != 0;
      • boolean omitNorms = (bits & OMIT_NORMS) != 0;
      • boolean storePayloads = (bits & STORE_PAYLOADS) != 0;
      • boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0;
  • 相关阅读:
    写给太阳村张老师及其员工的公开信
    不尽的想法,不够的时间
    XP+新装SQL Server 2005出现无法连接的问题+解决
    【Windows编程】【网络编程】【基于网络端口通信的客户端应用程序】解决方案【示意程序】
    [VS2005SP1]如何创建从母版页继承的Web窗体?(SP1所带来的小小变更)
    小程序大问题,MSDN中一个小小示例所带来的疑问,一个关于DataList的一个简单应用
    [Oracle]ASP.NET+Oracle连接类conn.cs
    SQLServer2005出了点怪事~(应该是编码问题~)
    [ASPNET2.0]Membership类+SQLServer2005,AspNet_regsql.exe的使用
    Originality Life~Some Desktop Design (From Google Ideas)+ Pictures & PNG Files & 3DMAX Files download!
  • 原文地址:https://www.cnblogs.com/bonelee/p/6394248.html
Copyright © 2020-2023  润新知