lucene版本:6.5.1

有限状态传感器,FST(Finite State Transducer)在lucene中扮演着非常重要的一个角色,在4.0后的版本lucene大量使用了这种数据结构,主要是用于在庞大的字典中快速的定位term的位置。

那么为什么使用FST呢?
考虑到这样一个场景,在lucene中倒排索引是核心,而其带来的问题就是term的字典是非常大,如何在保证term查询效率的前提下,能够尽量减少空间的使用,这便是FST所要解决的问题。相对于hashmap和treemap来说,fst在查询效率上稍逊于两者,而空间上的使用则是远优于前两者。其主要有以下几种特性:

  • 确定:同一种状态最多只能有一个转移可以访问到。
  • 无环:不可能重复遍历同一个状态。
  • 接受机:有限状态机只能接受特定的状态转移到终止状态。

FST与TRIE的关系
TRIE可以看做是一个FST,唯一的一个不同是TRIE只共享前缀,而FSA不仅共享前缀还共享后缀。
假设我们有一个这样的Set: mon,tues,thurs。FSA是这样的:

相应的TRIE则是这样的,只共享了前缀。

那么FST在lucene中是如何实现的呢?

在lucene中,FST相关的源码保存于包org.apache.lucene.util.fs中,其中builder.java内定义了相关的类以及建立和查询的过程。FST可以简单的看作是图,其中的节点我们成为状态机的状态而边则是状态机的“转移”。我们看一个简单的例子:

 public void test () {try {String inputValues[] = {"cat", "deep", "do", "dog", "dogs"};long outputValues[] = {5, 7, 17, 18, 21};PositiveIntOutputs outputs = PositiveIntOutputs.getSingleton();Builder<Long> builder = new Builder<Long>(FST.INPUT_TYPE.BYTE1, outputs);IntsRefBuilder scratchInts = new IntsRefBuilder();for (int i = 0; i < inputValues.length; i++) {builder.add(Util.toIntsRef(new BytesRef(inputValues[i]), scratchInts), outputValues[i]);}FST<Long> fst = builder.finish();Long value = Util.get(fst, new BytesRef("cat"));System.out.println(value); // 18} catch (Exception e) {;}}

以上的示例是建立以及查询一个fst的整个过程。首先我们明确几个问题:

  1. term在fst或者在底层索引中的保存形式?
    在lucene中,term的字符串会以BytesRef的形式进行保存,也就是16进制的形式。比如说对于字符串“cat”来说的term。在底层我们发现其实是{63 61 67}。那么为什么要这样处理呢?我的理解是可能是节约空间,我们就以cat为例子:
字符串:[Vint = 3][c][a][t]
字节流:[63][61][67]
会节省一个存储空间

其中源码的主要结构为:

  • Class ByteRef:term的类型,里面包含了String到ByteRef的转换函数等。
  • Class builder: 里面包含了建立fst的主要函数 add()。
  • Class UnCompileNode:新插入的节点。
  • Class Arc :输入的“转移”条件。

我们这里一起学习以下builder.java函数中的add()函数。

 public void add(IntsRef input, T output) throws IOException {//这里的 input为一组16进制的数字,通常我们可以通过new BytesRef(『String』)接口获取。if (output.equals(NO_OUTPUT)) {output = NO_OUTPUT;}assert lastInput.length() == 0 || input.compareTo(lastInput.get()) >= 0: "inputs are added out of order lastInput=" + lastInput.get() + " vs input=" + input;assert validOutput(output);//System.out.println("\nadd: " + input);if (input.length == 0) {// empty input: only allowed as first input.  we have// to special case this because the packed FST// format cannot represent the empty input since// 'finalness' is stored on the incoming arc, not on// the nodefrontier[0].inputCount++;frontier[0].isFinal = true;fst.setEmptyOutput(output);return;}// 同LastInput相比获取最长前缀字符串。int pos1 = 0;int pos2 = input.offset;final int pos1Stop = Math.min(lastInput.length(), input.length);while(true) {frontier[pos1].inputCount++;//System.out.println("  incr " + pos1 + " ct=" + frontier[pos1].inputCount + " n=" + frontier[pos1]);if (pos1 >= pos1Stop || lastInput.intAt(pos1) != input.ints[pos2]) {break;}pos1++;pos2++;}final int prefixLenPlus1 = pos1+1;// 1.新插入的节点放到frontier数组,UnCompileNode表明是新插入的,以后还可能会变化,还未放入FST内。if (frontier.length < input.length+1) {final UnCompiledNode<T>[] next = ArrayUtil.grow(frontier, input.length+1);for(int idx=frontier.length;idx<next.length;idx++) {next[idx] = new UnCompiledNode<>(this, idx);}frontier = next;}// 2.从prefixLenPlus1, 进行freeze冰冻操作, 添加并构建最小FSTfreezeTail(prefixLenPlus1);// init tail states for current input// 3.将当前input剩下的部分插入,构建arc转移(前缀是共用的,不用添加新的状态)。for(int idx=prefixLenPlus1;idx<=input.length;idx++) {frontier[idx-1].addArc(input.ints[input.offset + idx - 1],frontier[idx]);frontier[idx].inputCount++;}final UnCompiledNode<T> lastNode = frontier[input.length];if (lastInput.length() != input.length || prefixLenPlus1 != input.length + 1) {lastNode.isFinal = true;lastNode.output = NO_OUTPUT;}// push conflicting outputs forward, only as far as// needed// 4.如果有冲突的话,重新分配output值for(int idx=1;idx<prefixLenPlus1;idx++) {final UnCompiledNode<T> node = frontier[idx];final UnCompiledNode<T> parentNode = frontier[idx-1];final T lastOutput = parentNode.getLastOutput(input.ints[input.offset + idx - 1]);assert validOutput(lastOutput);final T commonOutputPrefix;final T wordSuffix;if (lastOutput != NO_OUTPUT) {// 使用common方法,计算output的共同前缀commonOutputPrefix = fst.outputs.common(output, lastOutput);assert validOutput(commonOutputPrefix);// 使用subtract方法,计算重新分配的output·wordSuffix = fst.outputs.subtract(lastOutput, commonOutputPrefix);assert validOutput(wordSuffix);parentNode.setLastOutput(input.ints[input.offset + idx - 1], commonOutputPrefix);node.prependOutput(wordSuffix);} else {commonOutputPrefix = wordSuffix = NO_OUTPUT;}output = fst.outputs.subtract(output, commonOutputPrefix);assert validOutput(output);}if (lastInput.length() == input.length && prefixLenPlus1 == 1+input.length) {// same input more than 1 time in a row, mapping to// multiple outputslastNode.output = fst.outputs.merge(lastNode.output, output);} else {// this new arc is private to this new input; set its// arc output to the leftover output:frontier[prefixLenPlus1-1].setLastOutput(input.ints[input.offset + prefixLenPlus1-1], output);}// save last inputlastInput.copyInts(input);//System.out.println("  count[0]=" + frontier[0].inputCount);}

freezeTail(prefixLenPlus1)函数的作用

首先来说,对于新插入的UnCompileNode首先会保存到fronter数组,然后在将“静态”也就是不可改变的部分以CompileNode形式插入到fst中。
那么为什么会有部分是不变的呢?
因为输入是预先排序好的,比如插入完所有以字母“a”开始的term后,该部分就变成了静态的,因为以“b”为开始的term不会对上面的产生改变。

lucene随笔-FST(Finite State Transducer)有限状态传感器相关推荐

  1. FST(Finite State Transducers, 有限状态转换器)简介及示例

    一.简介 Finite State Transducers 简称 FST, 中文名:有穷状态转换器.FST目前在语音识别和自然语言搜索.处理等方向被广泛应用. 例如,在自然语言处理中,经常会遇到一些针 ...

  2. 【Elasticsearch】es FST (Finite State Tranduer) 有限状态转换器

    1.概述 FST 有限状态转换器 有一篇博客我有翻译到这篇博客. 参考:[elasticsearch]You AutoComplete Me FST数构建的树如下 演示网站地址:http://exam ...

  3. 字典数据结构-FST(Finite State Transducers)

    http://www.cnblogs.com/LBSer/p/4119841.html 1 lucene字典 使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找到该te ...

  4. 计算机态,(计算机)有限态自动机,FSM(finite state machine),音标,读音,翻译,英文例句,英语词典...

    补充资料:ω-有限自动机 ω-有限自动机 ω-finite state automata 1094·.一youx一anz}dongJ-..有限自动机(.一rinite state automata)一 ...

  5. Verilog学习笔记HDLBits——Finite State Machines(1)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.Finite State Machines 1.Simple FSM 1(asychronous reset) 2 ...

  6. 有限状态机FSM(Finite State Machine)及实现方式介绍(转)

    原文:https://www.cnblogs.com/barrywxx/p/12860573.html 一.为什么引入有限状态机? 最近做一个项目,项目中很多实体(Entity),每个实体都有很多状态 ...

  7. 证明与计算(7): 有限状态机(Finite State Machine)

    什么是有限状态机(Finite State Machine)? 什么是确定性有限状态机(deterministic finite automaton, DFA )? 什么是非确定性有限状态机(nond ...

  8. 有限状态机FSM(finite state machine) 二

    有限状态机FSM(finite state machine) 二 延续上一篇继续有限状态机 上一篇中的状态切换判断是在每一个状态类的 OnExecute 方法中各种 if else 硬编码逻辑 当状态 ...

  9. FSM(Finite State Machine,有限状态机)设计

    有限状态机(Finite State Machine, FSM),根据状态机的输出是否与输入有关,可分为Moore型状态机和Mealy型状态机.Moore型状态机输出仅仅与现态有关和Mealy型状态机 ...

最新文章

  1. 【 C 】const 进一步解读(由switch引出)
  2. 【转】VS 安全开发生命周期(SDL)检查
  3. iOS 配置pch文件
  4. Cocos2d-x.3.0开发环境搭建
  5. python dataframe函数_python pandas中DataFrame类型数据操作函数的方法
  6. php控制台输入输出
  7. 转载 openlayers 3.0 教程
  8. 微信小程序获取二维码scene参数报错 41030:invalid page rid: xx-xx-xx
  9. 宋宝华:当Linux内核遭遇鲨鱼—kernelshark
  10. 恢复有道词典单词记录本的几种方法(非完美)
  11. windows7修复计算机在哪里找,Windows7系统修复方法大全
  12. excel建立层级_Power Pivot财务科目(层级深度amp;筛选深度)
  13. MySQL中临时表(TEMPORARY)
  14. 温故而知新---jquery(jq)进阶篇
  15. Xcode9 通过无线链接调试,亲测成功
  16. 数字取证二 熟练掌握鉴证大师 了解NTFS分析、LogFile文件使用和USN日志分析
  17. python怎样快速下载库_Python如何急速下载第三方库详解
  18. 绝缘电阻仪测试仪与绝缘耐压测试仪的区别
  19. 开发日志_Jan.9
  20. Centos6.6安装简易桌面环境

热门文章

  1. Linux中打包+压缩命令(超详细)
  2. 【f1c200s/f1c100s】mangopi自制linux开发板驱动适配进度(PCB、代码开源)
  3. 深度学习之 神经网络算法原理
  4. Spring拦截器的实现以及通过注解实现拦截
  5. 【Halcon知识】select_contours_xld算子的环境
  6. python stdout_如何从Python函数调用中捕获stdout输出?
  7. DELL Vostro 1000键盘输入反应慢的问题
  8. 浪潮信息与宁盾共促身份管理系统升级 联合解决方案通过澎湃技术认证
  9. php 截取字符串中的英文,PHP截取中英文字符串
  10. 王者荣耀服务器维护公告5月8号,王者荣耀5月8日更新公告 收集初夏特饮兑换特效[多图]...