美文网首页
【whoosh】【SegmentWriter】

【whoosh】【SegmentWriter】

作者: JinMoon | 来源:发表于2018-07-22 22:11 被阅读17次

【SegmentWriter】

**SegmentWriter.add_document(self, fields)

(每次写入完成会以terminfo结构返回相关信息)

  • 获取文档ID:docnum
  • 遍历入参fields[]中每个<fieldname,value>
    • 获取fieldname的类型定义(FieldType) field
      • 如果field.indexed为True意味着本字段需要索引
        • 调用field.index(value)对value进行分词,返回一组item(token_bytes, freq, weight, vbytes)元组,其中token_bytes为分词后的token字节序列,vbytes可能是该token在value中出现的位置数组(TEXT类型,不同类型vbyte含义不同)。
        • 把分词后的每个item进行权重转换(乘以文档权重)、词频处理等操作,然后附加上文档ID(docnum)、字段名称(fieldname)形成新的待索引项new_item(fieldname, token_bytes, docnum, weight, vbytes),并使用add_post函数添加到self.SortingPool中。
        • 在后续的SegmentWriter._finalize_segment => SegmentWriter._flush_segment()中,通过调用W3FieldWriter.add_postings()来以块(block)为单位将self.SortingPool中的待索引元组(Posting)逐个写入到"segid.pst"文件中。
          • 因为self.SortingPool中存储的待索引项new_item(fieldname, tbytes, docnum, weight, vbytes)是全局有序的(字典序),排序的规则是先按fieldname排序,fieldname相同的按tbytes排序,以此逐字段类推。这意味着相同filedname,相同tbytes会排列在相邻位置,这样通过顺序遍历SortingPool[]内元素,就能够以<filedname, tbytes>为键,将<filedname, tbytes> 相同(且相邻)的待索引项new_item(fieldname, tbytes, docnum, weight, vbytes)中的(docnum, weight, vbytes)中抽离出来,统一存储到同一块磁盘文件区域中 。
          • 这样,fieldname和tbytes二者便能够唯一确定倒排索引的key,而具有相同的<filedname, tbytes>的一组待索引项new_item(fieldname, tbytes, docnum, weight, vbytes)中的value部分(docnum, weight, vbytes ),则成为倒排索引的索引链,即key => values[(docnum, weight, vbytes)],由此可见,通过<filedname, tbytes>便可以得到包含它的所有文档ID、权重以及附属信息vbytes。
          • 具体操作就是:
            • 每遇到新的filedname,就先调用finish_field()再调用start_field(),start_field()主要是获取filedname所对应的fieldid,创建并初始化W3PostingsWriter对象(用于往"seg_id.pst"文件中写入数据),并设置一些其他变量;finish_field()函数逻辑比较简单,就是重置这些变量。
            • 每遇到新的token_bytes,就先调用finish_term()再调用start_term():
              • start_term() : 等价于调用W3PostingsWriter.start_postings(),重置块数目、成员数组和统计相关的成员变量,获取Posting文件"seg_id.pst"的写入起始偏移。
              • add(docnum, weight, value, length):等价于调用W3PostingsWriter.add_posting(),将数据缓存到(_ids[], _weights[], _values[])并定期写入到Posting文件"seg_id.pst"中。
              • finish_term() : 等价于调用W3PostingsWriter.finish_postings(),会将缓存(_ids[], _weights[], _values[])中的数据以块(block)为单位 写入到"seg_id.pst"文件中,并将写入数据长度(length)、文件偏移(startoffset)、块数目(block cnt)以及一些统计信息(最大/小长度,权重) ,以terminfo对象的方式返回。
            • 当处理完<filedname, tbytes>所对应的索引项之后,在finish_term()执行结束后会返回term_info对象,它存储了<filedname, tbytes>的核心元信息,主要包含一些统计信息(df,最大/小字段长度,最大/小文档ID,最大权重),索引项存储在"seg_id.pst"文件中的起始偏移、长度 。
          • 至此,<filedname, tbytes>的所有待索引项被存储到"seg_id.pst"文件中,相关的写入信息被以term_info对象记录并返回,接着需要:
            • 获取索引Key:keybytes = fieldid + textbytes,其中fieldid和filedname是一一对应的,可相互替换的。
            • 获取索引Value: valbytes = terminfo.to_bytes(),调用terminfo.to_bytes()把term_info序列化成二进制字节串,作为索引值。
            • 插入索引:filetables.OrderedHashWriter.add(keybytes, valbytes),这样,便可以根据<filedname, tbytes>定位到terminfo,进而定位并取出"seg_id.pst"文件中存储的和<filedname, tbytes>相关联的倒排文档链表。
              • filetables.OrderedHashWriter继承自filetables.HashWriter,本质上是一个基于磁盘的二阶哈希表,只是它要求必须按递增顺序添加key,为了方便后续的二分查找和区间查询。
              • filetables.HashWriter是基于磁盘的二阶哈希表的底层实现基类,
                • add(key, value):
                  1. 获取当前文件偏移pos
                  2. 写入len(key)
                  3. 写入len(value)
                  4. 写入key
                  5. 写入value
                  6. 计算key的哈希值h=hashfn(key),得到索引项<h, pos>
                  7. buckets[h & 255].append((h, pos)),将索引项添加到内存数组所实现的哈希桶中
                • 在调用add()将所有的<key, value>添加到桶里后,close()函数会调用_write_hashes()函数将索引结构写入到文件中,具体操作有点复杂:
                  1. 因为共有256个bucket,每个桶中存储了若干<hashfn(key), pos>键值对,现在逐个遍历和处理每个bucket。
                  2. 当前桶bucket内含有元素数目为len(bucket),也即存储了len(bucket)个<hashfn(key), pos>键值对数据,现在需要开辟numslots=2*len(bucket)空间的哈希表(内存数组),并重新分配和存储这len(bucket)个数据,空位置用<0,0>元素占位。
                  3. 在完成重新分配和存储之后,把当前桶的新的2*len(bucket)个<hashfn(key), pos>元素以二进制的方式(每个元素字节大小相同)逐个写入到文件中,写入前先获取当前文件写入偏移pos,这个偏移pos连同元素数目numslots组成键值对<pos, numslots>临时存储到directory[]中,待所有桶被处理和写入之后追加写到文件尾部。
                • close()函数调用完_write_hashes()函数之后,会调用_write_directory()函数把刚记录的每个buckets的<pos, numslots>写入到文件尾部。
                • 最后,close()函数还会调用_write_extras()写入额外信息。
                • 最最后,写入这个额外信息区域的长度,over。

相关文章

网友评论

      本文标题:【whoosh】【SegmentWriter】

      本文链接:https://www.haomeiwen.com/subject/syvrmftx.html